Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
parent
2e338c00cb
commit
3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions
61
GeneralsMD/Code/Tools/matchbot/debug.cpp
Normal file
61
GeneralsMD/Code/Tools/matchbot/debug.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: debug.cpp ////////////////////////////////////////////////////////
|
||||
// Minmal debug info
|
||||
// Author: Matthew D. Campbell, Sept 2002
|
||||
|
||||
#include <wdebug.h>
|
||||
#include "debug.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS
|
||||
|
||||
#include <stdarg.h>
|
||||
void DebugCrash( const char *fmt, ... ) {}
|
||||
char* TheCurrentIgnoreCrashPtr;
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
void DebugLog(const char *fmt, ...)
|
||||
{
|
||||
static char buffer[1024];
|
||||
va_list va;
|
||||
va_start( va, fmt );
|
||||
vsnprintf(buffer, 1024, fmt, va );
|
||||
buffer[1023] = 0;
|
||||
va_end( va );
|
||||
|
||||
//printf( buffer );
|
||||
DBGMSG(buffer);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DEBUG
|
||||
|
46
GeneralsMD/Code/Tools/matchbot/debug.h
Normal file
46
GeneralsMD/Code/Tools/matchbot/debug.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// FILE: debug.h //////////////////////////////////////////////////////////////
|
||||
// Minimal debug info
|
||||
// Author: Matthew D. Campbell, Sept 2002
|
||||
|
||||
#ifndef __DEBUG_H__
|
||||
#define __DEBUG_H__
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void DebugLog( const char *fmt, ... );
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define DEBUG_LOG(x) DebugLog x
|
||||
|
||||
#else // DEBUG
|
||||
|
||||
#define DEBUG_LOG(x) {}
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
#endif // __DEBUG_H__
|
||||
|
95
GeneralsMD/Code/Tools/matchbot/encrypt.cpp
Normal file
95
GeneralsMD/Code/Tools/matchbot/encrypt.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "encrypt.h"
|
||||
|
||||
#define MAX_CHARS 65
|
||||
static char Base_String[MAX_CHARS] =
|
||||
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
|
||||
|
||||
static char Return_Buffer[MAX_ENCRYPTED_STRING + 1];
|
||||
static char Temp_Buffer [MAX_ENCRYPTED_STRING + 1];
|
||||
|
||||
/*******************************************************/
|
||||
/* This function is a simple one-way encryption that */
|
||||
/* is portable across many platforms */
|
||||
/* */
|
||||
/* The 'String' to encrypt MUST be up to 8 chars long */
|
||||
/* and should be no shorter than 4 characters. */
|
||||
/* It can contain letters and numbers and '.' and '/' */
|
||||
/*******************************************************/
|
||||
|
||||
/* String is the original string to encrypt */
|
||||
/* Seed is the string to encrypt */
|
||||
//char *encrypt(char *String, char *Seed)
|
||||
char *do_encrypt(char *String)
|
||||
{
|
||||
/* We need a 56 bit key, so use two 32 bit values */
|
||||
/* and we'll strip off the high order 8 bits */
|
||||
//unsigned long Random_Seed_Value_high = 0; /* 32 bit seed value */
|
||||
//unsigned long Random_Seed_Value_low = 0; /* 32 bit seed value */
|
||||
//unsigned long Temp_high = 0; /* 32 bit storage value */
|
||||
//unsigned long Temp_low = 0; /* 32 bit storage value */
|
||||
unsigned int UpCnt = 0, DnCnt = 0, Cnt = 0;
|
||||
unsigned int Length = strlen(String);
|
||||
|
||||
/* Ok, here is the algorithm: */
|
||||
/* */
|
||||
|
||||
if (Length > MAX_ENCRYPTED_STRING)
|
||||
Length = MAX_ENCRYPTED_STRING;
|
||||
|
||||
for (UpCnt = 0, DnCnt = Length; UpCnt < Length; UpCnt++, DnCnt--)
|
||||
if (String[UpCnt] & 0x01)
|
||||
Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) &
|
||||
String[DnCnt];
|
||||
else
|
||||
Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) ^
|
||||
String[DnCnt];
|
||||
|
||||
for (Cnt = 0; Cnt < MAX_ENCRYPTED_STRING; Cnt++)
|
||||
Return_Buffer[Cnt] = Base_String[Temp_Buffer[Cnt] & 0x3F];
|
||||
|
||||
Return_Buffer[Cnt] = NULL;
|
||||
return (Return_Buffer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
void main(void)
|
||||
{
|
||||
char Input_String[9];
|
||||
char *new_string;
|
||||
|
||||
while (1)
|
||||
{
|
||||
printf ("Enter a string to encrypt:");
|
||||
gets(Input_String);
|
||||
printf("\nString enterred was: %s", Input_String);
|
||||
new_string = encrypt(Input_String, "ab");
|
||||
printf("\nEncrypted string is: %s", new_string);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
28
GeneralsMD/Code/Tools/matchbot/encrypt.h
Normal file
28
GeneralsMD/Code/Tools/matchbot/encrypt.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ENCRYPT_HEADER
|
||||
#define ENCRYPT_HEADER
|
||||
|
||||
#define MAX_ENCRYPTED_STRING 8
|
||||
//char *do_encrypt(char *, char *);
|
||||
char *do_encrypt(char *);
|
||||
|
||||
#endif
|
||||
|
||||
|
1322
GeneralsMD/Code/Tools/matchbot/generals.cpp
Normal file
1322
GeneralsMD/Code/Tools/matchbot/generals.cpp
Normal file
File diff suppressed because it is too large
Load diff
193
GeneralsMD/Code/Tools/matchbot/generals.h
Normal file
193
GeneralsMD/Code/Tools/matchbot/generals.h
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GENERALS_H__
|
||||
#define __GENERALS_H__
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
//#include <wstring.h>
|
||||
//#include <dictionary.h>
|
||||
//#include <arraylist.h>
|
||||
#include "matcher.h"
|
||||
#include "global.h"
|
||||
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <hash_map>
|
||||
typedef std::vector<bool> MapBitSet;
|
||||
|
||||
// =====================================================================
|
||||
// Users
|
||||
// =====================================================================
|
||||
|
||||
// Here are the states a matcher can be in:
|
||||
typedef enum
|
||||
{
|
||||
STATUS_INVAL = 0,
|
||||
STATUS_INCHANNEL, // Just entered the channel
|
||||
STATUS_WORKING, // Sent info, needs to be matched
|
||||
STATUS_MATCHED, // Been matched, but is still in the channel
|
||||
} UserStatus;
|
||||
|
||||
class GeneralsUser
|
||||
{
|
||||
public:
|
||||
GeneralsUser(void);
|
||||
UserStatus status;
|
||||
|
||||
int points, minPoints, maxPoints;
|
||||
|
||||
int discons, maxDiscons;
|
||||
|
||||
int country;
|
||||
int color;
|
||||
|
||||
bool widened;
|
||||
time_t timeToWiden;
|
||||
time_t matchStart; // when did we request a match?
|
||||
|
||||
// This is a ping to a designated 3rd-party server who just
|
||||
// responds to pings. The idea is that if the game server is
|
||||
// behind a firewall, the client will have a 1000ms ping to it,
|
||||
// even though he might be very close. To combat this, we have
|
||||
// clients & servers ping some 3rd-party servers & calculate a
|
||||
// pesudo-ping based on the sum of pings server-->3rd-->client.
|
||||
std::vector<int> pseudoPing;
|
||||
int maxPing;
|
||||
|
||||
unsigned int IP;
|
||||
int NAT;
|
||||
|
||||
MapBitSet maps;
|
||||
|
||||
int numPlayers;
|
||||
};
|
||||
|
||||
// =====================================================================
|
||||
// Matcher class
|
||||
// =====================================================================
|
||||
|
||||
typedef std::map<std::string, GeneralsUser*> UserMap;
|
||||
typedef std::map<int, UserMap> LadderMap;
|
||||
|
||||
class GeneralsMatcher : public MatcherClass
|
||||
{
|
||||
public:
|
||||
GeneralsMatcher();
|
||||
virtual ~GeneralsMatcher()
|
||||
{}
|
||||
|
||||
virtual void init(void);
|
||||
virtual void checkMatches(void);
|
||||
|
||||
virtual void handleDisconnect( const char *reason );
|
||||
virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType );
|
||||
virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType );
|
||||
virtual void handlePlayerJoined( const char *nick );
|
||||
virtual void handlePlayerLeft( const char *nick );
|
||||
virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick );
|
||||
virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags );
|
||||
|
||||
private:
|
||||
LadderMap m_ladders;
|
||||
UserMap m_nonLadderUsers1v1;
|
||||
UserMap m_nonLadderUsers2v2;
|
||||
UserMap m_nonLadderUsers3v3;
|
||||
UserMap m_nonLadderUsers4v4;
|
||||
UserMap m_nonMatchingUsers;
|
||||
|
||||
double computeMatchFitness(const std::string& i1, const GeneralsUser *u1, const std::string& i2, const GeneralsUser *u2);
|
||||
|
||||
GeneralsUser* findUser(const std::string& who);
|
||||
GeneralsUser* findUserInLadder(const std::string& who, int ladderID);
|
||||
GeneralsUser* findUserInAnyLadder(const std::string& who);
|
||||
GeneralsUser* findNonLadderUser(const std::string& who);
|
||||
GeneralsUser* findNonMatchingUser(const std::string& who);
|
||||
|
||||
void addUser(const std::string& who);
|
||||
void addUserInLadder(const std::string& who, int ladderID, GeneralsUser *user);
|
||||
void addUserInAnyLadder(const std::string& who, GeneralsUser *user);
|
||||
void addNonLadderUser(const std::string& who, GeneralsUser *user);
|
||||
void addNonMatchingUser(const std::string& who, GeneralsUser *user);
|
||||
|
||||
bool removeUser(const std::string& who);
|
||||
GeneralsUser* removeUserInLadder(const std::string& who, int ladderID);
|
||||
GeneralsUser* removeUserInAnyLadder(const std::string& who);
|
||||
GeneralsUser* removeNonLadderUser(const std::string& who);
|
||||
GeneralsUser* removeNonMatchingUser(const std::string& who);
|
||||
|
||||
void checkMatchesInUserMap(UserMap& userMap, int ladderID, int numPlayers, bool showPoolSize);
|
||||
|
||||
void dumpUsers(void);
|
||||
|
||||
void sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4,
|
||||
std::string name5, std::string name6, std::string name7, std::string name8,
|
||||
GeneralsUser *user1, GeneralsUser *user2, GeneralsUser *user3, GeneralsUser *user4,
|
||||
GeneralsUser *user5, GeneralsUser *user6, GeneralsUser *user7, GeneralsUser *user8,
|
||||
int numPlayers, int ladderID);
|
||||
|
||||
// Command handlers for above privmsg commands (offset is the
|
||||
// offset for the getToken() past the command token)
|
||||
bool handleUserInfo(const char *nick, const std::string& msg);
|
||||
bool handleUserWiden(const char *nick);
|
||||
|
||||
// Weights for various matching parameters
|
||||
int weightLowPing;
|
||||
int weightAvgPoints;
|
||||
int totalWeight;
|
||||
|
||||
time_t m_nextPoolSizeAnnouncement;
|
||||
int m_secondsBetweenPoolSizeAnnouncements;
|
||||
|
||||
//typedef std::vector<std::string> StringVec;
|
||||
//StringVec mapFileList;
|
||||
}
|
||||
;
|
||||
|
||||
// =====================================================================
|
||||
// TEST Client Matcher class
|
||||
// =====================================================================
|
||||
|
||||
class GeneralsClientMatcher : public MatcherClass
|
||||
{
|
||||
public:
|
||||
GeneralsClientMatcher();
|
||||
virtual ~GeneralsClientMatcher()
|
||||
{}
|
||||
|
||||
virtual void init(void);
|
||||
virtual void checkMatches(void);
|
||||
|
||||
virtual void handleDisconnect( const char *reason );
|
||||
virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType );
|
||||
virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType );
|
||||
virtual void handlePlayerJoined( const char *nick );
|
||||
virtual void handlePlayerLeft( const char *nick );
|
||||
virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick );
|
||||
virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags );
|
||||
|
||||
private:
|
||||
}
|
||||
;
|
||||
|
||||
#endif /* __GENERALS_H__ */
|
||||
|
51
GeneralsMD/Code/Tools/matchbot/global.cpp
Normal file
51
GeneralsMD/Code/Tools/matchbot/global.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib> // for FILE ops
|
||||
#include "global.h"
|
||||
|
||||
GlobalClass Global;
|
||||
|
||||
GlobalClass::GlobalClass(void)
|
||||
{}
|
||||
|
||||
bool GlobalClass::ReadFile(const char *fname)
|
||||
{
|
||||
FILE *fp;
|
||||
if ((fp = fopen(fname, "r")) == NULL)
|
||||
return false;
|
||||
config.readFile(fp);
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GlobalClass::GetString(const Wstring& key, Wstring& val)
|
||||
{
|
||||
val = "";
|
||||
config.getString(key, val, "STRINGS");
|
||||
if (val == "")
|
||||
{
|
||||
val.setFormatted("MISSING:%s", key.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
57
GeneralsMD/Code/Tools/matchbot/global.h
Normal file
57
GeneralsMD/Code/Tools/matchbot/global.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GLOBAL_H__
|
||||
#define __GLOBAL_H__
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#ifdef IN
|
||||
#undef IN
|
||||
#endif
|
||||
#define IN const
|
||||
#endif
|
||||
#include <wstypes.h>
|
||||
#include <configfile.h>
|
||||
#include <critsec.h>
|
||||
#include <threadfac.h>
|
||||
#include <tcp.h>
|
||||
#include "matcher.h"
|
||||
#include "rand.h"
|
||||
|
||||
class GlobalClass
|
||||
{
|
||||
public:
|
||||
GlobalClass();
|
||||
|
||||
ConfigFile config;
|
||||
bool ReadFile(const char *fname);
|
||||
|
||||
bool GetString(const Wstring& key, Wstring& val);
|
||||
|
||||
RandClass rnd;
|
||||
};
|
||||
|
||||
extern GlobalClass Global;
|
||||
|
||||
// Log rotation functions
|
||||
void rotateOutput(void);
|
||||
void rotateParanoid(void);
|
||||
|
||||
#endif
|
||||
|
409
GeneralsMD/Code/Tools/matchbot/main.cpp
Normal file
409
GeneralsMD/Code/Tools/matchbot/main.cpp
Normal file
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
#include <cstdlib>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
//#define THREADSAFE_HEADER
|
||||
|
||||
#include <wstring.h>
|
||||
#include <wdebug.h>
|
||||
#include <filed.h>
|
||||
#include <stdoutd.h>
|
||||
#include <wstypes.h>
|
||||
#include <xtime.h>
|
||||
#include "global.h"
|
||||
#include "generals.h"
|
||||
#include "timezone.h"
|
||||
#include <threadfac.h>
|
||||
|
||||
#include <tcp.h>
|
||||
#include "mydebug.h"
|
||||
|
||||
#ifdef _UNIX
|
||||
using namespace std;
|
||||
#else
|
||||
#define sleep(x) Sleep(1000 * (x))
|
||||
#endif
|
||||
|
||||
static char *Program_Usage = "A config filename can be given on the command line (default=matchbot.cfg)\n";
|
||||
void logMonitor(void *);
|
||||
void paranoidLogMonitor(void *);
|
||||
|
||||
OutputDevice * output_device = NULL;
|
||||
OutputDevice * paranoid_output_device = NULL;
|
||||
|
||||
|
||||
void Signal_Quit(int)
|
||||
{
|
||||
INFMSG("Exiting due to signal.");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
void Setup_Signals(void)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
struct sigaction act, oact;
|
||||
act.sa_handler = Signal_Quit;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGTERM, &act, &oact);
|
||||
sigaction(SIGINT, &act, &oact);
|
||||
#endif
|
||||
}
|
||||
|
||||
int VerifyFileDescriptors(int requested)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
struct rlimit limit;
|
||||
if (!getrlimit(_SC_OPEN_MAX, &limit))
|
||||
{
|
||||
INFMSG("Hard limit on file descriptors: " << limit.rlim_max);
|
||||
if (limit.rlim_max < (unsigned int)requested)
|
||||
{
|
||||
ERRMSG("Too many file descriptors requested");
|
||||
ERRMSG("Hard Limit: " << limit.rlim_max << ", requested: " << requested);
|
||||
requested = limit.rlim_max;
|
||||
}
|
||||
|
||||
limit.rlim_cur = limit.rlim_max; /* make soft limit the max */
|
||||
if (setrlimit(_SC_OPEN_MAX, &limit) == -1)
|
||||
{
|
||||
ERRMSG("Error setting max file descriptors to " << limit.rlim_cur);
|
||||
exit(-1);
|
||||
}
|
||||
INFMSG("Soft limit on file descriptors: " << limit.rlim_cur);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERRMSG("Couldn't get limit for _SC_OPEN_MAX");
|
||||
exit(-1);
|
||||
}
|
||||
#endif
|
||||
return requested;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GeneralsMatcher *s_generalsMatcher = NULL;
|
||||
GeneralsClientMatcher *s_generalsClientMatcher = NULL;
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
Wstring config_fname = "matchbot.cfg";
|
||||
|
||||
// You can specify the config file on the command line
|
||||
if (argc == 2)
|
||||
config_fname = argv[1];
|
||||
|
||||
// Read the config file
|
||||
FILE *fp;
|
||||
if ((fp = fopen(config_fname.get(), "r")) == NULL)
|
||||
{
|
||||
cerr << "\nCan't open the config file '" << config_fname.get() << "'\n\n";
|
||||
cerr << Program_Usage << endl;
|
||||
exit( -1);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
Global.ReadFile(config_fname.get());
|
||||
|
||||
// Setup debugging & logging output
|
||||
Wstring output_file;
|
||||
output_file.set("matchbot.log");
|
||||
Global.config.getString("OUTPUTFILE", output_file);
|
||||
if (output_file != "STDOUT")
|
||||
{
|
||||
int append = 1;
|
||||
Global.config.getInt("APPENDTOLOG", append);
|
||||
if (append)
|
||||
output_device = new FileD(output_file.get(), "a");
|
||||
else
|
||||
output_device = new FileD(output_file.get(), "w");
|
||||
if (!output_device)
|
||||
{
|
||||
cerr << "Could not open " << output_file.get() << " for writing!" << endl;
|
||||
exit( -1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output_device = new StdoutD;
|
||||
}
|
||||
MsgManager::setAllStreams(output_device);
|
||||
DBGMSG("Matching bot started!");
|
||||
INFMSG("Matching bot " << argv[0] << " started!");
|
||||
|
||||
// Setup logging of suspicious activity
|
||||
Wstring paranoid_output_file;
|
||||
paranoid_output_file.set("hacks.log");
|
||||
Global.config.getString("PARANOIDFILE", paranoid_output_file);
|
||||
if (paranoid_output_file != "STDOUT")
|
||||
{
|
||||
paranoid_output_device = new FileD(paranoid_output_file.get(), "a");
|
||||
if (!paranoid_output_device)
|
||||
{
|
||||
cerr << "Could not open " << paranoid_output_file.get() << " for writing!" << endl;
|
||||
exit( -1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
paranoid_output_device = new StdoutD;
|
||||
}
|
||||
MyMsgManager::setParanoidStream(paranoid_output_device);
|
||||
DBGMSG("Hack log started!");
|
||||
PARANOIDMSG("Hack log started!");
|
||||
|
||||
Setup_Signals();
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// ----- Initialize Winsock -----
|
||||
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
|
||||
|
||||
// Check game type & start matcher
|
||||
Wstring gametype = "unknown";
|
||||
Global.config.getString("GAME", gametype);
|
||||
|
||||
// Command-line override for gamemode.
|
||||
// This is for test suites, so they can use
|
||||
// the same config file as the corresponding
|
||||
// matchbot.
|
||||
const char *s = argv[0] + strlen(argv[0]);
|
||||
while (s > argv[0] && *s != '/')
|
||||
--s;
|
||||
if (*s == '/')
|
||||
++s;
|
||||
Wstring exe = s;
|
||||
exe.toLower();
|
||||
DBGMSG("Executable file is [" << exe.get() << "]");
|
||||
|
||||
if (gametype == "Generals")
|
||||
{
|
||||
DBGMSG("Generals matching behavior");
|
||||
s_generalsMatcher = new GeneralsMatcher;
|
||||
s_generalsMatcher->connectAndLoop();
|
||||
}
|
||||
else if (gametype == "GeneralsClient")
|
||||
{
|
||||
DBGMSG("Generals TEST client matching behavior");
|
||||
s_generalsClientMatcher = new GeneralsClientMatcher;
|
||||
s_generalsClientMatcher->connectAndLoop();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "\nNo valid GAME entry found!" << endl;
|
||||
exit( -1);
|
||||
}
|
||||
|
||||
if (s_generalsMatcher)
|
||||
delete s_generalsMatcher;
|
||||
if (s_generalsClientMatcher)
|
||||
delete s_generalsClientMatcher;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------+
|
||||
| THREAD: logMonitor |
|
||||
| This thread is spawned once per execution. It will activate after |
|
||||
| midnight and create a new log file. The old one gets put into the |
|
||||
| logfiles directory. |
|
||||
`----------------------------------------------------------------------*/
|
||||
void logMonitor(void *)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
Xtime xtime;
|
||||
time_t curtime;
|
||||
//char timebuf[40];
|
||||
char filenamebuf[128];
|
||||
int delay = -1;
|
||||
Global.config.getInt("ROTATEDELAY", delay);
|
||||
DBGMSG("ROTATEDELAY: " << delay);
|
||||
if (delay == -1)
|
||||
return ;
|
||||
while (1)
|
||||
{
|
||||
curtime = time(NULL);
|
||||
// get the number of seconds that have passed since midnight
|
||||
// of the current day.
|
||||
curtime -= TimezoneOffset();
|
||||
time_t timeofday = curtime % (delay);
|
||||
if ((timeofday > 0) && (timeofday <= 300))
|
||||
{
|
||||
// We're within 5 minutes of midnight, switch the files.
|
||||
|
||||
DBGMSG("about to switch.");
|
||||
Wstring logfilename = "matchbot.log";
|
||||
Global.config.getString("OUTPUTFILE", logfilename);
|
||||
Wstring newfilename = "tmp.log";
|
||||
Global.config.getString("ROTATEFILE", newfilename);
|
||||
MsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), newfilename.get());
|
||||
Wstring logpath = "logs";
|
||||
Global.config.getString("LOGPATH", logpath);
|
||||
xtime.update();
|
||||
sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), xtime.getMonth(),
|
||||
xtime.getMDay(), xtime.getYear(), xtime.getHour(), xtime.getMinute(), xtime.getSecond());
|
||||
rename(newfilename.get(), filenamebuf);
|
||||
DBGMSG("Normal: Just been switched. " << logfilename.get() << ", " << newfilename.get());
|
||||
sleep(60*60*23); // sleep the next 23 hours
|
||||
}
|
||||
sleep(300);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void rotateOutput(void)
|
||||
{
|
||||
Xtime xtime;
|
||||
char filenamebuf[128];
|
||||
Wstring logfilename = "matchbot.log";
|
||||
Wstring newfilename = "tmp.log";
|
||||
Wstring logpath = "logs";
|
||||
|
||||
DBGMSG("About to switch.");
|
||||
|
||||
Global.config.getString("OUTPUTFILE", logfilename);
|
||||
Global.config.getString("ROTATEFILE", newfilename);
|
||||
Global.config.getString("LOGPATH", logpath);
|
||||
|
||||
// This grabs the semaphore, renames the file, and switches the output device
|
||||
MsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(),
|
||||
newfilename.get());
|
||||
|
||||
// clean up the tmp filename and move it to the log dir.
|
||||
sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(),
|
||||
xtime.getMonth(), xtime.getMDay(), xtime.getYear(), xtime.getHour(),
|
||||
xtime.getMinute(), xtime.getSecond());
|
||||
#ifdef _WINDOWS
|
||||
mkdir(logpath.get());
|
||||
#else
|
||||
mkdir(logpath.get(), 00666);
|
||||
#endif
|
||||
rename(newfilename.get(), filenamebuf);
|
||||
|
||||
DBGMSG("Normal: Just been switched. " << logfilename.get() << ", " <<
|
||||
newfilename.get());
|
||||
}
|
||||
|
||||
void paranoidLogMonitor(void *)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
Xtime xtime;
|
||||
time_t curtime;
|
||||
//char timebuf[40];
|
||||
char filenamebuf[128];
|
||||
int delay = -1;
|
||||
Global.config.getInt("ROTATEDELAY", delay);
|
||||
PARANOIDMSG("ROTATEDELAY: " << delay);
|
||||
if (delay == -1)
|
||||
return ;
|
||||
while (1)
|
||||
{
|
||||
curtime = time(NULL);
|
||||
// get the number of seconds that have passed since midnight
|
||||
// of the current day.
|
||||
curtime -= TimezoneOffset();
|
||||
time_t timeofday = curtime % (delay);
|
||||
if ((timeofday > 0) && (timeofday <= 300))
|
||||
{
|
||||
// We're within 5 minutes of midnight, switch the files.
|
||||
|
||||
PARANOIDMSG("about to switch.");
|
||||
Wstring logfilename = "matchbot.log";
|
||||
Global.config.getString("PARANOIDFILE", logfilename);
|
||||
Wstring newfilename = "tmp.log";
|
||||
Global.config.getString("ROTATEPARANOIDFILE", newfilename);
|
||||
MyMsgManager::ReplaceAllStreams((FileD*)paranoid_output_device, logfilename.get(), newfilename.get());
|
||||
Wstring logpath = "logs";
|
||||
Global.config.getString("PARANOIDLOGPATH", logpath);
|
||||
xtime.update();
|
||||
sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), xtime.getMonth(),
|
||||
xtime.getMDay(), xtime.getYear(), xtime.getHour(), xtime.getMinute(), xtime.getSecond());
|
||||
rename(newfilename.get(), filenamebuf);
|
||||
PARANOIDMSG("Paranoid: Just been switched. " << logfilename.get() << ", " << newfilename.get());
|
||||
sleep(60*60*23); // sleep the next 23 hours
|
||||
}
|
||||
sleep(300);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void rotateParanoid(void)
|
||||
{
|
||||
Xtime xtime;
|
||||
char filenamebuf[128];
|
||||
Wstring logfilename = "matchbot.log";
|
||||
Wstring newfilename = "tmp.log";
|
||||
Wstring logpath = "logs";
|
||||
|
||||
PARANOIDMSG("About to switch.");
|
||||
|
||||
Global.config.getString("PARANOIDFILE", logfilename);
|
||||
Global.config.getString("ROTATEPARANOIDFILE", newfilename);
|
||||
Global.config.getString("PARANOIDLOGPATH", logpath);
|
||||
|
||||
// This grabs the semaphore, renames the file, and switches the output device
|
||||
MyMsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(),
|
||||
newfilename.get());
|
||||
|
||||
// clean up the tmp filename and move it to the log dir.
|
||||
sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(),
|
||||
xtime.getMonth(), xtime.getMDay(), xtime.getYear(), xtime.getHour(),
|
||||
xtime.getMinute(), xtime.getSecond());
|
||||
#ifdef _WINDOWS
|
||||
mkdir(logpath.get());
|
||||
#else
|
||||
mkdir(logpath.get(), 00666);
|
||||
#endif
|
||||
rename(newfilename.get(), filenamebuf);
|
||||
|
||||
PARANOIDMSG("Paranoid: Just been switched. " << logfilename.get() << ", " <<
|
||||
newfilename.get());
|
||||
}
|
||||
|
||||
|
421
GeneralsMD/Code/Tools/matchbot/matcher.cpp
Normal file
421
GeneralsMD/Code/Tools/matchbot/matcher.cpp
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "matcher.h"
|
||||
#include "encrypt.h"
|
||||
#include "timezone.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define usleep(x) Sleep((x)/100000)
|
||||
#endif
|
||||
|
||||
MatcherClass::MatcherClass(void)
|
||||
{
|
||||
m_lastRotation = 0;
|
||||
m_baseNick = "matcher";
|
||||
m_joinSuccess = false;
|
||||
done = 0;
|
||||
|
||||
m_rotateLogs = false;
|
||||
quiet = false;
|
||||
}
|
||||
|
||||
Wstring MatcherClass::getString(const Wstring& key)
|
||||
{
|
||||
Wstring res;
|
||||
Global.GetString(key, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void logIt(const char *Txt)
|
||||
{
|
||||
// intentionally crash if we can't open it
|
||||
FILE *fp = fopen("logIt.txt", "w");
|
||||
fputs(Txt, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void MatcherClass::readLoop(void)
|
||||
{
|
||||
int delay = -1;
|
||||
Global.config.getInt("ROTATEDELAY", delay);
|
||||
DBGMSG("ROTATEDELAY: " << delay);
|
||||
|
||||
do
|
||||
{
|
||||
static time_t lastLogTime = 0;
|
||||
time_t now = time(NULL);
|
||||
if (now > lastLogTime + 300)
|
||||
{
|
||||
lastLogTime = now;
|
||||
INFMSG("still here" << endl);
|
||||
}
|
||||
|
||||
logIt("peerThink\n");
|
||||
peerThink(m_peer);
|
||||
|
||||
logIt("peerIsConnected\n");
|
||||
if (peerIsConnected(m_peer))
|
||||
{
|
||||
logIt("checkMatches()\n");
|
||||
checkMatches();
|
||||
logIt("checkMatches() done\n");
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
|
||||
msleep(1);
|
||||
|
||||
// rotate logs if it's time
|
||||
if (delay != -1)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
Xtime xtime;
|
||||
time_t curtime;
|
||||
curtime = time(NULL);
|
||||
// get the number of seconds that have passed since midnight
|
||||
// of the current day.
|
||||
curtime -= TimezoneOffset();
|
||||
time_t timeofday = curtime % (delay);
|
||||
if ((timeofday > 0) && (timeofday <= 300))
|
||||
{
|
||||
rotateOutput();
|
||||
rotateParanoid();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
while (!done);
|
||||
DBGMSG("Bailing out of readLoop!" << endl);
|
||||
INFMSG("Bailing out of readLoop!" << endl);
|
||||
ERRMSG("Bailing out of readLoop!" << endl);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void DisconnectedCallback ( PEER peer, const char * reason, void * param)
|
||||
{
|
||||
DBGMSG("Disconnected: " << reason);
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handleDisconnect( reason );
|
||||
}
|
||||
|
||||
static void RoomMessageCallback ( PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param)
|
||||
{
|
||||
DBGMSG("(PUBLIC) " << nick << ": " << message);
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handleRoomMessage( nick, message, messageType );
|
||||
}
|
||||
|
||||
static void PlayerMessageCallback ( PEER peer, const char * nick, const char * message, MessageType messageType, void * param)
|
||||
{
|
||||
DBGMSG("(PRIVATE) " << nick << ": " << message);
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handlePlayerMessage( nick, message, messageType );
|
||||
}
|
||||
|
||||
static void PlayerJoinedCallback ( PEER peer, RoomType roomType, const char * nick, void * param)
|
||||
{
|
||||
DBGMSG(nick << " joined the room");
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handlePlayerJoined( nick );
|
||||
}
|
||||
|
||||
static void PlayerLeftCallback ( PEER peer, RoomType roomType, const char * nick, const char * reason, void * param)
|
||||
{
|
||||
DBGMSG(nick << " left the room");
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handlePlayerLeft( nick );
|
||||
}
|
||||
|
||||
static void PlayerChangedNickCallback ( PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param)
|
||||
{
|
||||
INFMSG(oldNick << " changed nicks to " << newNick);
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handlePlayerChangedNick( oldNick, newNick );
|
||||
}
|
||||
|
||||
static void EnumPlayersCallback ( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param)
|
||||
{
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handlePlayerEnum( success == PEERTrue, index, nick, flags);
|
||||
}
|
||||
|
||||
static int s_groupID = 0;
|
||||
static void ListGroupRoomsCallback ( PEER peer, PEERBool success, int groupID, SBServer server, const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying, void * param)
|
||||
{
|
||||
if (success && name && !strcasecmp(name, "QuickMatch"))
|
||||
{
|
||||
s_groupID = groupID;
|
||||
}
|
||||
}
|
||||
|
||||
static void ConnectCallback ( PEER peer, PEERBool success, void * param)
|
||||
{
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handleConnect( success == PEERTrue );
|
||||
}
|
||||
|
||||
static void JoinCallback ( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void * param)
|
||||
{
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handleJoin( success == PEERTrue );
|
||||
}
|
||||
|
||||
static void NickErrorCallback ( PEER peer, int type, const char * badNick, void * param)
|
||||
{
|
||||
ERRMSG("Nick error with " << badNick);
|
||||
|
||||
if(type == PEER_IN_USE)
|
||||
{
|
||||
int len = strlen(badNick);
|
||||
std::string nickStr = badNick;
|
||||
int newVal = 0;
|
||||
if (badNick[len-1] == '}' && badNick[len-3] == '{' && isdigit(badNick[len-2]))
|
||||
{
|
||||
newVal = badNick[len-2] - '0' + 1;
|
||||
nickStr.erase(len-3, 3);
|
||||
}
|
||||
|
||||
nickStr.append("{");
|
||||
char tmp[2];
|
||||
tmp[0] = '0'+newVal;
|
||||
tmp[1] = '\0';
|
||||
nickStr.append(tmp);
|
||||
nickStr.append("}");
|
||||
DBGMSG("Nickname taken: was "<<badNick<<", new val = "<<newVal<<", new nick = "<<nickStr.c_str());
|
||||
|
||||
if (newVal < 10)
|
||||
{
|
||||
// Retry the connect with a similar nick.
|
||||
peerRetryWithNick(peer, nickStr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cancel the connect.
|
||||
peerRetryWithNick(peer, NULL);
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handleNickError( badNick );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cancel the connect.
|
||||
peerRetryWithNick(peer, NULL);
|
||||
MatcherClass *matcher = (MatcherClass *)param;
|
||||
if (matcher)
|
||||
matcher->handleNickError( badNick );
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void callbackEach( CHAT chat, CHATBool success, int index, const char *channel,
|
||||
const char *topic, int numUsers, void *param )
|
||||
{
|
||||
DEBUG_LOG(("Chat channel success: %d\n", success));
|
||||
if (!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(("Channel[%d]: %s (%s), %d users\n",
|
||||
index, channel, topic, numUsers));
|
||||
}
|
||||
|
||||
void callbackAll( CHAT chat, CHATBool success, int numChannels, const char **channels,
|
||||
const char **topics, int *numUsers, void *param )
|
||||
{
|
||||
DEBUG_LOG(("Chat channels success: %d\n", success));
|
||||
if (!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("%d channels found\n", numChannels));
|
||||
|
||||
for (int i=0; i<numChannels; ++i)
|
||||
{
|
||||
DEBUG_LOG(("Channel[%d]: %s (%s), %d users\n",
|
||||
i, channels[i], topics[i], numUsers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void MatcherClass::handleConnect( bool success )
|
||||
{
|
||||
m_connectSuccess = success;
|
||||
|
||||
//DEBUG_LOG(("Enumerating chat channels\n"));
|
||||
//chatEnumChannels( peerGetChat(m_peer), "", callbackEach, callbackAll, NULL, CHATTrue );
|
||||
//DEBUG_LOG(("Done enumerating chat channels\n"));
|
||||
}
|
||||
|
||||
void MatcherClass::handleGroupRoomList( bool success, int groupID, const char *name )
|
||||
{
|
||||
}
|
||||
|
||||
void MatcherClass::handleJoin( bool success )
|
||||
{
|
||||
m_joinSuccess = success;
|
||||
if (m_joinSuccess)
|
||||
{
|
||||
DBGMSG("Joined room - listing players");
|
||||
peerEnumPlayers(m_peer, GroupRoom, EnumPlayersCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
void MatcherClass::handleNickError( const char *badNick )
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void AuthenticateCDKeyCallback
|
||||
(
|
||||
PEER peer,
|
||||
int result,
|
||||
const char * message,
|
||||
void * param
|
||||
)
|
||||
{
|
||||
bool *val = (bool *)param;
|
||||
if (val)
|
||||
{
|
||||
*val = (result >= 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MatcherClass::connectAndLoop(void)
|
||||
{
|
||||
// Game-specific initializations, if neccessary
|
||||
init();
|
||||
|
||||
// Check for possible quit from init()-based self-tests
|
||||
if (done)
|
||||
return ;
|
||||
|
||||
// Defaults.
|
||||
////////////
|
||||
Wstring title = "gmtest";
|
||||
Wstring secretKey = "HA6zkS";
|
||||
Wstring serialNo = "";
|
||||
m_profileID = 0;
|
||||
Global.config.getString("Nick", m_baseNick, "LOGIN");
|
||||
DBGMSG("base nick is " << m_baseNick.get());
|
||||
m_baseNick.toLower();
|
||||
Global.config.getString("Title", title, "LOGIN");
|
||||
Global.config.getString("SecretKey", secretKey, "LOGIN");
|
||||
Global.config.getInt("ProfileID", m_profileID, "LOGIN");
|
||||
Global.config.getString("CDKey", serialNo, "LOGIN");
|
||||
PEERCallbacks callbacks;
|
||||
PEERBool pingRooms[NumRooms];
|
||||
PEERBool crossPingRooms[NumRooms];
|
||||
|
||||
// Setup the callbacks.
|
||||
///////////////////////
|
||||
memset(&callbacks, 0, sizeof(PEERCallbacks));
|
||||
callbacks.disconnected = DisconnectedCallback;
|
||||
callbacks.playerChangedNick = PlayerChangedNickCallback;
|
||||
callbacks.playerJoined = PlayerJoinedCallback;
|
||||
callbacks.playerLeft = PlayerLeftCallback;
|
||||
callbacks.roomMessage = RoomMessageCallback;
|
||||
callbacks.playerMessage = PlayerMessageCallback;
|
||||
callbacks.param = this;
|
||||
|
||||
// Init.
|
||||
////////
|
||||
m_peer = peerInitialize(&callbacks);
|
||||
if(!m_peer)
|
||||
{
|
||||
ERRMSG("Failed to init peer object" << endl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ping/cross-ping in no room.
|
||||
/////////////////////////////////
|
||||
pingRooms[TitleRoom] = PEERFalse;
|
||||
pingRooms[GroupRoom] = PEERFalse;
|
||||
pingRooms[StagingRoom] = PEERFalse;
|
||||
crossPingRooms[TitleRoom] = PEERFalse;
|
||||
crossPingRooms[GroupRoom] = PEERFalse;
|
||||
crossPingRooms[StagingRoom] = PEERFalse;
|
||||
|
||||
// Set the title.
|
||||
/////////////////
|
||||
if(!peerSetTitle(m_peer, title.get(), secretKey.get(), title.get(), secretKey.get(), 0, 30, PEERTrue, pingRooms, crossPingRooms))
|
||||
{
|
||||
peerShutdown(m_peer);
|
||||
ERRMSG("Failed to set the title" << endl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect.
|
||||
///////////
|
||||
m_connectSuccess = false;
|
||||
m_nick = m_baseNick.get();
|
||||
peerConnect(m_peer, m_baseNick.get(), m_profileID, NickErrorCallback, ConnectCallback, this, PEERTrue);
|
||||
if(!m_connectSuccess)
|
||||
{
|
||||
peerShutdown(m_peer);
|
||||
ERRMSG("Failed to connect" << endl);
|
||||
return;
|
||||
}
|
||||
|
||||
bool cdOk = false;
|
||||
peerAuthenticateCDKey(m_peer, serialNo.get(), AuthenticateCDKeyCallback, &cdOk, PEERTrue);
|
||||
if (!cdOk)
|
||||
{
|
||||
peerShutdown(m_peer);
|
||||
ERRMSG("Failed to auth CDKey " << serialNo.get() << endl);
|
||||
return;
|
||||
}
|
||||
|
||||
m_groupID = 0;
|
||||
peerListGroupRooms(m_peer, NULL, ListGroupRoomsCallback, &m_groupID, PEERTrue);
|
||||
m_groupID = s_groupID;
|
||||
|
||||
DBGMSG("QuickMatch room is " << m_groupID);
|
||||
|
||||
// Join the title room.
|
||||
///////////////////////
|
||||
peerJoinGroupRoom(m_peer, m_groupID, JoinCallback, this, PEERTrue);
|
||||
if(!m_joinSuccess)
|
||||
{
|
||||
peerDisconnect(m_peer);
|
||||
peerShutdown(m_peer);
|
||||
ERRMSG("Failed to join the title room" << endl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connected, so lets do our thing
|
||||
readLoop();
|
||||
|
||||
peerDisconnect(m_peer);
|
||||
peerShutdown(m_peer);
|
||||
}
|
||||
|
93
GeneralsMD/Code/Tools/matchbot/matcher.h
Normal file
93
GeneralsMD/Code/Tools/matchbot/matcher.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MATCHER_H__
|
||||
#define __MATCHER_H__
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
#include <configfile.h>
|
||||
#include <critsec.h>
|
||||
#include <threadfac.h>
|
||||
#include <tcp.h>
|
||||
#include <cstdarg>
|
||||
#include <sem4.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <peer/peer.h>
|
||||
|
||||
class MatcherClass
|
||||
{
|
||||
public:
|
||||
MatcherClass();
|
||||
virtual ~MatcherClass()
|
||||
{}
|
||||
|
||||
virtual void init(void)
|
||||
{}
|
||||
|
||||
virtual void checkMatches(void)
|
||||
{}
|
||||
|
||||
virtual void handleDisconnect( const char *reason )
|
||||
{}
|
||||
virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType )
|
||||
{}
|
||||
virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType )
|
||||
{}
|
||||
virtual void handlePlayerJoined( const char *nick )
|
||||
{}
|
||||
virtual void handlePlayerLeft( const char *nick )
|
||||
{}
|
||||
virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick )
|
||||
{}
|
||||
virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags )
|
||||
{}
|
||||
|
||||
void handleConnect( bool success );
|
||||
void handleGroupRoomList( bool success, int groupID, const char *name );
|
||||
void handleJoin( bool success );
|
||||
void handleNickError( const char *badNick );
|
||||
|
||||
void connectAndLoop( void );
|
||||
|
||||
protected:
|
||||
|
||||
Wstring getString(const Wstring& key);
|
||||
|
||||
Wstring m_baseNick;
|
||||
std::string m_nick;
|
||||
int m_profileID;
|
||||
PEER m_peer;
|
||||
bool m_connectSuccess;
|
||||
bool m_joinSuccess;
|
||||
void readLoop( void );
|
||||
|
||||
int done; // 0=no, neg=quit;error, pos=quit;success
|
||||
bool quiet;
|
||||
int m_groupID;
|
||||
|
||||
bool m_rotateLogs; // check for log rotation in this thread?
|
||||
time_t m_lastRotation;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __MATCHER_H__ */
|
||||
|
101
GeneralsMD/Code/Tools/matchbot/mydebug.cpp
Normal file
101
GeneralsMD/Code/Tools/matchbot/mydebug.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "mydebug.h"
|
||||
#include "streamer.h"
|
||||
#include "odevice.h"
|
||||
|
||||
|
||||
// static MyMsgManager *msg_manager=NULL;
|
||||
|
||||
// static int paranoid_enabled=0;
|
||||
static ostream *paranoid_ostream=NULL;
|
||||
static Streamer paranoid_streamer;
|
||||
|
||||
// Don't dare touch this semaphore in application code!
|
||||
#ifdef USE_SEM
|
||||
Sem4 MyDebugLibSemaphore;
|
||||
#else
|
||||
CritSec MyDebugLibSemaphore;
|
||||
#endif
|
||||
|
||||
|
||||
int MyMsgManager::setAllStreams(OutputDevice *device)
|
||||
{
|
||||
if (device==NULL)
|
||||
return(1);
|
||||
|
||||
MYDEBUGLOCK;
|
||||
paranoid_streamer.setOutputDevice(device);
|
||||
delete(paranoid_ostream);
|
||||
paranoid_ostream=new ostream(¶noid_streamer);
|
||||
|
||||
MYDEBUGUNLOCK;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int MyMsgManager::setParanoidStream(OutputDevice *device)
|
||||
{
|
||||
if (device==NULL)
|
||||
return(1);
|
||||
|
||||
MYDEBUGLOCK;
|
||||
paranoid_streamer.setOutputDevice(device);
|
||||
delete(paranoid_ostream);
|
||||
paranoid_ostream=new ostream(¶noid_streamer);
|
||||
MYDEBUGUNLOCK;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ostream *MyMsgManager::paranoidStream(void)
|
||||
{
|
||||
return(paranoid_ostream);
|
||||
}
|
||||
|
||||
|
||||
int MyMsgManager::ReplaceAllStreams(FileD * output_device, char *device_filename, char *copy_filename)
|
||||
{
|
||||
MYDEBUGLOCK;
|
||||
|
||||
delete(paranoid_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);
|
||||
|
||||
paranoid_streamer.setOutputDevice(output_device);
|
||||
paranoid_ostream = new ostream(¶noid_streamer);
|
||||
|
||||
MYDEBUGUNLOCK;
|
||||
|
||||
return(0);
|
||||
}
|
138
GeneralsMD/Code/Tools/matchbot/mydebug.h
Normal file
138
GeneralsMD/Code/Tools/matchbot/mydebug.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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 MYDEBUG_HEADER
|
||||
#define MYDEBUG_HEADER
|
||||
|
||||
#define USE_SEM
|
||||
|
||||
#include "wstypes.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <iostream.h>
|
||||
#include <strstrea.h>
|
||||
#else
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#ifdef USE_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: Changed from semaphores to critical sections because Windows doesn't provide
|
||||
// a good way of doing semaphores (I think)
|
||||
// MDC: Never mind, they seem to be working now! I'm leaving it in, though, so anyone can
|
||||
// flip a switch between the two.
|
||||
|
||||
#ifdef USE_SEM
|
||||
extern Sem4 MyDebugLibSemaphore;
|
||||
#define MYDEBUGLOCK MyDebugLibSemaphore.Wait()
|
||||
#define MYDEBUGUNLOCK MyDebugLibSemaphore.Post()
|
||||
#else
|
||||
extern CritSec MyDebugLibSemaphore;
|
||||
#define MYDEBUGLOCK MyDebugLibSemaphore.lock()
|
||||
#define MYDEBUGUNLOCK MyDebugLibSemaphore.unlock()
|
||||
#endif
|
||||
|
||||
// Print an information message
|
||||
#define PARANOIDMSG(X)\
|
||||
{\
|
||||
char timebuf[40]; \
|
||||
Xtime now; \
|
||||
now -= TimezoneOffset(); \
|
||||
now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \
|
||||
MYDEBUGLOCK; \
|
||||
if (MyMsgManager::paranoidStream()) \
|
||||
(*(MyMsgManager::paranoidStream())) << "HACK " << timebuf << " [" << \
|
||||
__FILE__ << " " << __LINE__ << "] " << X << endl; \
|
||||
MYDEBUGUNLOCK; \
|
||||
}
|
||||
|
||||
// Just get a stream to the information device, no extra junk
|
||||
#define PARANOIDSTREAM(X)\
|
||||
{\
|
||||
MYDEBUGLOCK; \
|
||||
if (MyMsgManager::paranoidStream()) \
|
||||
(*(MyMsgManager::paranoidStream())) << X;\
|
||||
MYDEBUGUNLOCK; \
|
||||
}
|
||||
|
||||
|
||||
//#undef MYDEBUGLOCK
|
||||
//#undef MYDEBUGUNLOCK
|
||||
|
||||
class MyMsgManager
|
||||
{
|
||||
protected:
|
||||
MyMsgManager();
|
||||
|
||||
public:
|
||||
static int setAllStreams(OutputDevice *device);
|
||||
static int setParanoidStream(OutputDevice *device);
|
||||
static int ReplaceAllStreams(FileD *output_device, char *device_filename, char *copy_filename);
|
||||
|
||||
static void enableParanoid(int flag);
|
||||
|
||||
static ostream *paranoidStream(void);
|
||||
};
|
||||
|
||||
#endif
|
132
GeneralsMD/Code/Tools/matchbot/rand.cpp
Normal file
132
GeneralsMD/Code/Tools/matchbot/rand.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rand.h"
|
||||
#include <cmath>
|
||||
|
||||
static const double theMultFactor = 1.0 / (pow(2, 8 * sizeof(unsigned int)) - 1.0);
|
||||
|
||||
RandClass::RandClass(int start)
|
||||
{
|
||||
seed[0] = 0xf22d0e56L;
|
||||
seed[1] = 0x883126e9L;
|
||||
seed[2] = 0xc624dd2fL;
|
||||
seed[3] = 0x702c49cL;
|
||||
seed[4] = 0x9e353f7dL;
|
||||
seed[5] = 0x6fdf3b64L;
|
||||
|
||||
unsigned int ax;
|
||||
|
||||
ax = start; /* mov eax,SEED */
|
||||
ax += 0xf22d0e56; /* add eax,0f22d0e56h */
|
||||
seed[0] = ax; /* mov seed,eax */
|
||||
ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */
|
||||
seed[1] = ax; /* mov seed+4,eax */
|
||||
ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */
|
||||
seed[2] = ax; /* mov seed+8,eax */
|
||||
ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */
|
||||
seed[3] = ax; /* mov seed+12,eax */
|
||||
ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */
|
||||
seed[4] = ax; /* mov seed+16,eax */
|
||||
ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */
|
||||
seed[5] = ax; /* mov seed+20,eax */
|
||||
}
|
||||
|
||||
// Add with carry. SUM is replaced with A + B + C, C is replaced with 1 if there was a carry, 0 if there wasn't. A carry occurred if the sum is less than one of the inputs. This is addition, so carry can never be more than one.
|
||||
#define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B)))
|
||||
|
||||
unsigned int RandClass::randomValue( void )
|
||||
{
|
||||
unsigned int ax;
|
||||
unsigned int c = 0;
|
||||
|
||||
|
||||
ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */
|
||||
/* add ax,seed+16 */
|
||||
seed[4] = ax; /* mov seed+8,ax */
|
||||
|
||||
ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */
|
||||
seed[3] = ax; /* mov seed+12,ax */
|
||||
|
||||
ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */
|
||||
seed[2] = ax; /* mov seed+8,ax */
|
||||
|
||||
ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */
|
||||
seed[1] = ax; /* mov seed+4,ax */
|
||||
|
||||
ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */
|
||||
seed[0] = ax; /* mov seed+0,ax */
|
||||
|
||||
/* Increment seed array, bubbling up the carries. */
|
||||
if (!++seed[5])
|
||||
{
|
||||
if (!++seed[4])
|
||||
{
|
||||
if (!++seed[3])
|
||||
{
|
||||
if (!++seed[2])
|
||||
{
|
||||
if (!++seed[1])
|
||||
{
|
||||
++seed[0];
|
||||
++ax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(ax);
|
||||
}
|
||||
|
||||
|
||||
int RandClass::Int(void)
|
||||
{
|
||||
return (int)randomValue();
|
||||
}
|
||||
|
||||
int RandClass::Int(int low, int high)
|
||||
{
|
||||
unsigned int delta = high - low + 1;
|
||||
int rval;
|
||||
|
||||
if (delta == 0)
|
||||
return high;
|
||||
|
||||
rval = ((int)(randomValue()%delta)) + low;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
double RandClass::Double(void)
|
||||
{
|
||||
return Double(0.0, 1.0);
|
||||
}
|
||||
|
||||
double RandClass::Double(double low, double high)
|
||||
{
|
||||
double delta = high - low;
|
||||
double rval;
|
||||
|
||||
if (delta <= 0.0)
|
||||
return high;
|
||||
|
||||
rval = ((double)(randomValue()) * theMultFactor) * delta + low;
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
45
GeneralsMD/Code/Tools/matchbot/rand.h
Normal file
45
GeneralsMD/Code/Tools/matchbot/rand.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RAND_H__
|
||||
#define __RAND_H__
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
class RandClass
|
||||
{
|
||||
public:
|
||||
RandClass(int start = 0);
|
||||
~RandClass()
|
||||
{}
|
||||
|
||||
|
||||
int Int(void);
|
||||
double Double(void);
|
||||
int Int(int low, int high);
|
||||
double Double(double low, double high);
|
||||
|
||||
private:
|
||||
|
||||
unsigned int randomValue( void );
|
||||
unsigned int seed[6];
|
||||
|
||||
};
|
||||
|
||||
#endif /* __RAND_H__ */
|
||||
|
709
GeneralsMD/Code/Tools/matchbot/wlib/arraylist.h
Normal file
709
GeneralsMD/Code/Tools/matchbot/wlib/arraylist.h
Normal file
|
@ -0,0 +1,709 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
483
GeneralsMD/Code/Tools/matchbot/wlib/configfile.cpp
Normal file
483
GeneralsMD/Code/Tools/matchbot/wlib/configfile.cpp
Normal file
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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);
|
||||
}
|
76
GeneralsMD/Code/Tools/matchbot/wlib/configfile.h
Normal file
76
GeneralsMD/Code/Tools/matchbot/wlib/configfile.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
129
GeneralsMD/Code/Tools/matchbot/wlib/critsec.cpp
Normal file
129
GeneralsMD/Code/Tools/matchbot/wlib/critsec.cpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
}
|
62
GeneralsMD/Code/Tools/matchbot/wlib/critsec.h
Normal file
62
GeneralsMD/Code/Tools/matchbot/wlib/critsec.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
680
GeneralsMD/Code/Tools/matchbot/wlib/dictionary.h
Normal file
680
GeneralsMD/Code/Tools/matchbot/wlib/dictionary.h
Normal file
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
|
51
GeneralsMD/Code/Tools/matchbot/wlib/filed.h
Normal file
51
GeneralsMD/Code/Tools/matchbot/wlib/filed.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
495
GeneralsMD/Code/Tools/matchbot/wlib/linkedlist.h
Normal file
495
GeneralsMD/Code/Tools/matchbot/wlib/linkedlist.h
Normal file
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
40
GeneralsMD/Code/Tools/matchbot/wlib/mboxd.h
Normal file
40
GeneralsMD/Code/Tools/matchbot/wlib/mboxd.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
57
GeneralsMD/Code/Tools/matchbot/wlib/monod.cpp
Normal file
57
GeneralsMD/Code/Tools/matchbot/wlib/monod.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
}
|
74
GeneralsMD/Code/Tools/matchbot/wlib/monod.h
Normal file
74
GeneralsMD/Code/Tools/matchbot/wlib/monod.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
32
GeneralsMD/Code/Tools/matchbot/wlib/odevice.h
Normal file
32
GeneralsMD/Code/Tools/matchbot/wlib/odevice.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
182
GeneralsMD/Code/Tools/matchbot/wlib/sem4.cpp
Normal file
182
GeneralsMD/Code/Tools/matchbot/wlib/sem4.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
|
64
GeneralsMD/Code/Tools/matchbot/wlib/sem4.h
Normal file
64
GeneralsMD/Code/Tools/matchbot/wlib/sem4.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
39
GeneralsMD/Code/Tools/matchbot/wlib/stderrd.h
Normal file
39
GeneralsMD/Code/Tools/matchbot/wlib/stderrd.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
40
GeneralsMD/Code/Tools/matchbot/wlib/stdoutd.h
Normal file
40
GeneralsMD/Code/Tools/matchbot/wlib/stdoutd.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
141
GeneralsMD/Code/Tools/matchbot/wlib/streamer.cpp
Normal file
141
GeneralsMD/Code/Tools/matchbot/wlib/streamer.cpp
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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);
|
||||
}
|
66
GeneralsMD/Code/Tools/matchbot/wlib/streamer.h
Normal file
66
GeneralsMD/Code/Tools/matchbot/wlib/streamer.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
39
GeneralsMD/Code/Tools/matchbot/wlib/syslogd.cpp
Normal file
39
GeneralsMD/Code/Tools/matchbot/wlib/syslogd.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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);
|
||||
}
|
48
GeneralsMD/Code/Tools/matchbot/wlib/syslogd.h
Normal file
48
GeneralsMD/Code/Tools/matchbot/wlib/syslogd.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
221
GeneralsMD/Code/Tools/matchbot/wlib/threadfac.cpp
Normal file
221
GeneralsMD/Code/Tools/matchbot/wlib/threadfac.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
124
GeneralsMD/Code/Tools/matchbot/wlib/threadfac.h
Normal file
124
GeneralsMD/Code/Tools/matchbot/wlib/threadfac.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
62
GeneralsMD/Code/Tools/matchbot/wlib/threadsafe.h
Normal file
62
GeneralsMD/Code/Tools/matchbot/wlib/threadsafe.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
71
GeneralsMD/Code/Tools/matchbot/wlib/timezone.cpp
Normal file
71
GeneralsMD/Code/Tools/matchbot/wlib/timezone.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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;
|
||||
}
|
41
GeneralsMD/Code/Tools/matchbot/wlib/timezone.h
Normal file
41
GeneralsMD/Code/Tools/matchbot/wlib/timezone.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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_
|
||||
|
61
GeneralsMD/Code/Tools/matchbot/wlib/ustring.h
Normal file
61
GeneralsMD/Code/Tools/matchbot/wlib/ustring.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
190
GeneralsMD/Code/Tools/matchbot/wlib/wdebug.cpp
Normal file
190
GeneralsMD/Code/Tools/matchbot/wlib/wdebug.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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);
|
||||
}
|
317
GeneralsMD/Code/Tools/matchbot/wlib/wdebug.h
Normal file
317
GeneralsMD/Code/Tools/matchbot/wlib/wdebug.h
Normal file
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
597
GeneralsMD/Code/Tools/matchbot/wlib/wstring.cpp
Normal file
597
GeneralsMD/Code/Tools/matchbot/wlib/wstring.cpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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;
|
||||
}
|
||||
}
|
93
GeneralsMD/Code/Tools/matchbot/wlib/wstring.h
Normal file
93
GeneralsMD/Code/Tools/matchbot/wlib/wstring.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
118
GeneralsMD/Code/Tools/matchbot/wlib/wstypes.h
Normal file
118
GeneralsMD/Code/Tools/matchbot/wlib/wstypes.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
802
GeneralsMD/Code/Tools/matchbot/wlib/wtime.cpp
Normal file
802
GeneralsMD/Code/Tools/matchbot/wlib/wtime.cpp
Normal file
|
@ -0,0 +1,802 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
_CRTIMP 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;
|
||||
}
|
128
GeneralsMD/Code/Tools/matchbot/wlib/wtime.h
Normal file
128
GeneralsMD/Code/Tools/matchbot/wlib/wtime.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
1015
GeneralsMD/Code/Tools/matchbot/wlib/xtime.cpp
Normal file
1015
GeneralsMD/Code/Tools/matchbot/wlib/xtime.cpp
Normal file
File diff suppressed because it is too large
Load diff
146
GeneralsMD/Code/Tools/matchbot/wlib/xtime.h
Normal file
146
GeneralsMD/Code/Tools/matchbot/wlib/xtime.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
319
GeneralsMD/Code/Tools/matchbot/wnet/field.cpp
Normal file
319
GeneralsMD/Code/Tools/matchbot/wnet/field.cpp
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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;
|
||||
}
|
||||
}
|
||||
|
101
GeneralsMD/Code/Tools/matchbot/wnet/field.h
Normal file
101
GeneralsMD/Code/Tools/matchbot/wnet/field.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
};
|
||||
|
||||
|
||||
|
522
GeneralsMD/Code/Tools/matchbot/wnet/packet.cpp
Normal file
522
GeneralsMD/Code/Tools/matchbot/wnet/packet.cpp
Normal file
|
@ -0,0 +1,522 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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;
|
||||
}
|
||||
|
108
GeneralsMD/Code/Tools/matchbot/wnet/packet.h
Normal file
108
GeneralsMD/Code/Tools/matchbot/wnet/packet.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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;
|
||||
};
|
||||
|
1242
GeneralsMD/Code/Tools/matchbot/wnet/tcp.cpp
Normal file
1242
GeneralsMD/Code/Tools/matchbot/wnet/tcp.cpp
Normal file
File diff suppressed because it is too large
Load diff
199
GeneralsMD/Code/Tools/matchbot/wnet/tcp.h
Normal file
199
GeneralsMD/Code/Tools/matchbot/wnet/tcp.h
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
372
GeneralsMD/Code/Tools/matchbot/wnet/udp.cpp
Normal file
372
GeneralsMD/Code/Tools/matchbot/wnet/udp.cpp
Normal file
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
}
|
114
GeneralsMD/Code/Tools/matchbot/wnet/udp.h
Normal file
114
GeneralsMD/Code/Tools/matchbot/wnet/udp.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
Reference in a new issue