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

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

View file

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

View file

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

View file

@ -0,0 +1,76 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Carpenter (The RedAlert ladder creator)
File Name : configfile.h
Author : Neal Kettler
Start Date : June 9, 1997
Last Update : May 13, 1999
\***************************************************************************/
#ifndef CONFIGFILE_HEADER
#define CONFIGFILE_HEADER
#include "wstypes.h"
#include "dictionary.h"
#include "wstring.h"
#include "critsec.h"
#include "arraylist.h"
class ConfigFile
{
public:
ConfigFile();
~ConfigFile();
bit8 readFile(FILE *config);
bit8 getString(IN Wstring &key,OUT Wstring &value, IN char *section=NULL) const;
bit8 getString(IN char *key,OUT Wstring &value, IN char *section=NULL) const;
bit8 getInt(IN Wstring &key,OUT sint32 &value, IN char *section=NULL) const;
bit8 getInt(IN char *key,OUT sint32 &value, IN char *section=NULL) const;
bit8 getInt(IN Wstring &key,OUT sint16 &value, IN char *section=NULL) const;
bit8 getInt(IN char *key,OUT sint16 &value, IN char *section=NULL) const;
// Enumerate through the config lines
bit8 enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section=NULL) const;
// Manual update of config file
bit8 setString(IN Wstring &key,IN Wstring &value, IN char *section=NULL);
bit8 setString(IN char *key,IN Wstring &value, IN char *section=NULL);
bit8 setInt(IN Wstring &key,IN sint32 &value, IN char *section=NULL);
bit8 setInt(IN char *key,IN sint32 &value, IN char *section=NULL);
bit8 removeEntry(IN Wstring &key, IN char *section=NULL);
bit8 removeEntry(IN char *key, IN char *section=NULL);
bit8 writeFile(FILE *config); // Does not preserve comments, etc
ArrayList<Wstring> sectionList; // stores the names of all sections
private:
Dictionary<Wstring,Wstring> Dictionary_; // stores the mappings from keys
// to value strings
// The lock is only needed around the immediate access to the dictionary, no writes
// are allowed so you don't need to worry about an outer lock around the enumerate
CritSec Critsec_; // lock around dictionary
};
#endif

View file

@ -0,0 +1,129 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "critsec.h"
#include <assert.h>
#include "wlib/wdebug.h"
CritSec::CritSec()
{
#ifdef _UNIX
pthread_mutex_init(&Mutex_, NULL);
RefCount_ = 0;
#elif defined(_WIN32)
InitializeCriticalSection(&CritSec_);
#endif
}
CritSec::~CritSec()
{
#ifdef _UNIX
pthread_mutex_destroy(&Mutex_);
#elif defined(_WIN32)
DeleteCriticalSection(&CritSec_);
#endif
}
// The "lock" function blocks until the mutex is available.
// Returns 0 on success, error code on error.
//
// A thread that already has a lock will increment a reference count if it calls
// lock again. It must then call unlock() enough times to get the reference to 0.
//
// If refcount is not null you can get the current ref counter after the lock.
//
sint32 CritSec::lock(int *refcount) RO
{
#ifdef _UNIX
sint32 status;
// I TRY to get the lock. IF I succeed, OR if I fail because
// I already have the lock, I just increment the reference
// count and return.
if (((status = pthread_mutex_trylock(&Mutex_)) == 0) ||
((status == EBUSY) && (ThreadId_ == pthread_self())))
{
ThreadId_ = pthread_self();
RefCount_++;
if (refcount)
*refcount=RefCount_;
return(0);
}
// Otherwise, I wait for the lock.
if ((status = pthread_mutex_lock(&Mutex_)) == 0)
{
assert(RefCount_ == 0);
ThreadId_ = pthread_self();
RefCount_++;
}
else
{
ERRMSG("pthread_mutex_lock: " << strerror(errno));
}
if (refcount)
*refcount=RefCount_;
return(status);
#elif defined(_WIN32)
// TOFIX update the refcount
EnterCriticalSection(&CritSec_);
return(0);
#else
#error Must define either _WIN32 or _UNIX
#endif
}
// The "unlock" function release the critical section.
sint32 CritSec::unlock(void) RO
{
#ifdef _UNIX
sint32 status = 0;
assert(RefCount_ >= 0);
if (RefCount_ <= 0)
{
//ERRMSG("unlocking unlocked mutex!");
return(-1);
}
///assert(ThreadId_ == pthread_self());
if (ThreadId_ != pthread_self())
{
WRNMSG("tried to unlock a mutex not owned by self");
return(-1);
}
if (--RefCount_ == 0)
{
// Set thread id to zero -- we're going to release mutex
ThreadId_ = (pthread_t)0;
// Unlock the mutex.
if ((status = pthread_mutex_unlock(&Mutex_)) != 0)
ERRMSG("pthread_mutex_lock: " << strerror(errno));
}
return status;
#elif defined(_WIN32)
LeaveCriticalSection(&CritSec_);
return(0);
#endif
}

View file

@ -0,0 +1,62 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CRITSEC_HEADER
#define CRITSEC_HEADER
#include "wstypes.h"
#ifdef _WIN32
#include <windows.h>
#include <winbase.h>
#elif defined(_UNIX)
#include <pthread.h>
#include <errno.h>
#endif
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
//
// Critical Section built either on a POSIX Mutex, or a Win32 Critical Section
//
// POSIX version is done by keeping a thread_id and a reference count. Win32 version
// just calls the built in functions.
//
class CritSec
{
public:
CritSec();
~CritSec();
sint32 lock(int *refcount=NULL) RO;
sint32 unlock(void) RO;
protected:
#ifdef _WIN32
mutable CRITICAL_SECTION CritSec_;
#else
mutable pthread_mutex_t Mutex_; // Mutex lock
mutable pthread_t ThreadId_; // Owner of mutex
mutable int RefCount_; // Reference count
#endif
};
#endif

View file

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

View file

@ -0,0 +1,51 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FILED_HEADER
#define FILED_HEADER
#include "odevice.h"
class FileD : public OutputDevice
{
public:
FileD(IN char *filename, IN char *mode = "w")
{
out=fopen(filename,mode);
if (out==NULL)
out=fopen("FileDev.out",mode);
}
virtual ~FileD()
{ fclose(out); }
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memset(string,0,len+1);
memcpy(string,str,len);
fprintf(out,"%s",string);
delete[](string);
fflush(out);
return(len);
}
FILE *out;
};
#endif

View file

@ -0,0 +1,495 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name:
File Name : linkedlist.h
Author : Neal Kettler
Start Date : June 19, 1997
Last Update : June 19, 1997
Linked list template. This is a fairly standard doubly linked list that
allows insertion and removal at any point in the list. A current pointer
is used to quickly access items when they are examined sequentially.
Copies of the data are stored instead of a pointer to the original.
If you want to store pointers then the template should be of a pointer type.
\****************************************************************************/
#ifndef LINKEDLIST_HEADER
#define LINKEDLIST_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "wstypes.h"
template <class T>
class LNode
{
public:
T Node;
LNode<T> *Next;
LNode<T> *Prev;
};
template <class T>
class LinkedList
{
public:
LinkedList();
LinkedList(LinkedList<T> &other);
~LinkedList();
// Remove all entries from the lsit
void clear(void);
// Add a node after the zero based 'pos'
bit8 add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL);
bit8 addTail(IN T &node, OUT T **newnodeptr=NULL);
bit8 addHead(IN T &node, OUT T **newnodeptr=NULL);
// Remove a node
bit8 remove(OUT T &node,sint32 pos);
bit8 remove(sint32 pos);
bit8 removeHead(OUT T &node);
bit8 removeTail(OUT T &node);
// Get a node without removing from the list
bit8 get(OUT T &node,sint32 pos);
bit8 getHead(OUT T &node);
bit8 getTail(OUT T &node);
// Get a pointer to the internally managed data (careful!)
bit8 getPointer(OUT T **node, sint32 pos);
// Get the number of entries in the list
sint32 length(void);
// Print information on the list
void print(IN FILE *out);
// assignment operator
LinkedList<T> &operator=(LinkedList<T> &other);
private:
sint32 Entries; // Number of entries
LNode<T> *Head; // Head of the list
LNode<T> *Tail; // Tail of the list
LNode<T> *Current; // Current pointer & index for speed only
sint32 CurIndex;
};
//Create the empty list
template <class T>
LinkedList<T>::LinkedList()
{
Entries=0;
Head=Tail=Current=NULL;
CurIndex=-1; // Not valid when 0 entries
}
// copy constructor
template <class T>
LinkedList<T>::LinkedList(LinkedList<T> &other)
{
Entries=0;
Head=Tail=Current=NULL;
CurIndex=-1; // Not valid when 0 entries
(*this)=other;
}
//Free all the memory...
template <class T>
LinkedList<T>::~LinkedList()
{
clear(); // Remove the entries
}
// assignment operator
template <class T>
LinkedList<T> &LinkedList<T>::operator=(LinkedList<T> &other)
{
T node;
clear();
for (int i=0; i<other.length(); i++)
{
other.get(node,i);
addTail(node);
}
return(*this);
}
// Remove all the entries and free the memory
template <class T>
void LinkedList<T>::clear()
{
LNode<T> *temp,*del;
temp=Head;
while (temp) {
del=temp;
temp=temp->Next;
delete(del);
}
Entries=0;
CurIndex=-1;
Head=Tail=Current=NULL;
}
// When adding into a position, the new node goes at the zero based slot
// specified by pos. All other nodes get moved one slot down.
template <class T>
bit8 LinkedList<T>::add(IN T &node,sint32 pos, OUT T **newnodeptr)
{
LNode<T> *temp;
LNode<T> *item;
if (pos<0)
pos=0;
if (pos>Entries)
pos=Entries;
item=(LNode<T> *)new LNode<T>;
assert(item!=NULL);
item->Node=node; // copy the passed in object
item->Prev=NULL;
item->Next=NULL;
if (newnodeptr)
*newnodeptr=&(item->Node);
if ((pos==0)||(pos==Entries)) { // Both cases can be true for a new list!
if (pos==0) {
item->Next=Head;
if (Head)
Head->Prev=item;
Head=item;
}
if (pos==Entries) {
item->Prev=Tail;
if (Tail)
Tail->Next=item;
Tail=item;
}
Entries++;
Current=item;
CurIndex=pos;
return(TRUE);
}
// If control is here, we know the new node is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
item->Next=Current;
item->Prev=Current->Prev;
Current->Prev=item;
item->Prev->Next=item;
Current=item;
Entries++;
return(TRUE);
}
// Check the other possible speedup (adding after CurIndex)
if (pos==CurIndex+1) {
item->Next=Current->Next;
item->Prev=Current;
Current->Next=item;
item->Next->Prev=item;
Current=item;
CurIndex++;
Entries++;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
temp=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
temp=temp->Next;
assert(temp!=NULL);
}
item->Next=temp;
item->Prev=temp->Prev;
temp->Prev=item;
item->Prev->Next=item;
Current=item;
CurIndex=pos;
Entries++;
return(TRUE);
}
// Add to the first node, all others get shifted down one slot
template <class T>
bit8 LinkedList<T>::addHead(IN T &node, OUT T **newnodeptr)
{
return(add(node,0,newnodeptr));
}
// Append to the end of the list
template <class T>
bit8 LinkedList<T>::addTail(IN T &node, OUT T **newnodeptr)
{
return(add(node,length(),newnodeptr));
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(OUT T &node, sint32 pos)
{
////////LNode<T> *temp;
LNode<T> *item;
if (Entries==0)
return(FALSE);
if (pos<0)
pos=0;
if (pos>=Entries)
pos=Entries-1;
if ((pos==0)||(pos==Entries-1)) { // Both can be true for a 1 item list
if (pos==0) {
item=Head;
if (item->Next)
item->Next->Prev=NULL;
Head=item->Next;
node=item->Node;
Current=Head;
CurIndex=0;
}
if (pos==Entries-1) {
item=Tail;
if (item->Prev)
item->Prev->Next=NULL;
Tail=item->Prev;
node=item->Node;
Current=Tail;
CurIndex=Entries-2;
}
delete(item);
Entries--;
if (Entries==0) { // Super paranoia check
assert(Current==NULL);
assert(CurIndex==-1);
assert(Head==NULL);
assert(Tail==NULL);
}
return(TRUE);
}
// If control is here, we know the target node is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
item=Current;
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
// CurIndex stays the same
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// Check the other possible speedup (removing after CurIndex)
if (pos==CurIndex+1) {
item=Current->Next;
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
CurIndex++;
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
item=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
item=item->Next;
assert(item!=NULL);
}
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
CurIndex=pos;
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(sint32 pos)
{
T temp_node;
return(remove(temp_node,pos));
}
// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::removeHead(OUT T &node)
{
return(remove(node,0));
}
// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::removeTail(OUT T &node)
{
return(remove(node,Entries-1));
}
template <class T>
bit8 LinkedList<T>::get(OUT T &node, sint32 pos)
{
T *objptr;
bool retval=getPointer(&objptr,pos);
if (retval && objptr)
node=*objptr;
return(retval);
}
template <class T>
bit8 LinkedList<T>::getPointer(OUT T **node,sint32 pos)
{
if ((node==0)||(Entries==0))
return(FALSE);
LNode<T> *item;
if (pos<0)
{
//pos=0;
return(FALSE);
}
if (pos>=Entries)
{
//pos=Entries-1;
return(FALSE);
}
if (pos==0) {
*node=&(Head->Node);
return(TRUE);
} else if (pos==Entries-1) {
*node=&(Tail->Node);
return(TRUE);
}
// If control reaches here, we know target is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
*node=&(Current->Node);
return(TRUE);
} else if (pos==CurIndex+1) {
*node=&(Current->Next->Node);
CurIndex++;
Current=Current->Next;
return(TRUE);
} else if (pos==CurIndex-1) {
*node=&(Current->Prev->Node);
CurIndex--;
Current=Current->Prev;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
item=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
item=item->Next;
assert(item!=NULL);
}
*node=&(item->Node);
CurIndex=pos;
Current=item;
return(TRUE);
}
// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::getHead(OUT T &node)
{
return(get(node,0));
}
// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::getTail(OUT T &node)
{
return(get(node,Entries-1));
}
template <class T>
void LinkedList<T>::print(IN FILE *out)
{
LNode<T> *temp;
fprintf(out,"--------------------\n");
fprintf(out,"Entries = %d\n",length());
fprintf(out,"H = %8p C = %8p (%d) T = %8p\n",Head,Current,CurIndex,Tail);
temp=Head;
while (temp) {
fprintf(out," %8p<-((%8p))->%8p \n",temp->Prev,temp,temp->Next);
temp=temp->Next;
}
fprintf(out,"--------------------\n");
}
// Return the current length of the list
template <class T>
sint32 LinkedList<T>::length(void) {
return(Entries);
}
#endif

View file

@ -0,0 +1,40 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MBOXD_HEADER
#define MBOXD_HEADER
#include "odevice.h"
class MboxD : public OutputDevice
{
public:
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memset(string,0,len+1);
memcpy(string,str,len);
MessageBox(NULL,string,"Debug Message", MB_OK | MB_ICONINFORMATION);
delete[](string);
return(len);
}
};
#endif

View file

@ -0,0 +1,57 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "monod.h"
MonoD::MonoD(void)
{
#ifdef _WIN32
unsigned long retval;
handle = CreateFile("\\\\.\\MONO", GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE)
{
DeviceIoControl(handle, (DWORD)IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0,
&retval,0);
}
#endif
}
MonoD::~MonoD()
{
#ifdef _WIN32
CloseHandle(handle);
handle=NULL;
#endif
}
int MonoD::print(const char *str, int len)
{
#ifdef _WIN32
unsigned long retval;
WriteFile(handle, str, len, &retval, NULL);
////DeviceIoControl(handle, (DWORD)IOCTL_MONO_PRINT_RAW, (void *)str, len, NULL, 0,
//// &retval,0);
return(len);
#else
for (int i=0; i<len; i++)
fprintf(stderr,"%c",str[i]);
return(len);
#endif
}

View file

@ -0,0 +1,74 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MONOD_HEADER
#define MONOD_HEADER
#include <stdlib.h>
#include <stdio.h>
#include "odevice.h"
///////////////////////// WIN32 ONLY ///////////////////////////////////
#ifdef _WIN32
#include <windows.h>
#include <winioctl.h>
/*
** This is the identifier for the Monochrome Display Driver
*/
#define FILE_DEVICE_MONO 0x00008000
/*
** These are the IOCTL commands supported by the Monochrome Display Driver.
*/
#define IOCTL_MONO_HELP_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_PRINT_RAW CTL_CODE(FILE_DEVICE_MONO, 0x802, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_CURSOR CTL_CODE(FILE_DEVICE_MONO, 0x803, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SCROLL CTL_CODE(FILE_DEVICE_MONO, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_BRING_TO_TOP CTL_CODE(FILE_DEVICE_MONO, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_ATTRIBUTE CTL_CODE(FILE_DEVICE_MONO, 0x806, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_PAN CTL_CODE(FILE_DEVICE_MONO, 0x807, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_LOCK CTL_CODE(FILE_DEVICE_MONO, 0x808, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_UNLOCK CTL_CODE(FILE_DEVICE_MONO, 0x809, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80A, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_RESET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80B, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_SET_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80C, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_CLEAR_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80D, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_MONO_FILL_ATTRIB CTL_CODE(FILE_DEVICE_MONO, 0x80E, METHOD_BUFFERED, FILE_WRITE_DATA)
#endif // ifdef _WIN32
class MonoD : public OutputDevice
{
public:
MonoD();
~MonoD();
virtual int print(const char *str,int len);
private:
#ifdef _WIN32
HANDLE handle;
#endif
};
#endif

View file

@ -0,0 +1,32 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ODEVICE_HEADER
#define ODEVICE_HEADER
// This virtual base class provides an interface for output devices
// that can be used for the debugging package.
class OutputDevice
{
public:
OutputDevice() {}
virtual ~OutputDevice() {};
virtual int print(const char *s,int len)=0;
};
#endif

View file

@ -0,0 +1,182 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/******************************************************************************\
sem4.cpp Neal Kettler
Simple Posix semaphore class
This is useful because the constructor will automatically call sem_init
and you don't have to worry about it. It also allows for other semaphore
libraries if you don't have posix.
\******************************************************************************/
#include "sem4.h"
#ifdef _REENTRANT
Sem4::Sem4()
{
#ifndef _WINDOWS
sem_init(&sem,1,1);
#else
sem = CreateSemaphore(NULL, 1, 1, NULL);
#endif
}
Sem4::Sem4(uint32 value)
{
#ifndef _WINDOWS
sem_init(&sem,1,value);
#else
sem = CreateSemaphore(NULL, value, value, NULL);
#endif
}
Sem4::~Sem4()
{
#ifndef _WINDOWS
sem_destroy(&sem);
#else
if (sem) CloseHandle(sem);
#endif
}
sint32 Sem4::Wait(void) const
{
#ifndef _WINDOWS
return(sem_wait((sem_t *)&sem));
#else
if (!sem)
return -1; // no semaphore!
DWORD dwWaitResult = WaitForSingleObject(sem, INFINITE);
switch (dwWaitResult) {
case WAIT_OBJECT_0: // The semaphore object was signaled.
return 0;
break;
case WAIT_TIMEOUT: // Should not happen ;)
return -1;
break;
}
return -1;
#endif
}
sint32 Sem4::Post(void) const
{
#ifndef _WINDOWS
return(sem_post((sem_t *)&sem));
#else
if (!sem)
return -1;
if (!ReleaseSemaphore(sem, 1 ,NULL))
return -1;
return 0;
#endif
}
sint32 Sem4::TryWait(void) const
{
#ifndef _WINDOWS
return(sem_trywait((sem_t *)&sem));
#else
if (!sem)
return -1;
DWORD dwWaitResult = WaitForSingleObject(sem, 0L);
switch (dwWaitResult) {
case WAIT_OBJECT_0: // The semaphore object was signaled.
return 0;
break;
case WAIT_TIMEOUT:
return -1;
break;
}
return -1;
#endif
}
sint32 Sem4::GetValue(int *sval) const
{
#ifndef _WINDOWS
return(sem_getvalue((sem_t *)&sem,sval));
#else
if (!sem)
return -1;
long prev;
if (!ReleaseSemaphore(sem, 0, &prev))
return -1;
if (sval)
*sval = prev;
return 0;
#endif
}
sint32 Sem4::Destroy(void)
{
#ifndef _WINDOWS
return(sem_destroy(&sem));
#else
return CloseHandle(sem);
#endif
}
#else
/****************************************************************************
non threaded versions that do nothing
*****************************************************************************/
Sem4::Sem4()
{
}
Sem4::Sem4(uint32)
{
}
Sem4::~Sem4()
{
}
sint32 Sem4::Wait(void) const
{
return(0);
}
sint32 Sem4::Post(void) const
{
return(0);
}
sint32 Sem4::TryWait(void) const
{
return(0);
}
sint32 Sem4::GetValue(int *) const
{
return(0);
}
sint32 Sem4::Destroy(void)
{
return(0);
}
#endif

View file

@ -0,0 +1,64 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SEM4_HEADER
#define SEM4_HEADER
#include <limits.h>
#ifndef _WINDOWS
#include <unistd.h>
#endif
#include "wstypes.h"
#ifdef _REENTRANT
#ifndef _WINDOWS
#include <semaphore.h>
#else
#include <windows.h>
#endif // _WINDOWS
#endif // _REENTRANT
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
class Sem4
{
private:
#ifdef _REENTRANT
#ifndef _WINDOWS
sem_t sem;
#else
HANDLE sem;
#endif
#endif
public:
Sem4();
Sem4(uint32 value);
~Sem4();
sint32 Wait(void) const;
sint32 TryWait(void) const;
sint32 Post(void) const;
sint32 GetValue(int *sval) const;
sint32 Destroy(void);
};
#endif

View file

@ -0,0 +1,39 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STDERRD_HEADER
#define STDERRD_HEADER
#include "odevice.h"
class StderrD : public OutputDevice
{
public:
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memset(string,0,len+1);
memcpy(string,str,len);
fprintf(stderr,"%s",string);
delete[](string);
return(len);
}
};
#endif

View file

@ -0,0 +1,40 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STDOUTD_HEADER
#define STDOUTD_HEADER
#include "odevice.h"
class StdoutD : public OutputDevice
{
public:
virtual int print(const char *str,int len)
{
char *string=new char[len+1];
memcpy(string,str,len);
string[len]=0;
fprintf(stdout,"%s",string);
fflush(stdout);
delete[](string);
return(len);
}
};
#endif

View file

@ -0,0 +1,140 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "streamer.h"
#ifdef _WIN32
#include <windows.h>
#endif
Streamer::Streamer() : streambuf()
{
int state=unbuffered();
unbuffered(0); // 0 = buffered, 1 = unbuffered
}
Streamer::~Streamer()
{
///////// calling sync seems to cause crashes here on Win32
//sync();
/////////
delete[](base());
}
int Streamer::setOutputDevice(OutputDevice *device)
{
Output_Device=device;
return(0);
}
// put n chars from string into buffer
int Streamer::xsputn(const char* buf, int size) //implementation of sputn
{
if (size<=0) // Nothing to do
return(0);
const unsigned char *ptr=(const unsigned char *)buf;
for (int i=0; i<size; i++, ptr++)
{
if(*ptr=='\n')
{
if (overflow(*ptr)==EOF)
return(i);
}
else if (sputc(*ptr)==EOF)
return(i);
}
return(size);
}
// Flush the buffer and make more room if needed
int Streamer::overflow(int c)
{
if (c==EOF)
return(sync());
if ((pbase()==0) && (doallocate()==0))
return(EOF);
if((pptr() >= epptr()) && (sync()==EOF))
return(EOF);
else {
sputc(c);
if ((unbuffered() && c=='\n' || pptr() >= epptr())
&& sync()==EOF) {
return(EOF);
}
return(c);
}
}
// This is a write only stream, this should never happen
int Streamer::underflow(void)
{
return(EOF);
}
int Streamer::doallocate()
{
if (base()==NULL)
{
char *buf=new char[(2*STREAMER_BUFSIZ)]; // deleted by destructor
memset(buf,0,2*STREAMER_BUFSIZ);
// Buffer
setb(
buf, // base pointer
buf+STREAMER_BUFSIZ, // ebuf pointer (end of buffer);
0); // 0 = manual deletion of buff
// Get area
setg(
buf, // eback
buf, // gptr
buf); // egptr
buf+=STREAMER_BUFSIZ;
// Put area
setp(buf,buf+STREAMER_BUFSIZ);
return(1);
}
else
return(0);
}
int Streamer::sync()
{
if (pptr()<=pbase()) {
return(0);
}
int wlen=pptr()-pbase();
if (Output_Device)
{
Output_Device->print(pbase(),wlen);
}
if (unbuffered()) {
setp(pbase(),pbase());
}
else {
setp(pbase(),pbase()+STREAMER_BUFSIZ);
}
return(0);
}

View file

@ -0,0 +1,66 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STREAMER_HEADER
#define STREAMER_HEADER
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <iostream.h>
#include <string.h>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#include "odevice.h"
#ifndef STREAMER_BUFSIZ
// This limits the number of characters that can be sent to a single 'print'
// call. If your debug message is bigger than this, it will get split over
// multiple 'print' calls. That's usually not a problem.
#define STREAMER_BUFSIZ 2048
#endif
// Provide a streambuf interface for a class that can 'print'
class Streamer : public streambuf
{
public:
Streamer();
virtual ~Streamer();
int setOutputDevice(OutputDevice *output_device);
protected:
// Virtual methods from streambuf
int xsputn(const char* s, int n); // buffer some characters
int overflow(int = EOF); // flush buffer and make more room
int underflow(void); // Does nothing
int sync();
int doallocate(); // allocate a buffer
OutputDevice *Output_Device;
};
#endif

View file

@ -0,0 +1,39 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "syslogd.h"
SyslogD::SyslogD(char *ident,int logopt,int facility,int _priority)
{
#ifndef _WINDOWS
openlog(ident,logopt,facility);
priority=_priority;
#endif
}
int SyslogD::print(const char *str, int len)
{
#ifndef _WINDOWS
char *temp_str=new char[len+1];
memset(temp_str,0,len+1);
strncpy(temp_str,str,len);
syslog(priority,temp_str);
delete[](temp_str);
#endif
return(len);
}

View file

@ -0,0 +1,48 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SYSLOGD_HEADER
#define SYSLOGD_HEADER
#include <stdlib.h>
#include <stdio.h>
#ifndef _WINDOWS
#include <syslog.h>
#endif
#include <string.h>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#include "odevice.h"
// Windows doesn't have a syslog equivalent (does it?), so this class does little there
class SyslogD : public OutputDevice
{
public:
SyslogD(char *ident,int logopt,int facility,int priority);
virtual int print(const char *str,int len);
private:
int priority;
};
#endif

View file

@ -0,0 +1,221 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define THREADFAC_CODE
#include "threadfac.h"
int Runnable::ThreadCount_ = 0;
CritSec Runnable::CritSec_; // to protect ThreadCount_
// MDC: Made all this dependent on _REENTRANT being defined so VC++ doesn't complain on
// single-threaded programs...
//
// Note: I chose the following type signature for thread functions
// void function(void *data);
//
//
// Since Win32 & POSIX have different type signatures for the thread entry points
// an intermediate system-dependent function in this file gets called first.
// That function then calls the system independent version. So the system dependent
// version needs 2 Items 1) The address of the _real_ thread func 2) the data
// to pass. We only have 1 argument available, so you figure out the rest...
//
// This is for internal use only
//
struct ThreadInformation
{
void *startPoint; // The address of the _real_ thread function, or class
void *data; // data to pass to real thread function or class
bit8 destroy; // only applies to classes, should delete after execution?
};
//
// Start a thread inside a class
//
bit8 ThreadFactory::startThread(Runnable &runable, void *data, bit8 destroy)
{
#ifdef _REENTRANT
{
Runnable::CritSec_.lock();
Runnable::ThreadCount_++;
Runnable::CritSec_.unlock();
}
ThreadInformation *tInfo=new ThreadInformation;
tInfo->startPoint=(void *)&runable;
tInfo->data=data;
tInfo->destroy=destroy;
#ifdef _WIN32
// Under windows call _beginthreadex instead of CreateThread so you can
// use all the normal C library stuff. (IMPORTANT!!!)
uint32 handle;
uint32 stup1d;
handle=_beginthreadex(NULL,0, threadClassLauncher, tInfo, 0, &stup1d);
if (handle!=NULL)
return(TRUE);
else
{
{
runable.CritSec_.lock();
runable.ThreadCount_--; // Ok, so it didn't really start
runable.CritSec_.unlock();
}
return(FALSE);
}
#else // UNIX
// Setup thread attributes for client threads
int retval;
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM);
retval=pthread_create(NULL,&threadAttr, threadClassLauncher, tInfo);
if (retval==0)
return(TRUE);
else
{
{
runable.CritSec_.lock();
runable.ThreadCount_--; // Ok, so it didn't really start
runable.CritSec_.unlock();
}
return(FALSE);
}
#endif
#else
return (FALSE);
#endif /* _REENTRANT */
}
//
// Start a thread inside a function
//
bit8 ThreadFactory::startThread(void (*start_func)(void *), void *data)
{
#ifdef _REENTRANT
ThreadInformation *tInfo=new ThreadInformation;
tInfo->startPoint=start_func;
tInfo->data=data;
#ifdef _WIN32
// Under windows call _beginthreadex instead of CreateThread so you can
// use all the normal C library stuff. (IMPORTANT!!!)
uint32 handle;
unsigned temp;
handle=_beginthreadex(NULL,0, threadFuncLauncher, tInfo, 0, &temp);
if (handle!=NULL)
return(TRUE);
return(FALSE);
#else // UNIX
// Setup thread attributes for client threads
int retval;
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM);
retval=pthread_create(NULL,&threadAttr, threadFuncLauncher, tInfo);
if (retval==0)
return(TRUE);
else
return(FALSE);
#endif
#else
return(FALSE);
#endif /* REENTRANT */
}
#ifdef _WIN32
unsigned __stdcall threadFuncLauncher(void *temp)
#else // UNIX
void *threadFuncLauncher(void *temp)
#endif
{
ThreadInformation *tInfo=(ThreadInformation *)temp;
void (*start_func)(void *);
start_func=(void (*)(void *))tInfo->startPoint;
void *data=tInfo->data;
delete(tInfo);
start_func(data);
return(0);
}
#ifdef _WIN32
unsigned __stdcall threadClassLauncher(void *temp)
#else // UNIX
void *threadClassLauncher(void *temp)
#endif
{
ThreadInformation *tInfo=(ThreadInformation *)temp;
Runnable *thrClass=(Runnable *)tInfo->startPoint;
void *data=tInfo->data;
bit8 destroy=tInfo->destroy;
delete(tInfo);
thrClass->run(data);
if (destroy) // May want to free memory after thread finishes
delete(thrClass);
{
Runnable::CritSec_.lock();
Runnable::ThreadCount_--;
Runnable::CritSec_.unlock();
}
#ifdef _WIN32
ExitThread(0); // is this really needed?
#endif
return(0);
}
Runnable::Runnable()
{ }
Runnable::~Runnable()
{ }
// Is there a thread running in this class
bit8 Runnable::isRunning(void)
{
// Don't need to lock a simple assignment
int temp=ThreadCount_;
return((temp>0)?TRUE:FALSE);
}
// How many threads are running in this class
int Runnable::getThreadCount(void)
{
// Don't need to lock a simple assignment
int temp=ThreadCount_;
return(temp);
}
#undef THREADFAC_CODE

View file

@ -0,0 +1,124 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//
// Platform independent thread creation (Win32 & POSIX)
//
#ifndef THREADFAC_HEADER
#define THREADFAC_HEADER
#ifdef _WIN32
#include <process.h>
#endif
#include "wstypes.h"
#include <stdlib.h>
#ifdef _WIN32
#include <wtypes.h>
#else // UNIX
#include <pthread.h>
#endif
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#include "critsec.h"
#ifdef THREADFAC_CODE
// This is the fake thread entry point for functions
#ifdef _WIN32
static unsigned __stdcall threadFuncLauncher(void *temp);
#else // UNIX
static void *threadFuncLauncher(void *temp);
#endif
// Fake entry point for classes
#ifdef _WIN32
static unsigned __stdcall threadClassLauncher(void *temp);
#else // UNIX
static void *threadClassLauncher(void *temp);
#endif
#endif
// Forward definition of base class for threaded classes
class Runnable;
//
// Call the static method startThread to begin a new thread.
//
class ThreadFactory
{
public:
static bit8 startThread(void (*start_func)(void *), void *data);
static bit8 startThread(Runnable &runable, void *data, bit8 destroy=FALSE);
};
//
// Base class for when you want a thread to execute inside a class
// instead of a function.
//
class Runnable
{
public:
Runnable();
virtual ~Runnable();
// ThreadFactory needs to be able to access the private
// IsRunning_ field.
friend class ThreadFactory;
// So do the threadClassLaunchers
#ifdef _WIN32
friend static unsigned __stdcall threadClassLauncher(void *temp);
#else // UNIX
friend void *threadClassLauncher(void *temp);
#endif
virtual void run(void *data)=0; // Thread entry point
void startThread(void *data,bit8 destroy=FALSE) // nice way to start a thread
{
ThreadFactory::startThread(*this,data,destroy);
};
// Is there a thread running in this class?
static bit8 isRunning(void);
// Get the count of threads running inside this class
static int getThreadCount();
private:
static int ThreadCount_;
static CritSec CritSec_; // to protect ThreadCount_
};
#endif

View file

@ -0,0 +1,62 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//
// threadsafe.h
//
// If you include this file and call non-threadsafe functions, it'll
// prevent your program from compiling. It's meant to be a slap
// on the wrist in case you forget and call an unsafe function
// from a threadsafe program.
//
//
// Reminder to self - use sigwait, not signal handlers!
//
#ifdef _REENTRANT
#ifndef THREADSAFE_HEADER
#define THREADSAFE_HEADER
#define strtok ("strtok() is not MT-SAFE!")
#define ascctime ("asctime() is not MT-SAFE!")
// Can't just do ctime, as Windows' objidl.h uses it as a FILETIME thingie
#define ctime(x) ("ctime() is not MT-SAFE!")
#define gmtime ("gmtime() is not MT-SAFE!")
#define localtime ("localtime() is not MT-SAFE!")
#define tzset ("tzset() is not MT-SAFE!")
#define tzsetwall ("tzsetwall() is not MT-SAFE!")
#define readdir ("readdir() is not MT-SAFE!")
#define rand ("rand() is not MT-SAFE!")
#define srand ("srand() is not MT-SAFE!")
#define random ("random() is not MT-SAFE!")
#define srandom ("srandom() is not MT-SAFE!")
#define tmpnam ("tmpnam() is not MT-SAFE!")
#define vfork ("vfork() is not MT-SAFE!")
#define system ("system() is not MT-SAFE!")
#define popen ("popen() is not MT-SAFE!")
#define pclose ("pclose() is not MT-SAFE!")
#define ctermid ("ctermid() is not MT-SAFE!")
#define getlogin ("getlogin() is not MT-SAFE!");
#endif // THREADSAFE_HEADER
#endif // _REENTRANT

View file

@ -0,0 +1,71 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wlib/xtime.h"
#include "timezone.h"
void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset) {
timezone_str = "Unknown Timezone";
timezone_offset = 0;
#ifdef _WINDOWS
struct _timeb wintime;
_ftime(&wintime);
if (wintime.dstflag) {
// Daylight savings time
if (_daylight) {
timezone_str = _tzname[1];
}
} else {
timezone_str = _tzname[0];
}
timezone_offset = wintime.timezone * 60; // its in minutes...
#endif
#ifndef _WINDOWS
struct timeval unixtime;
struct timezone unixtzone;
gettimeofday(&unixtime,&unixtzone);
struct tm unixtm;
localtime_r(&unixtime.tv_sec, &unixtm);
if (unixtm.tm_isdst) {
// Daylight savings time
if (daylight) timezone_str = tzname[1];
timezone_offset = altzone;
} else {
timezone_str = tzname[0];
timezone_offset = timezone;
}
#endif
}
const char * TimezoneString(void) {
const char *timezone_str;
int timezone_offset;
GetTimezoneInfo(timezone_str, timezone_offset);
return timezone_str;
}
int TimezoneOffset(void) {
const char *timezone_str;
int timezone_offset;
GetTimezoneInfo(timezone_str, timezone_offset);
return timezone_offset;
}

View file

@ -0,0 +1,41 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
timezone Matthew D. Campbell
This is just a couple of convenience functions for determining what timezone
we are now in. It even accounts for daylight savings! One caveat is that it
only tells you info about what the daylight savings info is now, not 5 minutes
from now, not 2 hours ago. Oh well.
\****************************************************************************/
#ifndef _TIMEZONE_H_
#define _TIMEZONE_H_
// Just fill in both the timezone description and its offset from GMT
void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset);
// Returns the description of the current timezone (daylight savings included)
const char * TimezoneString(void);
// Returns the offset from GMT of the current timezone
int TimezoneOffset(void);
#endif // _TIMEZONE_H_

View file

@ -0,0 +1,61 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef USTRING_HEADER
#define USTRING_HEADER
#include <stdlib.h>
#include <stdio.h>
#include <iostream.h>
#include <string>
// Windows headers have a tendency to redefine IN
#ifdef IN
#undef IN
#endif
#define IN const
#define MAX_BYTES_PER_CHAR 1
template <class charT>
class UstringT : public basic_string<charT, string_char_traits<charT> >
{
public:
explicit UstringT(int max_charlength) {
set_max_bytelength(max_charlength*MAX_BYTES_PER_CHAR);
}
UstringT() { max_bytelength=4000; }
size_t get_max_bytelength(void) { return(max_bytelength); }
void set_max_bytelength(size_t max) { max_bytelength=max; }
bool operator==(const UstringT<charT> &other)
{
const basic_string<charT, string_char_traits<charT> > *other_basic=&other;
const basic_string<charT, string_char_traits<charT> > *this_basic=this;
return((*other_basic)==(*this_basic));
}
private:
size_t max_bytelength;
};
typedef UstringT<char> Ustring;
#endif

View file

@ -0,0 +1,190 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "wdebug.h"
#include "streamer.h"
#include "odevice.h"
static MsgManager *msg_manager=NULL;
static int debug_enabled=0;
static ostream *debug_ostream=NULL;
static Streamer debug_streamer;
static int info_enabled=0;
static ostream *info_ostream=NULL;
static Streamer info_streamer;
static int warn_enabled=0;
static ostream *warn_ostream=NULL;
static Streamer warn_streamer;
static int error_enabled=0;
static ostream *error_ostream=NULL;
static Streamer error_streamer;
// Don't dare touch this semaphore in application code!
#ifdef USE_DEBUG_SEM
Sem4 DebugLibSemaphore;
#else
CritSec DebugLibSemaphore;
#endif
int MsgManager::setAllStreams(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
debug_streamer.setOutputDevice(device);
delete(debug_ostream);
debug_ostream=new ostream(&debug_streamer);
info_streamer.setOutputDevice(device);
delete(info_ostream);
info_ostream=new ostream(&info_streamer);
warn_streamer.setOutputDevice(device);
delete(warn_ostream);
warn_ostream=new ostream(&warn_streamer);
error_streamer.setOutputDevice(device);
delete(error_ostream);
error_ostream=new ostream(&error_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::ReplaceAllStreams(FileD * output_device, IN char *device_filename, IN char *copy_filename)
{
DebugLibSemaphore.Wait();
delete(debug_ostream);
delete(info_ostream);
delete(warn_ostream);
delete(error_ostream);
if (output_device != NULL)
{
delete(output_device);
output_device = NULL;
}
rename(device_filename, copy_filename);
// FileD new_device(device_filename);
output_device = new FileD(device_filename);
debug_streamer.setOutputDevice(output_device);
debug_ostream = new ostream(&debug_streamer);
info_streamer.setOutputDevice(output_device);
info_ostream=new ostream(&info_streamer);
warn_streamer.setOutputDevice(output_device);
warn_ostream = new ostream(&warn_streamer);
error_streamer.setOutputDevice(output_device);
error_ostream = new ostream(&error_streamer);
DebugLibSemaphore.Post();
return(0);
}
int MsgManager::setDebugStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
debug_streamer.setOutputDevice(device);
delete(debug_ostream);
debug_ostream=new ostream(&debug_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::setInfoStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
info_streamer.setOutputDevice(device);
delete(info_ostream);
info_ostream=new ostream(&info_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::setWarnStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
warn_streamer.setOutputDevice(device);
delete(warn_ostream);
warn_ostream=new ostream(&warn_streamer);
DEBUGUNLOCK;
return(0);
}
int MsgManager::setErrorStream(OutputDevice *device)
{
if (device==NULL)
return(1);
DEBUGLOCK;
error_streamer.setOutputDevice(device);
delete(error_ostream);
error_ostream=new ostream(&error_streamer);
DEBUGUNLOCK;
return(0);
}
ostream *MsgManager::debugStream(void)
{
return(debug_ostream);
}
ostream *MsgManager::infoStream(void)
{
return(info_ostream);
}
ostream *MsgManager::warnStream(void)
{
return(warn_ostream);
}
ostream *MsgManager::errorStream(void)
{
return(error_ostream);
}

View file

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

View file

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

View file

@ -0,0 +1,93 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Carpenter (The RedAlert ladder creator)
File Name : main.cpp
Author : Neal Kettler
Start Date : June 1, 1997
Last Update : June 17, 1997
\****************************************************************************/
#ifndef WSTRING_HEADER
#define WSTRING_HEADER
#include <stdio.h>
#include <stdlib.h>
#include "wstypes.h"
class Wstring
{
public:
Wstring();
Wstring(IN Wstring &other);
Wstring(IN char *string);
~Wstring();
void clear(void);
bit8 cat(IN char *string);
bit8 cat(uint32 size,IN char *string);
bit8 cat(IN Wstring &string);
void cellCopy(OUT char *dest, uint32 len);
char remove(sint32 pos, sint32 count);
bit8 removeChar(char c);
void removeSpaces(void);
char *get(void) RO;
char get(uint32 index) RO;
uint32 length(void) RO;
bit8 insert(char c, uint32 pos);
bit8 insert(char *instring, uint32 pos);
bit8 beautifyNumber();
bit8 replace(IN char *replaceThis,IN char *withThis);
char set(IN char *str);
char set(uint32 size,IN char *str);
bit8 set(char c, uint32 index);
char setFormatted(IN char *str, ...); // Added by Joe Howes
void setSize(sint32 bytes); // create an empty string
void toLower(void);
void toUpper(void);
bit8 truncate(uint32 len);
bit8 truncate(char c); // trunc after char c
sint32 getToken(int offset,char *delim,Wstring &out) RO;
sint32 getLine(int offset, Wstring &out);
void strgrow(int length);
bit8 operator==(IN char *other) RO;
bit8 operator==(IN Wstring &other) RO;
bit8 operator!=(IN char *other) RO;
bit8 operator!=(IN Wstring &other) RO;
Wstring &operator=(IN char *other);
Wstring &operator=(IN Wstring &other);
Wstring &operator+=(IN char *other);
Wstring &operator+=(IN Wstring &other);
Wstring operator+(IN char *other);
Wstring operator+(IN Wstring &other);
bool operator<(IN Wstring &other) RO;
private:
char *str; // Pointer to allocated string.
int strsize; // allocated data length
};
#endif

View file

@ -0,0 +1,118 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name: Carpenter (The RedAlert ladder creator)
File Name : wstypes.h
Author : Neal Kettler
Start Date : June 3, 1997
Last Update : June 17, 1997
Standard type definitions for the sake of portability and readability.
\***************************************************************************/
#ifndef WSTYPES_HEADER
#define WSTYPES_HEADER
#ifdef _REENTRANT // reentrant = threaded
// Headers with non threadsafe libs need to come before my hacky
// threadsafe.h otherwise they won't compile
#include <time.h>
#ifndef _WINDOWS
#define _POSIX_C_SOURCE 199506L
#define _POSIX_PTHREAD_SEMANTICS
#define __EXTENSIONS__
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef _WINDOWS
#include <unistd.h>
#include <sys/time.h>
#include <dirent.h>
#else
#include <time.h>
#include <sys/timeb.h>
#endif
#include "threadsafe.h" // enforce threadsafe-only calls
#endif
#define adelete(X) (delete[](X))
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif
#ifndef MAX
#define MAX(x,y) (((x)>(y))?(x):(y))
#endif
#ifndef NULL
#define NULL 0
#endif
//These are used for readability purposes mostly, when a method takes a
// pointer or reference these help specify what will happen to the data
// that is sent in.
#ifdef IN
#undef IN
#endif
#define IN const
#define OUT
#define INOUT
#define _IN_ const
// Used to declare a function or method as const or Read Only
#define RO const
typedef char bit8;
typedef char sint8;
typedef unsigned char uint8;
typedef signed short int sint16;
typedef unsigned short int uint16;
typedef signed int sint32;
typedef unsigned int uint32;
typedef float float32;
typedef double float64;
#define MAX_BIT8 0x1
#define MAX_UINT32 0xFFFFFFFF
#define MAX_UINT16 0xFFFF
#define MAX_UINT8 0xFF
#define MAX_SINT32 0x7FFFFFFF
#define MAX_SINT16 0x7FFF
#define MAX_SINT8 0x7F
#ifdef _WINDOWS
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#endif

View file

@ -0,0 +1,802 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
wtime Neal Kettler
\****************************************************************************/
#include <ctype.h>
#include "wtime.h"
static char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday",
"Friday","Saturday"};
static char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
"Aug","Sep","Oct","Nov","Dec"};
static char *FULLMONTHS[]={"January","February","March","April","May","June",
"July","August","September","October","November","December"};
// MDC: Windows doesn't provide a localtime_r, so make our own...
#ifdef _WINDOWS
#ifdef _REENTRANT
#include "critsec.h"
static CritSec localtime_critsec;
#undef localtime
_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;
}

View file

@ -0,0 +1,128 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
wtime Neal Kettler
\****************************************************************************/
#ifndef WTIME_HEADER
#define WTIME_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#ifndef _WINDOWS
#include <unistd.h>
#include <netinet/in.h>
#include <sys/time.h>
#else
#include <sys/timeb.h>
#include <winsock.h>
#endif
#include <time.h>
#include <string.h>
#include "wstypes.h"
class Wtime
{
public:
enum
{
POSITIVE=0,
NEGATIVE=1
};
Wtime(); // init to system time
Wtime( Wtime &other );
Wtime( uint32 other );
~Wtime();
void Update(); // Update members sec & usec to system time
void PrintTime(FILE *out) const;
void PrintTime(char *out) const;
void PrintDate(FILE *out) const;
void PrintDate(char *out) const;
uint32 GetSec(void) const; // Get member variable 'sec'
uint32 GetUsec(void) const; // Get member variable 'usec'
void SetSec(uint32 newsec);
void SetUsec(uint32 newusec);
void Set(uint32 newsec,uint32 newusec);
bit8 ParseDate(char *in);
bit8 FormatTime(char *out, char *format);
struct timeval *GetTimeval(void);
void GetTimevalMT(struct timeval &tv);
uint32 GetSecond(void) const; // Second (0- 60) (60 is for a leap second)
uint32 GetMinute(void) const; // Minute (0 - 59)
uint32 GetHour(void) const; // Hour (0-23)
uint32 GetMDay(void) const; // Day of Month (1-31)
uint32 GetWDay(void) const; // Day of Week (1-7)
uint32 GetYDay(void) const; // Day of Year (1-366)
uint32 GetMonth(void) const; // Month (1-12)
uint32 GetYWeek(void) const; // Week of Year (1-53)
uint32 GetYear(void) const; // Year (e.g. 1997)
bit8 GetSign(void) const; // 0 = pos 1 = neg
int Compare(const Wtime &other) const;
// comparisons
bit8 operator == ( const Wtime &other ) const;
bit8 operator != ( const Wtime &other ) const;
bit8 operator < ( const Wtime &other ) const;
bit8 operator > ( const Wtime &other ) const;
bit8 operator <= ( const Wtime &other ) const;
bit8 operator >= ( const Wtime &other ) const;
// assignments
Wtime &operator = (const Wtime &other);
Wtime &operator = (const uint32 other);
// math
// signed
void SignedAdd(const Wtime &other);
void SignedSubtract(const Wtime &other);
// unsigned
Wtime &operator += (const Wtime &other);
Wtime &operator -= (const Wtime &other);
Wtime operator + (Wtime &other);
Wtime operator - (Wtime &other);
Wtime &operator += (const uint32 other);
Wtime &operator -= (const uint32 other);
Wtime operator + (uint32 other);
Wtime operator - (uint32 other);
protected:
uint32 sec; // seconds since Jan 1, 1970
uint32 usec; // microseconds (millionths of a second)
bit8 sign; // for time differences 0 = pos 1 = neg
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,146 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
xtime Neal Kettler
This is version 2 of the Wtime library (now xtime). It now supports
time storage from the year 0 to well after the sun
will have gone supernova (OK, OK I admit it'll break in the
year 5 Million.)
The call to update the current time will break in 2038.
Hopefully by then somebody will replace the lame time()
function :-)
\****************************************************************************/
#ifndef XTIME_HEADER
#define XTIME_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#ifndef _WINDOWS
#include <unistd.h>
#include <netinet/in.h>
#include <sys/time.h>
#else
#include <sys/timeb.h>
#include <winsock.h>
#endif
#include <time.h>
#include <string.h>
#include "wstypes.h"
class Xtime
{
public:
Xtime(); // init to system time
Xtime( Xtime &other );
Xtime( time_t other ); // 1970-2038
~Xtime();
void addSeconds(sint32 seconds);
bit8 getTime(int &month, int &mday, int &year, int &hour, int &minute,
int &second) const;
bit8 setTime(int month, int mday, int year, int hour, int minute,
int second);
void update(); // Update members sec & usec to system time
// This will break after 2038
/********
void PrintTime(FILE *out) const;
void PrintTime(char *out) const;
void PrintDate(FILE *out) const;
void PrintDate(char *out) const;
**********/
sint32 getDay(void) const; // Get days since year 0
sint32 getMsec(void) const; // Get milliseconds into the day
void setDay(sint32 day);
void setMsec(sint32 msec);
void set(sint32 newday, sint32 newmsec);
bit8 ParseDate(char *in);
bit8 FormatTime(char *out, char *format);
bit8 getTimeval(struct timeval &tv);
// All of these may return -1 if the time is invalid
int getSecond(void) const; // Second (0-60) (60 is for a leap second)
int getMinute(void) const; // Minute (0-59)
int getHour(void) const; // Hour (0-23)
int getMDay(void) const; // Day of Month (1-31)
int getWDay(void) const; // Day of Week (1-7)
int getYDay(void) const; // Day of Year (1-366) (366 = leap yr)
int getMonth(void) const; // Month (1-12)
int getYWeek(void) const; // Week of Year (1-53)
int getYear(void) const; // Year (e.g. 1997)
// Modify the time components. Return FALSE if fail
bit8 setSecond(sint32 sec);
bit8 setMinute(sint32 min);
bit8 setHour(sint32 hour);
bit8 setYear(sint32 year);
bit8 setMonth(sint32 month);
bit8 setMDay(sint32 mday);
void normalize(void); // move msec overflows to the day
// Compare two times
int compare(const Xtime &other) const;
// comparisons
bit8 operator == ( const Xtime &other ) const;
bit8 operator != ( const Xtime &other ) const;
bit8 operator < ( const Xtime &other ) const;
bit8 operator > ( const Xtime &other ) const;
bit8 operator <= ( const Xtime &other ) const;
bit8 operator >= ( const Xtime &other ) const;
// assignments
Xtime &operator = (const Xtime &other);
Xtime &operator = (const time_t other);
// signed
Xtime &operator += (const Xtime &other);
Xtime &operator -= (const Xtime &other);
Xtime operator + (Xtime &other);
Xtime operator - (Xtime &other);
Xtime &operator += (const time_t other);
Xtime &operator -= (const time_t other);
Xtime operator + (time_t other);
Xtime operator - (time_t other);
protected:
sint32 day_; // days since Jan 1, 0
sint32 msec_; // milliseconds (thousandths of a sec)
};
#endif