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,235 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
/////////ArchiveFile.cpp ///////////////////////
// Bryan Cleveland, August 2002
////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/File.h"
#include "Common/PerfTimer.h"
// checks to see if str matches searchString. Search string is done in the
// using * and ? as wildcards. * is used to denote any number of characters,
// and ? is used to denote a single wildcard character.
static Bool SearchStringMatches(AsciiString str, AsciiString searchString)
{
if (str.getLength() == 0) {
if (searchString.getLength() == 0) {
return TRUE;
}
return FALSE;
}
if (searchString.getLength() == 0) {
return FALSE;
}
const char *c1 = str.str();
const char *c2 = searchString.str();
while ((*c1 == *c2) || (*c2 == '?') || (*c2 == '*')) {
if ((*c1 == *c2) || (*c2 == '?')) {
++c1;
++c2;
} else if (*c2 == '*') {
++c2;
if (*c2 == 0) {
return TRUE;
}
while (*c1 != 0) {
if (SearchStringMatches(AsciiString(c1), AsciiString(c2))) {
return TRUE;
}
++c1;
}
}
if (*c1 == 0) {
if (*c2 == 0) {
return TRUE;
}
return FALSE;
}
if (*c2 == 0) {
return FALSE;
}
}
return FALSE;
}
ArchiveFile::~ArchiveFile()
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
}
ArchiveFile::ArchiveFile()
{
m_rootDirectory.clear();
}
void ArchiveFile::addFile(const AsciiString& path, const ArchivedFileInfo *fileInfo)
{
AsciiString temp;
temp = path;
temp.toLower();
AsciiString token;
AsciiString debugpath;
DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
temp.nextToken(&token, "\\/");
while (token.getLength() > 0) {
if (dirInfo->m_directories.find(token) == dirInfo->m_directories.end())
{
dirInfo->m_directories[token].clear();
dirInfo->m_directories[token].m_directoryName = token;
}
debugpath.concat(token);
debugpath.concat('\\');
dirInfo = &(dirInfo->m_directories[token]);
temp.nextToken(&token, "\\/");
}
dirInfo->m_files[fileInfo->m_filename] = *fileInfo;
//path.concat(fileInfo->m_filename);
}
void ArchiveFile::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
AsciiString searchDir;
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
searchDir = originalDirectory;
searchDir.toLower();
AsciiString token;
searchDir.nextToken(&token, "\\/");
while (token.getLength() > 0) {
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
if (it != dirInfo->m_directories.end())
{
dirInfo = &it->second;
}
else
{
// if the directory doesn't exist, then there aren't any files to be had.
return;
}
searchDir.nextToken(&token, "\\/");
}
getFileListInDirectory(dirInfo, originalDirectory, searchName, filenameList, searchSubdirectories);
}
void ArchiveFile::getFileListInDirectory(const DetailedArchivedDirectoryInfo *dirInfo, const AsciiString& currentDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
DetailedArchivedDirectoryInfoMap::const_iterator diriter = dirInfo->m_directories.begin();
while (diriter != dirInfo->m_directories.end()) {
const DetailedArchivedDirectoryInfo *tempDirInfo = &(diriter->second);
AsciiString tempdirname;
tempdirname = currentDirectory;
if ((tempdirname.getLength() > 0) && (!tempdirname.endsWith("\\"))) {
tempdirname.concat('\\');
}
tempdirname.concat(tempDirInfo->m_directoryName);
getFileListInDirectory(tempDirInfo, tempdirname, searchName, filenameList, searchSubdirectories);
diriter++;
}
ArchivedFileInfoMap::const_iterator fileiter = dirInfo->m_files.begin();
while (fileiter != dirInfo->m_files.end()) {
if (SearchStringMatches(fileiter->second.m_filename, searchName)) {
AsciiString tempfilename;
tempfilename = currentDirectory;
if ((tempfilename.getLength() > 0) && (!tempfilename.endsWith("\\"))) {
tempfilename.concat('\\');
}
tempfilename.concat(fileiter->second.m_filename);
if (filenameList.find(tempfilename) == filenameList.end()) {
// only insert into the list if its not already in there.
filenameList.insert(tempfilename);
}
}
fileiter++;
}
}
void ArchiveFile::attachFile(File *file)
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
m_file = file;
}
const ArchivedFileInfo * ArchiveFile::getArchivedFileInfo(const AsciiString& filename) const
{
AsciiString path;
path = filename;
path.toLower();
AsciiString token;
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
path.nextToken(&token, "\\/");
while ((token.find('.') == NULL) || (path.find('.') != NULL)) {
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
if (it != dirInfo->m_directories.end())
{
dirInfo = &it->second;
}
else
{
return NULL;
}
path.nextToken(&token, "\\/");
}
ArchivedFileInfoMap::const_iterator it = dirInfo->m_files.find(token);
if (it != dirInfo->m_files.end())
{
return &it->second;
}
else
{
return NULL;
}
}

View file

@ -0,0 +1,327 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Game Engine Common
//
// File name: ArchiveFileSystem.cpp
//
// Created: 11/26/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/AsciiString.h"
#include "Common/PerfTimer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
ArchiveFileSystem *TheArchiveFileSystem = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//------------------------------------------------------
// ArchivedFileInfo
//------------------------------------------------------
ArchiveFileSystem::ArchiveFileSystem()
{
}
ArchiveFileSystem::~ArchiveFileSystem()
{
ArchiveFileMap::iterator iter = m_archiveFileMap.begin();
while (iter != m_archiveFileMap.end()) {
ArchiveFile *file = iter->second;
if (file != NULL) {
delete file;
file = NULL;
}
iter++;
}
}
void ArchiveFileSystem::loadIntoDirectoryTree(const ArchiveFile *archiveFile, const AsciiString& archiveFilename, Bool overwrite)
{
FilenameList filenameList;
archiveFile->getFileListInDirectory(AsciiString(""), AsciiString(""), AsciiString("*"), filenameList, TRUE);
FilenameListIter it = filenameList.begin();
while (it != filenameList.end()) {
// add this filename to the directory tree.
AsciiString path = *it;
path.toLower();
AsciiString token;
AsciiString debugpath;
ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
Bool infoInPath;
infoInPath = path.nextToken(&token, "\\/");
while (infoInPath && (!token.find('.') || path.find('.'))) {
ArchivedDirectoryInfoMap::iterator tempiter = dirInfo->m_directories.find(token);
if (tempiter == dirInfo->m_directories.end())
{
dirInfo->m_directories[token].clear();
dirInfo->m_directories[token].m_directoryName = token;
}
dirInfo = &(dirInfo->m_directories[token]);
debugpath.concat(token);
debugpath.concat('\\');
infoInPath = path.nextToken(&token, "\\/");
}
// token is the filename, and dirInfo is the directory that this file is in.
if (dirInfo->m_files.find(token) == dirInfo->m_files.end() || overwrite) {
AsciiString path2;
path2 = debugpath;
path2.concat(token);
// DEBUG_LOG(("ArchiveFileSystem::loadIntoDirectoryTree - adding file %s, archived in %s\n", path2.str(), archiveFilename.str()));
dirInfo->m_files[token] = archiveFilename;
}
it++;
}
}
void ArchiveFileSystem::loadMods() {
if (TheGlobalData->m_modBIG.isNotEmpty())
{
ArchiveFile *archiveFile = openArchiveFile(TheGlobalData->m_modBIG.str());
if (archiveFile != NULL) {
DEBUG_LOG(("ArchiveFileSystem::loadMods - loading %s into the directory tree.\n", TheGlobalData->m_modBIG.str()));
loadIntoDirectoryTree(archiveFile, TheGlobalData->m_modBIG, TRUE);
m_archiveFileMap[TheGlobalData->m_modBIG] = archiveFile;
DEBUG_LOG(("ArchiveFileSystem::loadMods - %s inserted into the archive file map.\n", TheGlobalData->m_modBIG.str()));
}
else
{
DEBUG_LOG(("ArchiveFileSystem::loadMods - could not openArchiveFile(%s)\n", TheGlobalData->m_modBIG.str()));
}
}
if (TheGlobalData->m_modDir.isNotEmpty())
{
#ifdef DEBUG_LOGGING
Bool ret =
#endif
loadBigFilesFromDirectory(TheGlobalData->m_modDir, "*.big", TRUE);
DEBUG_ASSERTLOG(ret, ("loadBigFilesFromDirectory(%s) returned FALSE!\n", TheGlobalData->m_modDir.str()));
}
}
Bool ArchiveFileSystem::doesFileExist(const Char *filename) const
{
AsciiString path = filename;
path.toLower();
AsciiString token;
const ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
path.nextToken(&token, "\\/");
while (!token.find('.') || path.find('.'))
{
ArchivedDirectoryInfoMap::const_iterator tempiter = dirInfo->m_directories.find(token);
if (tempiter != dirInfo->m_directories.end())
{
dirInfo = &tempiter->second;
path.nextToken(&token, "\\/");
}
else
{
// the directory doesn't exist, so return false
return FALSE;
}
}
// token is the filename, and dirInfo is the directory that this file is in.
if (dirInfo->m_files.find(token) == dirInfo->m_files.end()) {
return FALSE;
}
return TRUE;
}
File * ArchiveFileSystem::openFile(const Char *filename, Int access /* = 0 */)
{
AsciiString archiveFilename;
archiveFilename = getArchiveFilenameForFile(AsciiString(filename));
if (archiveFilename.getLength() == 0) {
return NULL;
}
return m_archiveFileMap[archiveFilename]->openFile(filename, access);
}
Bool ArchiveFileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const
{
if (fileInfo == NULL) {
return FALSE;
}
if (filename.getLength() <= 0) {
return FALSE;
}
AsciiString archiveFilename = getArchiveFilenameForFile(filename);
ArchiveFileMap::const_iterator it = m_archiveFileMap.find(archiveFilename);
if (it != m_archiveFileMap.end())
{
return it->second->getFileInfo(filename, fileInfo);
}
else
{
return FALSE;
}
}
AsciiString ArchiveFileSystem::getArchiveFilenameForFile(const AsciiString& filename) const
{
AsciiString path;
path = filename;
path.toLower();
AsciiString token;
AsciiString debugpath;
const ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
path.nextToken(&token, "\\/");
while (!token.find('.') || path.find('.')) {
ArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
if (it != dirInfo->m_directories.end())
{
dirInfo = &it->second;
}
else
{
// the directory doesn't exist, so return NULL
// dump the directories;
//DEBUG_LOG(("directory %s not found in %s in archive file system\n", token.str(), debugpath.str()));
//DEBUG_LOG(("directories in %s in archive file system are:\n", debugpath.str()));
//ArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.begin();
//while (it != dirInfo->m_directories.end()) {
// DEBUG_LOG(("\t%s\n", it->second.m_directoryName.str()));
// it++;
//}
//DEBUG_LOG(("end of directory list.\n"));
return AsciiString::TheEmptyString;
}
debugpath.concat(token);
debugpath.concat('\\');
path.nextToken(&token, "\\/");
}
ArchivedFileLocationMap::const_iterator it = dirInfo->m_files.find(token);
if (it != dirInfo->m_files.end())
{
return it->second;
}
else
{
return AsciiString::TheEmptyString;
}
}
void ArchiveFileSystem::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
ArchiveFileMap::const_iterator it = m_archiveFileMap.begin();
while (it != m_archiveFileMap.end()) {
it->second->getFileListInDirectory(currentDirectory, originalDirectory, searchName, filenameList, searchSubdirectories);
it++;
}
}

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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: AsciiString.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: AsciiString.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: General-purpose string classes
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CriticalSection.h"
// -----------------------------------------------------
/*static*/ AsciiString AsciiString::TheEmptyString;
//-----------------------------------------------------------------------------
inline char* skipSeps(char* p, const char* seps)
{
while (*p && strchr(seps, *p) != NULL)
++p;
return p;
}
//-----------------------------------------------------------------------------
inline char* skipNonSeps(char* p, const char* seps)
{
while (*p && strchr(seps, *p) == NULL)
++p;
return p;
}
//-----------------------------------------------------------------------------
inline char* skipWhitespace(char* p)
{
while (*p && isspace(*p))
++p;
return p;
}
//-----------------------------------------------------------------------------
inline char* skipNonWhitespace(char* p)
{
while (*p && !isspace(*p))
++p;
return p;
}
// -----------------------------------------------------
AsciiString::AsciiString(const AsciiString& stringSrc) : m_data(stringSrc.m_data)
{
ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
if (m_data)
++m_data->m_refCount;
validate();
}
// -----------------------------------------------------
#ifdef _DEBUG
void AsciiString::validate() const
{
if (!m_data) return;
DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
// DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated < 1024, ("m_numCharsAllocated suspiciously large"));
DEBUG_ASSERTCRASH(strlen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long (%d) for storage",strlen(m_data->peek())+1));
}
#endif
// -----------------------------------------------------
void AsciiString::debugIgnoreLeaks()
{
#ifdef MEMORYPOOL_DEBUG
if (m_data)
{
TheDynamicMemoryAllocator->debugIgnoreLeaksForThisBlock(m_data);
}
else
{
DEBUG_LOG(("cannot ignore the leak (no data)\n"));
}
#endif
}
// -----------------------------------------------------
void AsciiString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const char* strToCopy, const char* strToCat)
{
validate();
if (m_data &&
m_data->m_refCount == 1 &&
m_data->m_numCharsAllocated >= numCharsNeeded)
{
// no buffer manhandling is needed (it's already large enough, and unique to us)
if (strToCopy)
strcpy(m_data->peek(), strToCopy);
if (strToCat)
strcat(m_data->peek(), strToCat);
return;
}
int minBytes = sizeof(AsciiStringData) + numCharsNeeded*sizeof(char);
if (minBytes > MAX_LEN)
throw ERROR_OUT_OF_MEMORY;
int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
AsciiStringData* newData = (AsciiStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_AsciiString::ensureUniqueBufferOfSize");
newData->m_refCount = 1;
newData->m_numCharsAllocated = (actualBytes - sizeof(AsciiStringData))/sizeof(char);
#if defined(_DEBUG) || defined(_INTERNAL)
newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
#endif
if (m_data && preserveData)
strcpy(newData->peek(), m_data->peek());
else
newData->peek()[0] = 0;
// do these BEFORE releasing the old buffer, so that self-copies
// or self-cats will work correctly.
if (strToCopy)
strcpy(newData->peek(), strToCopy);
if (strToCat)
strcat(newData->peek(), strToCat);
releaseBuffer();
m_data = newData;
validate();
}
// -----------------------------------------------------
void AsciiString::releaseBuffer()
{
ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
validate();
if (m_data)
{
if (--m_data->m_refCount == 0)
{
TheDynamicMemoryAllocator->freeBytes(m_data);
}
m_data = 0;
}
validate();
}
// -----------------------------------------------------
AsciiString::AsciiString(const char* s) : m_data(0)
{
//DEBUG_ASSERTCRASH(isMemoryManagerOfficiallyInited(), ("Initializing AsciiStrings prior to main (ie, as static vars) can cause memory leak reporting problems. Are you sure you want to do this?\n"));
int len = (s)?strlen(s):0;
if (len)
{
ensureUniqueBufferOfSize(len + 1, false, s, NULL);
}
validate();
}
// -----------------------------------------------------
void AsciiString::set(const AsciiString& stringSrc)
{
ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
validate();
if (&stringSrc != this)
{
releaseBuffer();
m_data = stringSrc.m_data;
if (m_data)
++m_data->m_refCount;
}
validate();
}
// -----------------------------------------------------
void AsciiString::set(const char* s)
{
validate();
if (!m_data || s != peek())
{
int len = s ? strlen(s) : 0;
if (len)
{
ensureUniqueBufferOfSize(len + 1, false, s, NULL);
}
else
{
releaseBuffer();
}
}
validate();
}
// -----------------------------------------------------
char* AsciiString::getBufferForRead(Int len)
{
validate();
DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
validate();
return peek();
}
// -----------------------------------------------------
void AsciiString::translate(const UnicodeString& stringSrc)
{
validate();
/// @todo srj put in a real translation here; this will only work for 7-bit ascii
clear();
Int len = stringSrc.getLength();
for (Int i = 0; i < len; i++)
concat((char)stringSrc.getCharAt(i));
validate();
}
// -----------------------------------------------------
void AsciiString::concat(const char* s)
{
validate();
int addlen = strlen(s);
if (addlen == 0)
return; // my, that was easy
if (m_data)
{
ensureUniqueBufferOfSize(getLength() + addlen + 1, true, NULL, s);
}
else
{
set(s);
}
validate();
}
// -----------------------------------------------------
void AsciiString::trim()
{
validate();
if (m_data)
{
char *c = peek();
// Strip leading white space from the string.
c = skipWhitespace(c);
if (c != peek())
{
set(c);
}
if (m_data) // another check, because the previous set() could erase m_data
{
// Clip trailing white space from the string.
int len = strlen(peek());
for (int index = len-1; index >= 0; index--)
{
if (isspace(getCharAt(index)))
{
removeLastChar();
}
else
{
break;
}
}
}
}
validate();
}
// -----------------------------------------------------
void AsciiString::toLower()
{
validate();
if (m_data)
{
char buf[MAX_FORMAT_BUF_LEN];
strcpy(buf, peek());
char *c = buf;
while (c && *c)
{
*c = tolower(*c);
c++;
}
set(buf);
}
validate();
}
// -----------------------------------------------------
void AsciiString::removeLastChar()
{
validate();
if (m_data)
{
int len = strlen(peek());
if (len > 0)
{
ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
peek()[len - 1] = 0;
}
}
validate();
}
// -----------------------------------------------------
void AsciiString::format(AsciiString format, ...)
{
validate();
va_list args;
va_start(args, format);
format_va(format, args);
va_end(args);
validate();
}
// -----------------------------------------------------
void AsciiString::format(const char* format, ...)
{
validate();
va_list args;
va_start(args, format);
format_va(format, args);
va_end(args);
validate();
}
// -----------------------------------------------------
void AsciiString::format_va(const AsciiString& format, va_list args)
{
validate();
char buf[MAX_FORMAT_BUF_LEN];
if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format.str(), args) < 0)
throw ERROR_OUT_OF_MEMORY;
set(buf);
validate();
}
// -----------------------------------------------------
void AsciiString::format_va(const char* format, va_list args)
{
validate();
char buf[MAX_FORMAT_BUF_LEN];
if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format, args) < 0)
throw ERROR_OUT_OF_MEMORY;
set(buf);
validate();
}
// -----------------------------------------------------
Bool AsciiString::startsWith(const char* p) const
{
if (*p == 0)
return true; // everything starts with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strncmp(peek(), p, lenThat) == 0;
}
// -----------------------------------------------------
Bool AsciiString::startsWithNoCase(const char* p) const
{
if (*p == 0)
return true; // everything starts with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strnicmp(peek(), p, lenThat) == 0;
}
// -----------------------------------------------------
Bool AsciiString::endsWith(const char* p) const
{
if (*p == 0)
return true; // everything ends with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0;
}
// -----------------------------------------------------
Bool AsciiString::endsWithNoCase(const char* p) const
{
if (*p == 0)
return true; // everything ends with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0;
}
//-----------------------------------------------------------------------------
Bool AsciiString::isNone() const
{
return m_data && stricmp(peek(), "None") == 0;
}
//-----------------------------------------------------------------------------
Bool AsciiString::nextToken(AsciiString* tok, const char* seps)
{
if (this->isEmpty() || tok == this)
return false;
if (seps == NULL)
seps = " \n\r\t";
char* start = skipSeps(peek(), seps);
char* end = skipNonSeps(start, seps);
if (end > start)
{
Int len = end - start;
char* tmp = tok->getBufferForRead(len + 1);
memcpy(tmp, start, len);
tmp[len] = 0;
this->set(end);
return true;
}
else
{
this->clear();
tok->clear();
return false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,297 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Game Engine Common
//
// File name: CDManager.cpp
//
// Created: 11/26/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CDManager.h"
#include "GameLogic/GameLogic.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
CDManagerInterface* TheCDManager = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// CDDrive::CDDrive
//============================================================================
CDDrive::CDDrive()
: m_disk(CD::UNKNOWN_DISK)
{
m_diskName.clear();
m_drivePath.clear();
}
//============================================================================
// CDDrive::~CDDrive
//============================================================================
CDDrive::~CDDrive()
{
}
//============================================================================
// CDDrive::getPath
//============================================================================
AsciiString CDDrive::getPath( void )
{
return m_drivePath;
}
//============================================================================
// CDDrive::getDiskName
//============================================================================
AsciiString CDDrive::getDiskName( void )
{
return m_diskName;
}
void CDDrive::refreshInfo( void )
{
// map disk names to disk ID
m_disk = CD::UNKNOWN_DISK;
}
//============================================================================
// CDDrive::getDisk
//============================================================================
CD::Disk CDDrive::getDisk( void )
{
return m_disk;
}
//============================================================================
// CDDrive::setPath
//============================================================================
void CDDrive::setPath( const Char *path )
{
m_drivePath = path;
}
//============================================================================
// CDManager::CDManager
//============================================================================
CDManager::CDManager()
{
}
//============================================================================
// CDManager::~CDManager
//============================================================================
CDManager::~CDManager()
{
destroyAllDrives();
}
//============================================================================
// CDManager::init
//============================================================================
void CDManager::init( void )
{
}
//============================================================================
// CDManager::update
//============================================================================
void CDManager::update( void )
{
// Every so often, check to make sure the CD is still in the drive
if ((TheGameLogic->getFrame() % 300) == 299) {
refreshDrives();
}
}
//============================================================================
// CDManager::reset
//============================================================================
void CDManager::reset( void )
{
}
//============================================================================
// CDManager::driveCount
//============================================================================
Int CDManager::driveCount( void )
{
return m_drives.nodeCount();
}
//============================================================================
// CDManager::getDrive
//============================================================================
CDDriveInterface* CDManager::getDrive( Int index )
{
CDDriveInterface *cd = NULL;
LListNode *node = m_drives.getNode( index );
if ( node )
{
cd = (CDDriveInterface*) node->item();
}
return cd;
}
//============================================================================
// CDManager::newDrive
//============================================================================
CDDriveInterface* CDManager::newDrive( const Char *path )
{
CDDrive *drive= (CDDrive*) createDrive();
if ( drive )
{
drive->setPath( path );
drive->m_node.setItem( drive );
m_drives.add( &drive->m_node );
}
return drive;
}
//============================================================================
// CDManager::refreshDrives
//============================================================================
void CDManager::refreshDrives( void )
{
LListNode *node = m_drives.firstNode();
while ( node )
{
CDDriveInterface *drive = (CDDriveInterface *) node->item();
if ( drive )
{
drive->refreshInfo();
}
node = node->next();
}
}
//============================================================================
// CDManager::destroyAllDrives
//============================================================================
void CDManager::destroyAllDrives( void )
{
LListNode *node;
while ( (node = m_drives.firstNode() ) != NULL )
{
node->remove();
CDDriveInterface *drive = (CDDriveInterface *) node->item();
if ( drive )
{
delete drive;
}
}
}

View file

@ -0,0 +1,204 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Compression.cpp /////////////////////////////////////////////////////
// Author: Matthew D. Campbell
//////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Compression.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///// Performance Testing ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
//#define TEST_COMPRESSION
#ifdef TEST_COMPRESSION
#include "GameClient/MapUtil.h"
#include "Common/FileSystem.h"
#include "Common/File.h"
#include "Common/PerfTimer.h"
enum { NUM_TIMES = 1 };
struct CompData
{
public:
Int origSize;
Int compressedSize[COMPRESSION_MAX+1];
};
#define TEST_COMPRESSION_MIN COMPRESSION_BTREE
#define TEST_COMPRESSION_MAX COMPRESSION_MAX
void DoCompressTest( void )
{
Int i;
/*
PerfGather *s_compressGathers[TEST_COMPRESSION_MAX+1];
PerfGather *s_decompressGathers[TEST_COMPRESSION_MAX+1];
for (i = TEST_COMPRESSION_MIN; i < TEST_COMPRESSION_MAX+1; ++i)
{
s_compressGathers[i] = new PerfGather(CompressionManager::getCompressionNameByType((CompressionType)i));
s_decompressGathers[i] = new PerfGather(CompressionManager::getDecompressionNameByType((CompressionType)i));
}
*/
std::map<AsciiString, CompData> s_sizes;
std::map<AsciiString, MapMetaData>::const_iterator it = TheMapCache->find("userdata\\maps\\_usa01\\_usa01.map");
if (it != TheMapCache->end())
{
//if (it->second.m_isOfficial)
//{
//++it;
//continue;
//}
//static Int count = 0;
//if (count++ > 2)
//break;
File *f = TheFileSystem->openFile(it->first.str());
if (f)
{
DEBUG_LOG(("***************************\nTesting '%s'\n\n", it->first.str()));
Int origSize = f->size();
UnsignedByte *buf = (UnsignedByte *)f->readEntireAndClose();
UnsignedByte *uncompressedBuf = NEW UnsignedByte[origSize];
CompData d = s_sizes[it->first];
d.origSize = origSize;
d.compressedSize[COMPRESSION_NONE] = origSize;
for (i=TEST_COMPRESSION_MIN; i<=TEST_COMPRESSION_MAX; ++i)
{
DEBUG_LOG(("=================================================\n"));
DEBUG_LOG(("Compression Test %d\n", i));
Int maxCompressedSize = CompressionManager::getMaxCompressedSize( origSize, (CompressionType)i );
DEBUG_LOG(("Orig size is %d, max compressed size is %d bytes\n", origSize, maxCompressedSize));
UnsignedByte *compressedBuf = NEW UnsignedByte[maxCompressedSize];
memset(compressedBuf, 0, maxCompressedSize);
memset(uncompressedBuf, 0, origSize);
Int compressedLen, decompressedLen;
for (Int j=0; j < NUM_TIMES; ++j)
{
//s_compressGathers[i]->startTimer();
compressedLen = CompressionManager::compressData((CompressionType)i, buf, origSize, compressedBuf, maxCompressedSize);
//s_compressGathers[i]->stopTimer();
//s_decompressGathers[i]->startTimer();
decompressedLen = CompressionManager::decompressData(compressedBuf, compressedLen, uncompressedBuf, origSize);
//s_decompressGathers[i]->stopTimer();
}
d.compressedSize[i] = compressedLen;
DEBUG_LOG(("Compressed len is %d (%g%% of original size)\n", compressedLen, (double)compressedLen/(double)origSize*100.0));
DEBUG_ASSERTCRASH(compressedLen, ("Failed to compress\n"));
DEBUG_LOG(("Decompressed len is %d (%g%% of original size)\n", decompressedLen, (double)decompressedLen/(double)origSize*100.0));
DEBUG_ASSERTCRASH(decompressedLen == origSize, ("orig size does not match compressed+uncompressed output\n"));
if (decompressedLen == origSize)
{
Int ret = memcmp(buf, uncompressedBuf, origSize);
if (ret != 0)
{
DEBUG_CRASH(("orig buffer does not match compressed+uncompressed output - ret was %d\n", ret));
}
}
delete compressedBuf;
compressedBuf = NULL;
}
DEBUG_LOG(("d = %d -> %d\n", d.origSize, d.compressedSize[i]));
s_sizes[it->first] = d;
DEBUG_LOG(("s_sizes[%s] = %d -> %d\n", it->first.str(), s_sizes[it->first].origSize, s_sizes[it->first].compressedSize[i]));
delete[] buf;
buf = NULL;
delete[] uncompressedBuf;
uncompressedBuf = NULL;
}
++it;
}
for (i=TEST_COMPRESSION_MIN; i<=TEST_COMPRESSION_MAX; ++i)
{
Real maxCompression = 1000.0f;
Real minCompression = 0.0f;
Int totalUncompressedBytes = 0;
Int totalCompressedBytes = 0;
for (std::map<AsciiString, CompData>::iterator cd = s_sizes.begin(); cd != s_sizes.end(); ++cd)
{
CompData d = cd->second;
Real ratio = d.compressedSize[i]/(Real)d.origSize;
maxCompression = min(maxCompression, ratio);
minCompression = max(minCompression, ratio);
totalUncompressedBytes += d.origSize;
totalCompressedBytes += d.compressedSize[i];
}
DEBUG_LOG(("***************************************************\n"));
DEBUG_LOG(("Compression method %s:\n", CompressionManager::getCompressionNameByType((CompressionType)i)));
DEBUG_LOG(("%d bytes compressed to %d (%g%%)\n", totalUncompressedBytes, totalCompressedBytes,
totalCompressedBytes/(Real)totalUncompressedBytes*100.0f));
DEBUG_LOG(("Min ratio: %g%%, Max ratio: %g%%\n",
minCompression*100.0f, maxCompression*100.0f));
DEBUG_LOG(("\n"));
}
/*
PerfGather::dumpAll(10000);
PerfGather::resetAll();
CopyFile( "AAAPerfStats.csv", "AAACompressPerfStats.csv", FALSE );
for (i = TEST_COMPRESSION_MIN; i < TEST_COMPRESSION_MAX+1; ++i)
{
delete s_compressGathers[i];
s_compressGathers[i] = NULL;
delete s_decompressGathers[i];
s_decompressGathers[i] = NULL;
}
*/
}
#endif // TEST_COMPRESSION

View file

@ -0,0 +1,233 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: CopyProtection.cpp //////////////////////////////////////////////////
// Author: Matthew D. Campbell
// Taken From: Denzil Long's code in Tiberian Sun, by way of Yuri's Revenge
//////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/CopyProtection.h"
#ifndef DO_COPY_PROTECTION
//#pragma MESSAGE("*********************************************************")
//#pragma MESSAGE("************* COPY PROTECTION IS DISABLED ***************")
//#pragma MESSAGE("*********************************************************")
#else
LPVOID CopyProtect::s_protectedData = NULL;
static const char* const LAUNCHER_GUID =
"150C6462-4E49-4ccf-B073-57579569D994"; // Generals Multiplayer Test Launcher GUID
static const char* const protectGUID =
"6096561D-8A70-48ed-9FF8-18552419E50D"; // Generals Multiplayer Test Protect GUID
/*
static const char* const LAUNCHER_GUID =
"FB327081-64F2-43d8-9B72-503C3B765134"; // Generals Launcher GUID
static const char* const protectGUID =
"7BEB9006-CC19-4aca-913A-C870A88DE01A"; // Generals Protect GUID
*/
#if defined(_DEBUG) || defined(_INTERNAL)
Bool skipProtection(void)
{
//return FALSE;
if (FindWindow("Afx:400000:8:10011:0:fe8e0125", NULL) != NULL) // DevStudio
{
DEBUG_LOG(("DevStudio is running - skipping notifyLauncher()\n"));
return TRUE;
}
//return FALSE;
return TRUE;
}
#endif
// ---------------------------------------------------------------------------
Bool CopyProtect::isLauncherRunning(void)
{
DEBUG_LOG(("COPYPROTECTION - Checking if launcher is running\n"));
HANDLE launcherMutex = CreateMutex(NULL, FALSE, LAUNCHER_GUID);
Bool isRunning = (GetLastError() == ERROR_ALREADY_EXISTS);
if (launcherMutex != NULL)
{
CloseHandle(launcherMutex);
}
DEBUG_LOG(("result was %d\n", (int)isRunning));
#if defined(_DEBUG) || defined(_INTERNAL)
if (skipProtection())
{
DEBUG_LOG(("DevStudio is running - forcing to TRUE\n"));
return TRUE;
}
#endif
return isRunning;
}
// ---------------------------------------------------------------------------
Bool CopyProtect::notifyLauncher(void)
{
DEBUG_LOG(("COPYPROTECTION - Notify launcher\n"));
#if defined(_DEBUG) || defined(_INTERNAL)
if (skipProtection())
{
DEBUG_LOG(("DevStudio is running - skipping notifyLauncher()\n"));
return TRUE;
}
#endif
// Force system to create message queue
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// Signal launcher to send the beef
unsigned long eventTime = (timeGetTime() + 60000);
HANDLE event = NULL;
while (timeGetTime() < eventTime)
{
event = OpenEvent(EVENT_MODIFY_STATE, TRUE, protectGUID);
if (event)
{
break;
}
Sleep(0);
}
if (event)
{
SetEvent(event);
CloseHandle(event);
DEBUG_LOG(("Launcher notified.\n"));
DEBUG_LOG(("Waiting for message from launcher.\n"));
unsigned long endTime = (timeGetTime() + 10000);
while (timeGetTime() <= endTime)
{
if (PeekMessage(&msg, NULL, 0xBEEF, 0xBEEF, PM_REMOVE))
{
// 0xBEEF is in the WM_APP - 0xBFFF range
if (msg.message == 0xBEEF)
{
DEBUG_LOG(("COPYPROTECTION - Received message from launcher (Elapsed time %ld).\n",
(10000 - (endTime - timeGetTime()))));
HANDLE mappedFile = (HANDLE)msg.lParam;
s_protectedData = MapViewOfFileEx(mappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
if (s_protectedData == NULL)
{
DEBUG_LOG(("***** MapViewOfFileEx() Failed!\n"));
break;
}
DEBUG_LOG(("The message says: %s\n", s_protectedData));
return TRUE;
}
}
Sleep(0);
}
DEBUG_LOG(("***** Failed to notify launcher!\n"));
return FALSE;
}
else
{
DEBUG_LOG(("***** Failed to notify launcher!\n"));
}
return FALSE;
}
// ---------------------------------------------------------------------------
void CopyProtect::checkForMessage(UINT message, LPARAM lParam)
{
// 0xBEEF is in the WM_APP - 0xBFFF range
if (message == 0xBEEF)
{
DEBUG_LOG(("COPYPROTECTION - Received message from launcher.\n"));
HANDLE mappedFile = (HANDLE)lParam;
s_protectedData = MapViewOfFileEx(mappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
if (s_protectedData == NULL)
{
DEBUG_LOG(("***** MapViewOfFileEx() Failed!"));
return;
}
DEBUG_LOG(("The message says: %s\n", s_protectedData));
}
}
// ---------------------------------------------------------------------------
Bool CopyProtect::validate(void)
{
DEBUG_LOG(("COPYPROTECTION - Validating\n"));
DEBUG_LOG(("s_protectedData = %d (%s)\n", s_protectedData, (s_protectedData)?s_protectedData:"EEK!"));
if (s_protectedData != NULL)
{
return (strcmp((const char*)s_protectedData,
"Play the \"Command & Conquer: Generals\" Multiplayer Test.") == 0);
}
#if defined(_DEBUG) || defined(_INTERNAL)
if (skipProtection())
{
DEBUG_LOG(("DevStudio is running - forcing to TRUE\n"));
return TRUE;
}
#endif
return FALSE;
}
// ---------------------------------------------------------------------------
void CopyProtect::shutdown(void)
{
DEBUG_LOG(("COPYPROTECTION - Shutdown\n"));
if (s_protectedData != NULL)
{
UnmapViewOfFile(s_protectedData);
s_protectedData = NULL;
}
}
#endif // DO_COPY_PROTECTION

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/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CriticalSection.h"
// Definitions.
CriticalSection *TheAsciiStringCriticalSection = NULL;
CriticalSection *TheUnicodeStringCriticalSection = NULL;
CriticalSection *TheDmaCriticalSection = NULL;
CriticalSection *TheMemoryPoolCriticalSection = NULL;
CriticalSection *TheDebugLogCriticalSection = NULL;
#ifdef PERF_TIMERS
PerfGather TheCritSecPerfGather("CritSec");
#endif

View file

@ -0,0 +1,964 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// DataChunk.cpp
// Implementation of Data Chunk save/load system
// Author: Michael S. Booth, October 2000
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "stdlib.h"
#include "string.h"
#include "Compression.h"
#include "Common/DataChunk.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
// If verbose, lots of debug logging.
#define not_VERBOSE
CachedFileInputStream::CachedFileInputStream(void):m_buffer(NULL),m_size(0)
{
}
CachedFileInputStream::~CachedFileInputStream(void)
{
if (m_buffer) {
delete[] m_buffer;
m_buffer=NULL;
}
}
Bool CachedFileInputStream::open(AsciiString path)
{
File *file=TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
m_size = 0;
if (file) {
m_size=file->size();
if (m_size) {
m_buffer = file->readEntireAndClose();
file = NULL;
}
m_pos=0;
}
if (CompressionManager::isDataCompressed(m_buffer, m_size) == 0)
{
//DEBUG_LOG(("CachedFileInputStream::open() - file %s is uncompressed at %d bytes!\n", path.str(), m_size));
}
else
{
Int uncompLen = CompressionManager::getUncompressedSize(m_buffer, m_size);
//DEBUG_LOG(("CachedFileInputStream::open() - file %s is compressed! It should go from %d to %d\n", path.str(),
// m_size, uncompLen));
char *uncompBuffer = NEW char[uncompLen];
Int actualLen = CompressionManager::decompressData(m_buffer, m_size, uncompBuffer, uncompLen);
if (actualLen == uncompLen)
{
//DEBUG_LOG(("Using uncompressed data\n"));
delete[] m_buffer;
m_buffer = uncompBuffer;
m_size = uncompLen;
}
else
{
//DEBUG_LOG(("Decompression failed - using compressed data\n"));
// decompression failed. Maybe we invalidly thought it was compressed?
delete[] uncompBuffer;
}
}
//if (m_size >= 4)
//{
// DEBUG_LOG(("File starts as '%c%c%c%c'\n", m_buffer[0], m_buffer[1],
// m_buffer[2], m_buffer[3]));
//}
if (file)
{
file->close();
}
return m_size != 0;
}
void CachedFileInputStream::close(void)
{
if (m_buffer) {
delete[] m_buffer;
m_buffer=NULL;
}
m_pos=0;
m_size=0;
}
Int CachedFileInputStream::read(void *pData, Int numBytes)
{
if (m_buffer) {
if ((numBytes+m_pos)>m_size) {
numBytes=m_size-m_pos;
}
if (numBytes) {
memcpy(pData,m_buffer+m_pos,numBytes);
m_pos+=numBytes;
}
return(numBytes);
}
return 0;
}
UnsignedInt CachedFileInputStream::tell(void)
{
return m_pos;
}
Bool CachedFileInputStream::absoluteSeek(UnsignedInt pos)
{
if (pos<0) return false;
if (pos>m_size) {
pos=m_size;
}
m_pos=pos;
return true;
}
Bool CachedFileInputStream::eof(void)
{
return m_size==m_pos;
}
void CachedFileInputStream::rewind()
{
m_pos=0;
}
// -----------------------------------------------------------
//
// FileInputStream - helper class. Used to read in data using a FILE *
//
/*
FileInputStream::FileInputStream(void):m_file(NULL)
{
}
FileInputStream::~FileInputStream(void)
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
}
Bool FileInputStream::open(AsciiString path)
{
m_file = TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
return m_file==NULL?false:true;
}
void FileInputStream::close(void)
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
}
Int FileInputStream::read(void *pData, Int numBytes)
{
int bytesRead = 0;
if (m_file != NULL) {
bytesRead = m_file->read(pData, numBytes);
}
return(bytesRead);
}
UnsignedInt FileInputStream::tell(void)
{
UnsignedInt pos = 0;
if (m_file != NULL) {
pos = m_file->position();
}
return(pos);
}
Bool FileInputStream::absoluteSeek(UnsignedInt pos)
{
if (m_file != NULL) {
return (m_file->seek(pos, File::START) != -1);
}
return(false);
}
Bool FileInputStream::eof(void)
{
if (m_file != NULL) {
return (m_file->size() == m_file->position());
}
return(true);
}
void FileInputStream::rewind()
{
if (m_file != NULL) {
m_file->seek(0, File::START);
}
}
*/
//----------------------------------------------------------------------
// DataChunkOutput
// Data will be stored to a temporary m_tmp_file until the DataChunkOutput
// object is destroyed. At that time, the actual output m_tmp_file will
// be written, including a table of m_contents.
//----------------------------------------------------------------------
#define TEMP_FILENAME "_tmpChunk.dat"
DataChunkOutput::DataChunkOutput( OutputStream *pOut ) :
m_pOut(pOut)
{
AsciiString tmpFileName = TheGlobalData->getPath_UserData();
tmpFileName.concat(TEMP_FILENAME);
m_tmp_file = ::fopen( tmpFileName.str(), "wb" );
// Added Sadullah Nader
// Initializations missing and needed
m_chunkStack = NULL;
// End Add
}
DataChunkOutput::~DataChunkOutput()
{
// store the table of m_contents
m_contents.write(*m_pOut);
// Rewind the temp m_tmp_file
::fclose(m_tmp_file);
AsciiString tmpFileName = TheGlobalData->getPath_UserData();
tmpFileName.concat(TEMP_FILENAME);
m_tmp_file = ::fopen( tmpFileName.str(), "rb" );
::fseek(m_tmp_file, 0, SEEK_SET);
// append the temp m_tmp_file m_contents
char buffer[256];
int len = 256;
while( len == 256 )
{
// copy data from the temp m_tmp_file to the output m_tmp_file
len = ::fread( buffer, 1, 256, m_tmp_file );
m_pOut->write( buffer, len );
}
::fclose(m_tmp_file);
}
void DataChunkOutput::openDataChunk( char *name, DataChunkVersionType ver )
{
// allocate (or get existing) ID from the table of m_contents
UnsignedInt id = m_contents.allocateID( AsciiString(name) );
// allocate a new chunk and place it on top of the chunk stack
OutputChunk *c = newInstance(OutputChunk);
c->next = m_chunkStack;
m_chunkStack = c;
m_chunkStack->id = id;
// store the chunk ID
::fwrite( (const char *)&id, sizeof(UnsignedInt), 1, m_tmp_file );
// store the chunk version number
::fwrite( (const char *)&ver, sizeof(DataChunkVersionType), 1, m_tmp_file );
// remember this m_tmp_file position so we can write the real data size later
c->filepos = ::ftell(m_tmp_file);
#ifdef VERBOSE
DEBUG_LOG(("Writing chunk %s at %d (%x)\n", name, ::ftell(m_tmp_file), ::ftell(m_tmp_file)));
#endif
// store a placeholder for the data size
Int dummy = 0xffff;
::fwrite( (const char *)&dummy, sizeof(Int), 1, m_tmp_file );
}
void DataChunkOutput::closeDataChunk( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
return;
}
// remember where we are
Int here = ::ftell(m_tmp_file);
// rewind to store the data size
::fseek(m_tmp_file, m_chunkStack->filepos , SEEK_SET);
// compute data size (not including the actual data size itself)
Int size = here - m_chunkStack->filepos - sizeof(Int);
// store the data size
::fwrite( (const char *)&size, sizeof(Int) , 1, m_tmp_file );
// go back to where we were
::fseek(m_tmp_file, here , SEEK_SET);
// pop the chunk off the stack
OutputChunk *c = m_chunkStack;
#ifdef VERBOSE
DEBUG_LOG(("Closing chunk %s at %d (%x)\n", m_contents.getName(c->id).str(), here, here));
#endif
m_chunkStack = m_chunkStack->next;
c->deleteInstance();
}
void DataChunkOutput::writeReal( Real r )
{
::fwrite( (const char *)&r, sizeof(float) , 1, m_tmp_file );
}
void DataChunkOutput::writeInt( Int i )
{
::fwrite( (const char *)&i, sizeof(Int) , 1, m_tmp_file );
}
void DataChunkOutput::writeByte( Byte b )
{
::fwrite( (const char *)&b, sizeof(Byte) , 1, m_tmp_file );
}
void DataChunkOutput::writeArrayOfBytes(char *ptr, Int len)
{
::fwrite( (const char *)ptr, 1, len , m_tmp_file );
}
void DataChunkOutput::writeAsciiString( const AsciiString& theString )
{
UnsignedShort len = theString.getLength();
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
::fwrite( theString.str(), len , 1, m_tmp_file );
}
void DataChunkOutput::writeUnicodeString( UnicodeString theString )
{
UnsignedShort len = theString.getLength();
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
::fwrite( theString.str(), len*sizeof(WideChar) , 1, m_tmp_file );
}
void DataChunkOutput::writeDict( const Dict& d )
{
UnsignedShort len = d.getPairCount();
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
for (int i = 0; i < len; i++)
{
NameKeyType k = d.getNthKey(i);
AsciiString kname = TheNameKeyGenerator->keyToName(k);
Int keyAndType = m_contents.allocateID(kname);
keyAndType <<= 8;
Dict::DataType t = d.getNthType(i);
keyAndType |= (t & 0xff);
writeInt(keyAndType);
switch(t)
{
case Dict::DICT_BOOL:
writeByte(d.getNthBool(i)?1:0);
break;
case Dict::DICT_INT:
writeInt(d.getNthInt(i));
break;
case Dict::DICT_REAL:
writeReal(d.getNthReal(i));
break;
case Dict::DICT_ASCIISTRING:
writeAsciiString(d.getNthAsciiString(i));
break;
case Dict::DICT_UNICODESTRING:
writeUnicodeString(d.getNthUnicodeString(i));
break;
default:
DEBUG_CRASH(("impossible"));
break;
}
}
}
//----------------------------------------------------------------------
// DataChunkTableOfContents
//----------------------------------------------------------------------
DataChunkTableOfContents::DataChunkTableOfContents( void ) :
m_list(NULL),
m_nextID(1),
m_listLength(0),
m_headerOpened(false)
{
}
DataChunkTableOfContents::~DataChunkTableOfContents()
{
Mapping *m, *next;
// free all list elements
for( m=m_list; m; m=next )
{
next = m->next;
m->deleteInstance();
}
}
// return mapping data
Mapping *DataChunkTableOfContents::findMapping( const AsciiString& name )
{
Mapping *m;
for( m=m_list; m; m=m->next )
if (name == m->name )
return m;
return NULL;
}
// convert name to integer identifier
UnsignedInt DataChunkTableOfContents::getID( const AsciiString& name )
{
Mapping *m = findMapping( name );
if (m)
return m->id;
DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for name %s\n",name.str()));
return 0;
}
// convert integer identifier to name
AsciiString DataChunkTableOfContents::getName( UnsignedInt id )
{
Mapping *m;
for( m=m_list; m; m=m->next )
if (m->id == id)
return m->name;
DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for id %d\n",id));
return AsciiString::TheEmptyString;
}
// create new ID for given name or return existing mapping
UnsignedInt DataChunkTableOfContents::allocateID(const AsciiString& name )
{
Mapping *m = findMapping( name );
if (m)
return m->id;
else
{
// allocate new id mapping
m = newInstance(Mapping);
m->id = m_nextID++;
m->name = name ;
// prepend to list
m->next = m_list;
m_list = m;
m_listLength++;
return m->id;
}
}
// output the table of m_contents to a binary m_tmp_file stream
void DataChunkTableOfContents::write( OutputStream &s )
{
Mapping *m;
unsigned char len;
Byte tag[4]={'C','k', 'M', 'p'}; // Chunky height map. jba.
s.write(tag,sizeof(tag));
// output number of elements in the table
s.write( (void *)&this->m_listLength, sizeof(Int) );
// output symbol table
for( m=this->m_list; m; m=m->next )
{
len = m->name.getLength();
s.write( (char *)&len, sizeof(unsigned char) );
s.write( (char *)m->name.str(), len);
s.write( (char *)&m->id, sizeof(UnsignedInt) );
}
}
// read the table of m_contents from a binary m_tmp_file stream
// TODO: Should this reset the symbol table?
// Append symbols to table
void DataChunkTableOfContents::read( ChunkInputStream &s)
{
Int count, i;
UnsignedInt maxID = 0;
unsigned char len;
Mapping *m;
Byte tag[4]={'x','x', 'x', 'x'}; // Chunky height map. jba.
s.read(tag,sizeof(tag));
if (tag[0] != 'C' || tag[1] != 'k' || tag[2] != 'M' || tag[3] != 'p') {
return; // Don't throw, may happen with legacy files.
}
// get number of symbols in table
s.read( (char *)&count, sizeof(Int) );
for( i=0; i<count; i++ )
{
// allocate new id mapping
m = newInstance(Mapping);
// read string length
s.read( (char *)&len, sizeof(unsigned char) );
// allocate and read in string
if (len>0) {
char *str = m->name.getBufferForRead(len);
s.read( str, len );
str[len] = '\000';
}
// read id
s.read( (char *)&m->id, sizeof(UnsignedInt) );
// prepend to list
m->next = this->m_list;
this->m_list = m;
this->m_listLength++;
// track max ID used
if (m->id > maxID)
maxID = m->id;
}
m_headerOpened = count > 0 && !s.eof();
// adjust next ID so no ID's are reused
this->m_nextID = max( this->m_nextID, maxID+1 );
}
//----------------------------------------------------------------------
// DataChunkInput
//----------------------------------------------------------------------
DataChunkInput::DataChunkInput( ChunkInputStream *pStream ) : m_file( pStream ),
m_userData(NULL),
m_currentObject(NULL),
m_chunkStack(NULL),
m_parserList(NULL)
{
// read table of m_contents
m_contents.read(*m_file);
// store location of first data chunk
m_fileposOfFirstChunk = m_file->tell();
}
DataChunkInput::~DataChunkInput()
{
clearChunkStack();
UserParser *p, *next;
for (p=m_parserList; p; p=next) {
next = p->next;
p->deleteInstance();
}
}
// register a user parsing function for a given DataChunk label
void DataChunkInput::registerParser( const AsciiString& label, const AsciiString& parentLabel,
DataChunkParserPtr parser, void *userData )
{
UserParser *p = newInstance(UserParser);
p->label.set( label );
p->parentLabel.set(parentLabel );
p->parser = parser;
p->userData = userData;
// prepend parser to parser list
p->next = m_parserList;
m_parserList = p;
}
// parse the chunk stream using registered parsers
// it is assumed that the file position is at the start of a data chunk
// (it can be inside a parent chunk) when parse is called.
Bool DataChunkInput::parse( void *userData )
{
AsciiString label;
AsciiString parentLabel;
DataChunkVersionType ver;
UserParser *parser;
Bool scopeOK;
DataChunkInfo info;
// If the header wasn't a chunk table of contents, we can't parse.
if (!m_contents.isOpenedForRead()) {
return false;
}
// if we are inside a data chunk right now, get its name
if (m_chunkStack)
parentLabel = m_contents.getName( m_chunkStack->id );
while( atEndOfFile() == false )
{
if (m_chunkStack) { // If we are parsing chunks in a chunk, check current length.
if (m_chunkStack->dataLeft < CHUNK_HEADER_BYTES) {
DEBUG_ASSERTCRASH( m_chunkStack->dataLeft==0, ("Unexpected extra data in chunk."));
break;
}
}
// open the chunk
label = openDataChunk( &ver );
if (atEndOfFile()) { // FILE * returns eof after you read past end of file, so check.
break;
}
// find a registered parser for this chunk
for( parser=m_parserList; parser; parser=parser->next )
{
// chunk labels must match
if ( parser->label == label )
{
// make sure parent name (scope) also matches
scopeOK = true;
if (parentLabel != parser->parentLabel)
scopeOK = false;
if (scopeOK)
{
// m_tmp_file out the chunk info and call the user parser
info.label = label;
info.parentLabel = parentLabel;
info.version = ver;
info.dataSize = getChunkDataSize();
if (parser->parser( *this, &info, userData ) == false)
return false;
break;
}
}
}
// close chunk (and skip to end if need be)
closeDataChunk();
}
return true;
}
// clear the stack
void DataChunkInput::clearChunkStack( void )
{
InputChunk *c, *next;
for( c=m_chunkStack; c; c=next )
{
next = c->next;
c->deleteInstance();
}
m_chunkStack = NULL;
}
// reset the stream to just-opened state - ready to parse the first chunk
void DataChunkInput::reset( void )
{
clearChunkStack();
m_file->absoluteSeek( m_fileposOfFirstChunk );
}
// Checks if the file has our initial tag word.
Bool DataChunkInput::isValidFileType(void)
{
return m_contents.isOpenedForRead();
}
AsciiString DataChunkInput::openDataChunk(DataChunkVersionType *ver )
{
// allocate a new chunk and place it on top of the chunk stack
InputChunk *c = newInstance(InputChunk);
c->id = 0;
c->version = 0;
c->dataSize = 0;
//DEBUG_LOG(("Opening data chunk at offset %d (%x)\n", m_file->tell(), m_file->tell()));
// read the chunk ID
m_file->read( (char *)&c->id, sizeof(UnsignedInt) );
decrementDataLeft( sizeof(UnsignedInt) );
// read the chunk version number
m_file->read( (char *)&c->version, sizeof(DataChunkVersionType) );
decrementDataLeft( sizeof(DataChunkVersionType) );
// read the chunk data size
m_file->read( (char *)&c->dataSize, sizeof(Int) );
decrementDataLeft( sizeof(Int) );
// all of the data remains to be read
c->dataLeft = c->dataSize;
c->chunkStart = m_file->tell();
*ver = c->version;
c->next = m_chunkStack;
m_chunkStack = c;
if (this->atEndOfFile()) {
return (AsciiString(""));
}
return m_contents.getName( c->id );
}
// close chunk and move to start of next chunk
void DataChunkInput::closeDataChunk( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
return;
}
if (m_chunkStack->dataLeft > 0)
{
// skip past the remainder of this chunk
m_file->absoluteSeek( m_file->tell()+m_chunkStack->dataLeft );
decrementDataLeft( m_chunkStack->dataLeft );
}
// pop the chunk off the stack
InputChunk *c = m_chunkStack;
m_chunkStack = m_chunkStack->next;
c->deleteInstance();
}
// return label of current data chunk
AsciiString DataChunkInput::getChunkLabel( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return AsciiString("");
}
return m_contents.getName( m_chunkStack->id );
}
// return version of current data chunk
DataChunkVersionType DataChunkInput::getChunkVersion( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return NULL;
}
return m_chunkStack->version;
}
// return size of data stored in this chunk
UnsignedInt DataChunkInput::getChunkDataSize( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return NULL;
}
return m_chunkStack->dataSize;
}
// return size of data left to read in this chunk
UnsignedInt DataChunkInput::getChunkDataSizeLeft( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return NULL;
}
return m_chunkStack->dataLeft;
}
Bool DataChunkInput::atEndOfChunk( void )
{
if (m_chunkStack)
{
if (m_chunkStack->dataLeft <= 0)
return true;
return false;
}
return true;
}
// update data left in chunk(s)
// since data read from a chunk is also read from all parent chunks,
// traverse the chunk stack and decrement the data left for each
void DataChunkInput::decrementDataLeft( Int size )
{
InputChunk *c;
c = m_chunkStack;
while (c) {
c->dataLeft -= size;
c = c->next;
}
// The sizes of the parent chunks on the stack are adjusted in closeDataChunk.
}
Real DataChunkInput::readReal(void)
{
Real r;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Real), ("Read past end of chunk."));
m_file->read( (char *)&r, sizeof(Real) );
decrementDataLeft( sizeof(Real) );
return r;
}
Int DataChunkInput::readInt(void)
{
Int i;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Int), ("Read past end of chunk."));
m_file->read( (char *)&i, sizeof(Int) );
decrementDataLeft( sizeof(Int) );
return i;
}
Byte DataChunkInput::readByte(void)
{
Byte b;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Byte), ("Read past end of chunk."));
m_file->read( (char *)&b, sizeof(Byte) );
decrementDataLeft( sizeof(Byte) );
return b;
}
void DataChunkInput::readArrayOfBytes(char *ptr, Int len)
{
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
m_file->read( ptr, len );
decrementDataLeft( len );
}
Dict DataChunkInput::readDict()
{
UnsignedShort len;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
m_file->read( &len, sizeof(UnsignedShort) );
decrementDataLeft( sizeof(UnsignedShort) );
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
Dict d(len);
for (int i = 0; i < len; i++)
{
Int keyAndType = readInt();
Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
keyAndType >>= 8;
AsciiString kname = m_contents.getName(keyAndType);
NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
switch(t)
{
case Dict::DICT_BOOL:
d.setBool(k, readByte() ? true : false);
break;
case Dict::DICT_INT:
d.setInt(k, readInt());
break;
case Dict::DICT_REAL:
d.setReal(k, readReal());
break;
case Dict::DICT_ASCIISTRING:
d.setAsciiString(k, readAsciiString());
break;
case Dict::DICT_UNICODESTRING:
d.setUnicodeString(k, readUnicodeString());
break;
default:
throw ERROR_CORRUPT_FILE_FORMAT;
break;
}
}
return d;
}
AsciiString DataChunkInput::readAsciiString(void)
{
UnsignedShort len;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
m_file->read( &len, sizeof(UnsignedShort) );
decrementDataLeft( sizeof(UnsignedShort) );
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
AsciiString theString;
if (len>0) {
char *str = theString.getBufferForRead(len);
m_file->read( str, len );
decrementDataLeft( len );
// add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
str[len] = '\000';
}
return theString;
}
UnicodeString DataChunkInput::readUnicodeString(void)
{
UnsignedShort len;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
m_file->read( &len, sizeof(UnsignedShort) );
decrementDataLeft( sizeof(UnsignedShort) );
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
UnicodeString theString;
if (len>0) {
WideChar *str = theString.getBufferForRead(len);
m_file->read( (char*)str, len*sizeof(WideChar) );
decrementDataLeft( len*sizeof(WideChar) );
// add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
str[len] = '\000';
}
return theString;
}

View file

@ -0,0 +1,779 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Debug.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Debug.cpp
//
// Created: Steven Johnson, August 2001
//
// Desc: Debug logging and other debug utilities
//
// ----------------------------------------------------------------------------
// SYSTEM INCLUDES
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
// USER INCLUDES
#define DEBUG_THREADSAFE
#ifdef DEBUG_THREADSAFE
#include "Common/CriticalSection.h"
#endif
#include "Common/Debug.h"
#include "Common/registry.h"
#include "Common/SystemInfo.h"
#include "Common/UnicodeString.h"
#include "GameClient/GameText.h"
#include "GameClient/Keyboard.h"
#include "GameClient/Mouse.h"
#include "Common/StackDump.h"
// Horrible reference, but we really, really need to know if we are windowed.
extern bool DX8Wrapper_IsWindowed;
extern HWND ApplicationHWnd;
extern char *gAppPrefix; /// So WB can have a different log file name.
#ifdef _INTERNAL
// this should ALWAYS be present
#pragma optimize("", off)
#endif
// ----------------------------------------------------------------------------
// DEFINES
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
#if defined(_INTERNAL)
#define DEBUG_FILE_NAME "DebugLogFileI.txt"
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrevI.txt"
#elif defined(_DEBUG)
#define DEBUG_FILE_NAME "DebugLogFileD.txt"
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrevD.txt"
#else
#define DEBUG_FILE_NAME "DebugLogFile.txt"
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrev.txt"
#endif
#endif
// ----------------------------------------------------------------------------
// PRIVATE TYPES
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// PRIVATE DATA
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
static FILE *theLogFile = NULL;
#endif
#define LARGE_BUFFER 8192
static char theBuffer[ LARGE_BUFFER ]; // make it big to avoid weird overflow bugs in debug mode
static int theDebugFlags = 0;
static DWORD theMainThreadID = 0;
// ----------------------------------------------------------------------------
// PUBLIC DATA
// ----------------------------------------------------------------------------
char* TheCurrentIgnoreCrashPtr = NULL;
#ifdef DEBUG_LOGGING
UnsignedInt DebugLevelMask = 0;
const char *TheDebugLevels[DEBUG_LEVEL_MAX] = {
"NET"
};
#endif
// ----------------------------------------------------------------------------
// PRIVATE PROTOTYPES
// ----------------------------------------------------------------------------
static const char *getCurrentTimeString(void);
static const char *getCurrentTickString(void);
static const char *prepBuffer(const char* format, char *buffer);
#ifdef DEBUG_LOGGING
static void doLogOutput(const char *buffer);
#endif
static int doCrashBox(const char *buffer, Bool logResult);
static void whackFunnyCharacters(char *buf);
#ifdef DEBUG_STACKTRACE
static void doStackDump();
#endif
// ----------------------------------------------------------------------------
// PRIVATE FUNCTIONS
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
inline Bool ignoringAsserts()
{
#if defined(_DEBUG) || defined(_INTERNAL)
return !DX8Wrapper_IsWindowed || TheGlobalData->m_debugIgnoreAsserts;
#else
return !DX8Wrapper_IsWindowed;
#endif
}
// ----------------------------------------------------------------------------
inline HWND getThreadHWND()
{
return (theMainThreadID == GetCurrentThreadId())?ApplicationHWnd:NULL;
}
// ----------------------------------------------------------------------------
int MessageBoxWrapper( LPCSTR lpText, LPCSTR lpCaption, UINT uType )
{
HWND threadHWND = getThreadHWND();
if (!threadHWND)
return (uType & MB_ABORTRETRYIGNORE)?IDIGNORE:IDYES;
return ::MessageBox(threadHWND, lpText, lpCaption, uType);
}
// ----------------------------------------------------------------------------
// getCurrentTimeString
/**
Return the current time in string form
*/
// ----------------------------------------------------------------------------
static const char *getCurrentTimeString(void)
{
time_t aclock;
time(&aclock);
struct tm *newtime = localtime(&aclock);
return asctime(newtime);
}
// ----------------------------------------------------------------------------
// getCurrentTickString
/**
Return the current TickCount in string form
*/
// ----------------------------------------------------------------------------
static const char *getCurrentTickString(void)
{
static char TheTickString[32];
sprintf(TheTickString, "(T=%08lx)",::GetTickCount());
return TheTickString;
}
// ----------------------------------------------------------------------------
// prepBuffer
// zap the buffer and optionally prepend the tick time.
// ----------------------------------------------------------------------------
/**
Empty the buffer passed in, then optionally prepend the current TickCount
value in string form, depending on the setting of theDebugFlags.
*/
static const char *prepBuffer(const char* format, char *buffer)
{
buffer[0] = 0;
#ifdef ALLOW_DEBUG_UTILS
if (theDebugFlags & DEBUG_FLAG_PREPEND_TIME)
{
strcpy(buffer, getCurrentTickString());
strcat(buffer, " ");
}
#endif
return format;
}
// ----------------------------------------------------------------------------
// doLogOutput
/**
send a string directly to the log file and/or console without further processing.
*/
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
static void doLogOutput(const char *buffer)
{
// log message to file
if (theDebugFlags & DEBUG_FLAG_LOG_TO_FILE)
{
if (theLogFile)
{
fprintf(theLogFile, "%s", buffer); // note, no \n (should be there already)
fflush(theLogFile);
}
}
// log message to dev studio output window
if (theDebugFlags & DEBUG_FLAG_LOG_TO_CONSOLE)
{
::OutputDebugString(buffer);
}
}
#endif
// ----------------------------------------------------------------------------
// doCrashBox
/*
present a messagebox with the given message. Depending on user selection,
we exit the app, break into debugger, or continue execution.
*/
// ----------------------------------------------------------------------------
static int doCrashBox(const char *buffer, Bool logResult)
{
int result;
if (!ignoringAsserts()) {
//result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING|MB_DEFBUTTON3);
result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING);
} else {
result = IDIGNORE;
}
switch(result)
{
case IDABORT:
#ifdef DEBUG_LOGGING
if (logResult)
DebugLog("[Abort]\n");
#endif
_exit(1);
break;
case IDRETRY:
#ifdef DEBUG_LOGGING
if (logResult)
DebugLog("[Retry]\n");
#endif
::DebugBreak();
break;
case IDIGNORE:
#ifdef DEBUG_LOGGING
// do nothing, just keep going
if (logResult)
DebugLog("[Ignore]\n");
#endif
break;
}
return result;
}
#ifdef DEBUG_STACKTRACE
// ----------------------------------------------------------------------------
/**
Dumps a stack trace (from the current PC) to logfile and/or console.
*/
static void doStackDump()
{
const int STACKTRACE_SIZE = 24;
const int STACKTRACE_SKIP = 2;
void* stacktrace[STACKTRACE_SIZE];
doLogOutput("\nStack Dump:\n");
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, doLogOutput);
}
#endif
// ----------------------------------------------------------------------------
// whackFunnyCharacters
/**
Eliminates any undesirable nonprinting characters, aside from newline,
replacing them with spaces.
*/
// ----------------------------------------------------------------------------
static void whackFunnyCharacters(char *buf)
{
for (char *p = buf + strlen(buf) - 1; p >= buf; --p)
{
// ok, these are naughty magic numbers, but I'm guessing you know ASCII....
if (*p >= 0 && *p < 32 && *p != 10 && *p != 13)
*p = 32;
}
}
// ----------------------------------------------------------------------------
// PUBLIC FUNCTIONS
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// DebugInit
// ----------------------------------------------------------------------------
#ifdef ALLOW_DEBUG_UTILS
/**
Initialize the debug utilities. This should be called once, as near to the
start of the app as possible, before anything else (since other code will
probably want to make use of it).
*/
void DebugInit(int flags)
{
// if (theDebugFlags != 0)
// ::MessageBox(NULL, "Debug already inited", "", MB_OK|MB_APPLMODAL);
// just quietly allow multiple calls to this, so that static ctors can call it.
if (theDebugFlags == 0 && strcmp(gAppPrefix, "wb_") != 0)
{
theDebugFlags = flags;
theMainThreadID = GetCurrentThreadId();
#ifdef DEBUG_LOGGING
char dirbuf[ _MAX_PATH ];
::GetModuleFileName( NULL, dirbuf, sizeof( dirbuf ) );
char *pEnd = dirbuf + strlen( dirbuf );
while( pEnd != dirbuf )
{
if( *pEnd == '\\' )
{
*(pEnd + 1) = 0;
break;
}
pEnd--;
}
char prevbuf[ _MAX_PATH ];
char curbuf[ _MAX_PATH ];
strcpy(prevbuf, dirbuf);
strcat(prevbuf, gAppPrefix);
strcat(prevbuf, DEBUG_FILE_NAME_PREV);
strcpy(curbuf, dirbuf);
strcat(curbuf, gAppPrefix);
strcat(curbuf, DEBUG_FILE_NAME);
remove(prevbuf);
rename(curbuf, prevbuf);
theLogFile = fopen(curbuf, "w");
if (theLogFile != NULL)
{
DebugLog("Log %s opened: %s\n", curbuf, getCurrentTimeString());
}
#endif
}
}
#endif
// ----------------------------------------------------------------------------
// DebugLog
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
/**
Print a character string to the logfile and/or console.
*/
void DebugLog(const char *format, ...)
{
#ifdef DEBUG_THREADSAFE
ScopedCriticalSection scopedCriticalSection(TheDebugLogCriticalSection);
#endif
if (theDebugFlags == 0)
MessageBoxWrapper("DebugLog - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
format = prepBuffer(format, theBuffer);
va_list arg;
va_start(arg, format);
vsprintf(theBuffer + strlen(theBuffer), format, arg);
va_end(arg);
if (strlen(theBuffer) >= sizeof(theBuffer))
MessageBoxWrapper("String too long for debug buffer", "", MB_OK|MB_TASKMODAL);
whackFunnyCharacters(theBuffer);
doLogOutput(theBuffer);
}
#endif
// ----------------------------------------------------------------------------
// DebugCrash
// ----------------------------------------------------------------------------
#ifdef DEBUG_CRASHING
/**
Print a character string to the logfile and/or console, then halt execution
while presenting the user with an exit/debug/ignore dialog containing the same
text message.
*/
void DebugCrash(const char *format, ...)
{
// Note: You might want to make this thread safe, but we cannot. The reason is that
// there is an implicit requirement on other threads that the message loop be running.
// make it not static so that it'll be thread-safe.
// make it big to avoid weird overflow bugs in debug mode
char theCrashBuffer[ LARGE_BUFFER ];
if (theDebugFlags == 0)
{
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
MessageBoxWrapper("DebugCrash - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
}
format = prepBuffer(format, theCrashBuffer);
strcat(theCrashBuffer, "ASSERTION FAILURE: ");
va_list arg;
va_start(arg, format);
vsprintf(theCrashBuffer + strlen(theCrashBuffer), format, arg);
va_end(arg);
if (strlen(theCrashBuffer) >= sizeof(theCrashBuffer))
{
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
MessageBoxWrapper("String too long for debug buffers", "", MB_OK|MB_TASKMODAL);
}
#ifdef DEBUG_LOGGING
if (ignoringAsserts())
{
doLogOutput("**** CRASH IN FULL SCREEN - Auto-ignored, CHECK THIS LOG!\n");
}
whackFunnyCharacters(theCrashBuffer);
doLogOutput(theCrashBuffer);
#endif
#ifdef DEBUG_STACKTRACE
if (!TheGlobalData->m_debugIgnoreStackTrace)
{
doStackDump();
}
#endif
strcat(theCrashBuffer, "\n\nAbort->exception; Retry->debugger; Ignore->continue\n");
int result = doCrashBox(theCrashBuffer, true);
if (result == IDIGNORE && TheCurrentIgnoreCrashPtr != NULL)
{
int yn;
if (!ignoringAsserts())
{
yn = MessageBoxWrapper("Ignore this crash from now on?", "", MB_YESNO|MB_TASKMODAL);
}
else
{
yn = IDYES;
}
if (yn == IDYES)
*TheCurrentIgnoreCrashPtr = 1;
if( TheKeyboard )
TheKeyboard->resetKeys();
if( TheMouse )
TheMouse->reset();
}
}
#endif
// ----------------------------------------------------------------------------
// DebugShutdown
// ----------------------------------------------------------------------------
#ifdef ALLOW_DEBUG_UTILS
/**
Shut down the debug utilities. This should be called once, as near to the
end of the app as possible, after everything else (since other code will
probably want to make use of it).
*/
void DebugShutdown()
{
#ifdef DEBUG_LOGGING
if (theLogFile)
{
DebugLog("Log closed: %s\n", getCurrentTimeString());
fclose(theLogFile);
}
theLogFile = NULL;
#endif
theDebugFlags = 0;
}
// ----------------------------------------------------------------------------
// DebugGetFlags
// ----------------------------------------------------------------------------
/**
Get the current values for the flags passed to DebugInit. Most code will never
need to use this; the most common usage would be to temporarily enable or disable
the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
*/
int DebugGetFlags()
{
return theDebugFlags;
}
// ----------------------------------------------------------------------------
// DebugSetFlags
// ----------------------------------------------------------------------------
/**
Set the current values for the flags passed to DebugInit. Most code will never
need to use this; the most common usage would be to temporarily enable or disable
the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
*/
void DebugSetFlags(int flags)
{
theDebugFlags = flags;
}
#endif // ALLOW_DEBUG_UTILS
#ifdef ALLOW_DEBUG_UTILS
// ----------------------------------------------------------------------------
SimpleProfiler::SimpleProfiler()
{
QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq);
m_startThisSession = 0;
m_totalThisSession = 0;
m_totalAllSessions = 0;
m_numSessions = 0;
}
// ----------------------------------------------------------------------------
void SimpleProfiler::start()
{
DEBUG_ASSERTCRASH(m_startThisSession == 0, ("already started"));
QueryPerformanceCounter((LARGE_INTEGER*)&m_startThisSession);
}
// ----------------------------------------------------------------------------
void SimpleProfiler::stop()
{
if (m_startThisSession != 0)
{
__int64 stop;
QueryPerformanceCounter((LARGE_INTEGER*)&stop);
m_totalThisSession = stop - m_startThisSession;
m_totalAllSessions += stop - m_startThisSession;
m_startThisSession = 0;
++m_numSessions;
}
}
// ----------------------------------------------------------------------------
void SimpleProfiler::stopAndLog(const char *msg, int howOftenToLog, int howOftenToResetAvg)
{
stop();
// howOftenToResetAvg==0 means "never reset"
if (howOftenToResetAvg > 0 && m_numSessions >= howOftenToResetAvg)
{
m_numSessions = 0;
m_totalAllSessions = 0;
DEBUG_LOG(("%s: reset averages\n",msg));
}
DEBUG_ASSERTLOG(m_numSessions % howOftenToLog != 0, ("%s: %f msec, total %f msec, avg %f msec\n",msg,getTime(),getTotalTime(),getAverageTime()));
}
// ----------------------------------------------------------------------------
double SimpleProfiler::getTime()
{
stop();
return (double)m_totalThisSession / (double)m_freq * 1000.0;
}
// ----------------------------------------------------------------------------
int SimpleProfiler::getNumSessions()
{
stop();
return m_numSessions;
}
// ----------------------------------------------------------------------------
double SimpleProfiler::getTotalTime()
{
stop();
if (!m_numSessions)
return 0.0;
return (double)m_totalAllSessions * 1000.0 / ((double)m_freq);
}
// ----------------------------------------------------------------------------
double SimpleProfiler::getAverageTime()
{
stop();
if (!m_numSessions)
return 0.0;
return (double)m_totalAllSessions * 1000.0 / ((double)m_freq * (double)m_numSessions);
}
#endif // ALLOW_DEBUG_UTILS
// ----------------------------------------------------------------------------
// ReleaseCrash
// ----------------------------------------------------------------------------
/**
Halt the application, EVEN IN FINAL RELEASE BUILDS. This should be called
only when a crash is guaranteed by continuing, and no meaningful continuation
of processing is possible, even by throwing an exception.
*/
#define RELEASECRASH_FILE_NAME "ReleaseCrashInfo.txt"
#define RELEASECRASH_FILE_NAME_PREV "ReleaseCrashInfoPrev.txt"
static FILE *theReleaseCrashLogFile = NULL;
static void releaseCrashLogOutput(const char *buffer)
{
if (theReleaseCrashLogFile)
{
fprintf(theReleaseCrashLogFile, "%s", buffer); // note, no \n (should be there already)
fflush(theReleaseCrashLogFile);
}
}
void ReleaseCrash(const char *reason)
{
/// do additional reporting on the crash, if possible
char prevbuf[ _MAX_PATH ];
char curbuf[ _MAX_PATH ];
strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
strcpy(curbuf, TheGlobalData->getPath_UserData().str());
strcat(curbuf, RELEASECRASH_FILE_NAME);
remove(prevbuf);
rename(curbuf, prevbuf);
theReleaseCrashLogFile = fopen(curbuf, "w");
if (theReleaseCrashLogFile)
{
fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), reason);
fprintf(theReleaseCrashLogFile, "\nLast error:\n%s\n\nCurrent stack:\n", g_LastErrorDump.str());
const int STACKTRACE_SIZE = 12;
const int STACKTRACE_SKIP = 6;
void* stacktrace[STACKTRACE_SIZE];
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
fflush(theReleaseCrashLogFile);
fclose(theReleaseCrashLogFile);
theReleaseCrashLogFile = NULL;
}
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
#if defined(_DEBUG) || defined(_INTERNAL)
/* static */ char buff[8192]; // not so static so we can be threadsafe
_snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);
buff[8191] = 0;
::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
#else
// crash error messaged changed 3/6/03 BGC
// ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
if (!GetRegistryLanguage().compareNoCase("german2") || !GetRegistryLanguage().compareNoCase("german") )
{
::MessageBox(NULL, "Es ist ein gravierender Fehler aufgetreten. Solche Fehler können durch viele verschiedene Dinge wie Viren, überhitzte Hardware und Hardware, die den Mindestanforderungen des Spiels nicht entspricht, ausgelöst werden. Tipps zur Vorgehensweise findest du in den Foren unter www.generals.ea.com, Informationen zum Technischen Kundendienst im Handbuch zum Spiel.", "Fehler...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
}
else
{
::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
}
#endif
_exit(1);
}
void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m)
{
if (!TheGameText) {
ReleaseCrash(m.str());
// This won't ever return
return;
}
UnicodeString prompt = TheGameText->fetch(p);
UnicodeString mesg = TheGameText->fetch(m);
/// do additional reporting on the crash, if possible
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
if (TheSystemIsUnicode)
{
::MessageBoxW(NULL, mesg.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
}
else
{
// However, if we're using the default version of the message box, we need to
// translate the string into an AsciiString
AsciiString promptA, mesgA;
promptA.translate(prompt);
mesgA.translate(mesg);
//Make sure main window is not TOP_MOST
::SetWindowPos(ApplicationHWnd, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
::MessageBoxA(NULL, mesgA.str(), promptA.str(), MB_OK|MB_TASKMODAL|MB_ICONERROR);
}
char prevbuf[ _MAX_PATH ];
char curbuf[ _MAX_PATH ];
strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
strcpy(curbuf, TheGlobalData->getPath_UserData().str());
strcat(curbuf, RELEASECRASH_FILE_NAME);
remove(prevbuf);
rename(curbuf, prevbuf);
theReleaseCrashLogFile = fopen(curbuf, "w");
if (theReleaseCrashLogFile)
{
fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), mesg.str());
const int STACKTRACE_SIZE = 12;
const int STACKTRACE_SKIP = 6;
void* stacktrace[STACKTRACE_SIZE];
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
fflush(theReleaseCrashLogFile);
fclose(theReleaseCrashLogFile);
theReleaseCrashLogFile = NULL;
}
_exit(1);
}

View file

@ -0,0 +1,135 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#if (0)
#include "Common/Directory.h"
//-------------------------------------------------------------------------------------------------
static void TimetToFileTime( time_t t, FILETIME& ft )
{
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
ft.dwLowDateTime = (DWORD) ll;
ft.dwHighDateTime = ll >>32;
}
static time_t FileTimeToTimet( const FILETIME& ft )
{
LONGLONG ll = (ft.dwHighDateTime << 32) + ft.dwLowDateTime - 116444736000000000;
ll /= 10000000;
return (time_t)ll;
}
//-------------------------------------------------------------------------------------------------
void FileInfo::set( const WIN32_FIND_DATA& info )
{
filename = info.cFileName;
creationTime = FileTimeToTimet(info.ftCreationTime);
accessTime = FileTimeToTimet(info.ftLastAccessTime);
modTime = FileTimeToTimet(info.ftLastWriteTime);
attributes = info.dwFileAttributes;
filesize = info.nFileSizeLow;
//DEBUG_LOG(("FileInfo::set(): fname=%s, size=%d\n", filename.str(), filesize));
}
//-------------------------------------------------------------------------------------------------
Directory::Directory( const AsciiString& dirPath ) : m_dirPath(dirPath)
{
WIN32_FIND_DATA item; // search item
HANDLE hFile; // handle for search resources
char currDir[ MAX_PATH ];
// sanity
if( m_dirPath.isEmpty() )
{
DEBUG_LOG(( "Empty dirname\n"));
return;
}
// save the current directory
GetCurrentDirectory( MAX_PATH, currDir );
// switch into the directory provided
if( SetCurrentDirectory( m_dirPath.str() ) == 0 )
{
DEBUG_LOG(( "Can't set directory '%s'\n", m_dirPath.str() ));
return;
}
// go through each item in the output directory
bool done = false;
hFile = FindFirstFile( "*", &item);
if( hFile == INVALID_HANDLE_VALUE )
{
DEBUG_LOG(( "Can't search directory '%s'\n", m_dirPath.str() ));
done = true;
}
FileInfo info;
while (!done)
{
// if this is a subdirectory keep the name around till the end
if( BitTest( item.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY ) )
{
if ( strcmp( item.cFileName, "." ) && strcmp( item.cFileName, ".." ) )
{
info.set(item);
m_subdirs.insert( info );
}
}
else
{
info.set(item);
m_files.insert( info );
}
if ( FindNextFile( hFile, &item ) == 0 )
{
done = true;
}
}
// close search
FindClose( hFile );
// restore the working directory to what it was when we started here
SetCurrentDirectory( currDir );
}
FileInfoSet* Directory::getFiles( void )
{
return &m_files;
}
FileInfoSet* Directory::getSubdirs( void )
{
return &m_subdirs;
}
#endif

View file

@ -0,0 +1,56 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// DisabledTypes.cpp /////////////////////////////////////////////////////////////////////////////////////
// Kris Morness, September 2002
#include "PreRTS.h"
#include "Common/DisabledTypes.h"
#include "Common/BitFlagsIO.h"
const char* DisabledMaskType::s_bitNameList[] =
{
"DEFAULT",
"DISABLED_HACKED",
"DISABLED_EMP",
"DISABLED_HELD",
"DISABLED_PARALYZED",
"DISABLED_UNMANNED",
"DISABLED_UNDERPOWERED",
"DISABLED_FREEFALL",
"DISABLED_SCRIPT_DISABLED",
"DISABLED_SCRIPT_UNDERPOWERED",
NULL
};
DisabledMaskType DISABLEDMASK_NONE; // inits to all zeroes
DisabledMaskType DISABLEDMASK_ALL;
void initDisabledMasks()
{
SET_ALL_DISABLEDMASK_BITS( DISABLEDMASK_ALL );
}

View file

@ -0,0 +1,259 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO_
//
// File name: IO_File.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "Common/File.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//=================================================================
// File::File
//=================================================================
File::File()
: m_open(FALSE),
m_deleteOnClose(FALSE),
m_access(NONE)
{
setName("<no file>");
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//=================================================================
// File::~File
//=================================================================
File::~File()
{
close();
}
//=================================================================
// File::open
//=================================================================
/**
* Any derived open() members must first call File::open. If File::open
* succeeds but the derived class's open failes then make sure to call
* File::close() before returning.
*/
//=================================================================
Bool File::open( const Char *filename, Int access )
{
if( m_open )
{
return FALSE;
}
setName( filename );
if( (access & ( STREAMING | WRITE )) == ( STREAMING | WRITE ))
{
// illegal access
return FALSE;
}
if( (access & ( TEXT | BINARY)) == ( TEXT | BINARY ))
{
// illegal access
return FALSE;
}
if ( (access & (READ|WRITE)) == 0 )
{
access |= READ;
}
if ( !(access & (READ|APPEND)) )
{
access |= TRUNCATE;
}
if ( (access & (TEXT|BINARY)) == 0 )
{
access |= BINARY;
}
m_access = access;
m_open = TRUE;
return TRUE;
}
//=================================================================
// File::close
//=================================================================
/**
* Must call File::close() for each successful File::open() call.
*/
//=================================================================
void File::close( void )
{
if( m_open )
{
setName( "<no file>" );
m_open = FALSE;
if ( m_deleteOnClose )
{
this->deleteInstance(); // on special cases File object will delete itself when closing
}
}
}
//=================================================================
// File::size
//=================================================================
/**
* Default implementation of File::size. Derived classes can optimize
* this member function.
*/
//=================================================================
Int File::size( void )
{
Int pos = seek( 0, CURRENT );
Int size = seek( 0, END );
seek( pos, START );
return size < 0 ? 0 : size;
}
//============================================================================
// File::position
//============================================================================
Int File::position( void )
{
return seek(0, CURRENT);
}
//============================================================================
// File::print
//============================================================================
Bool File::print ( const Char *format, ...)
{
Char buffer[10*1024];
Int len;
if ( ! (m_access & TEXT ) )
{
return FALSE;
}
va_list args;
va_start( args, format ); /* Initialize variable arguments. */
len = vsprintf( buffer, format, args );
va_end( args );
if ( len >= sizeof(buffer) )
{
// Big Problem
assert( FALSE );
return FALSE;
}
return (write ( buffer, len ) == len);
}
Bool File::eof() {
return (position() == size());
}

View file

@ -0,0 +1,324 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO
//
// File name: IO_FileSystem.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/CDManager.h"
#include "Common/GameAudio.h"
#include "Common/LocalFileSystem.h"
#include "Common/PerfTimer.h"
DECLARE_PERF_TIMER(FileSystem)
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//===============================
// TheFileSystem
//===============================
/**
* This is the FileSystem's singleton class. All file access
* should be through TheFileSystem, unless code needs to use an explicit
* File or FileSystem derivative.
*
* Using TheFileSystem->open and File exclusively for file access, particularly
* in library or modular code, allows applications to transparently implement
* file access as they see fit. This is particularly important for code that
* needs to be shared between applications, such as games and tools.
*/
//===============================
FileSystem *TheFileSystem = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// FileSystem::FileSystem
//============================================================================
FileSystem::FileSystem()
{
}
//============================================================================
// FileSystem::~FileSystem
//============================================================================
FileSystem::~FileSystem()
{
}
//============================================================================
// FileSystem::init
//============================================================================
void FileSystem::init( void )
{
TheLocalFileSystem->init();
TheArchiveFileSystem->init();
}
//============================================================================
// FileSystem::update
//============================================================================
void FileSystem::update( void )
{
USE_PERF_TIMER(FileSystem)
TheLocalFileSystem->update();
TheArchiveFileSystem->update();
}
//============================================================================
// FileSystem::reset
//============================================================================
void FileSystem::reset( void )
{
USE_PERF_TIMER(FileSystem)
TheLocalFileSystem->reset();
TheArchiveFileSystem->reset();
}
//============================================================================
// FileSystem::open
//============================================================================
File* FileSystem::openFile( const Char *filename, Int access )
{
USE_PERF_TIMER(FileSystem)
File *file = NULL;
if ( TheLocalFileSystem != NULL )
{
file = TheLocalFileSystem->openFile( filename, access );
}
if ( (TheArchiveFileSystem != NULL) && (file == NULL) )
{
file = TheArchiveFileSystem->openFile( filename );
}
return file;
}
//============================================================================
// FileSystem::doesFileExist
//============================================================================
Bool FileSystem::doesFileExist(const Char *filename) const
{
USE_PERF_TIMER(FileSystem)
if (TheLocalFileSystem->doesFileExist(filename)) {
return TRUE;
}
if (TheArchiveFileSystem->doesFileExist(filename)) {
return TRUE;
}
return FALSE;
}
//============================================================================
// FileSystem::getFileListInDirectory
//============================================================================
void FileSystem::getFileListInDirectory(const AsciiString& directory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
USE_PERF_TIMER(FileSystem)
TheLocalFileSystem->getFileListInDirectory(AsciiString(""), directory, searchName, filenameList, searchSubdirectories);
TheArchiveFileSystem->getFileListInDirectory(AsciiString(""), directory, searchName, filenameList, searchSubdirectories);
}
//============================================================================
// FileSystem::getFileInfo
//============================================================================
Bool FileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const
{
USE_PERF_TIMER(FileSystem)
if (fileInfo == NULL) {
return FALSE;
}
memset(fileInfo, 0, sizeof(fileInfo));
if (TheLocalFileSystem->getFileInfo(filename, fileInfo)) {
return TRUE;
}
if (TheArchiveFileSystem->getFileInfo(filename, fileInfo)) {
return TRUE;
}
return FALSE;
}
//============================================================================
// FileSystem::createDirectory
//============================================================================
Bool FileSystem::createDirectory(AsciiString directory)
{
USE_PERF_TIMER(FileSystem)
if (TheLocalFileSystem != NULL) {
return TheLocalFileSystem->createDirectory(directory);
}
return FALSE;
}
//============================================================================
// FileSystem::areMusicFilesOnCD
//============================================================================
Bool FileSystem::areMusicFilesOnCD()
{
#if 1
return TRUE;
#else
if (!TheCDManager) {
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - No CD Manager; returning false\n"));
return FALSE;
}
AsciiString cdRoot;
Int dc = TheCDManager->driveCount();
for (Int i = 0; i < dc; ++i) {
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - checking drive %d\n", i));
CDDriveInterface *cdi = TheCDManager->getDrive(i);
if (!cdi) {
continue;
}
cdRoot = cdi->getPath();
if (!cdRoot.endsWith("\\"))
cdRoot.concat("\\");
cdRoot.concat("gensec.big");
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - checking for %s\n", cdRoot.str()));
File *musicBig = TheLocalFileSystem->openFile(cdRoot.str());
if (musicBig)
{
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - found it!\n"));
musicBig->close();
return TRUE;
}
}
return FALSE;
#endif
}
//============================================================================
// FileSystem::loadMusicFilesFromCD
//============================================================================
void FileSystem::loadMusicFilesFromCD()
{
if (!TheCDManager) {
return;
}
AsciiString cdRoot;
Int dc = TheCDManager->driveCount();
for (Int i = 0; i < dc; ++i) {
CDDriveInterface *cdi = TheCDManager->getDrive(i);
if (!cdi) {
continue;
}
cdRoot = cdi->getPath();
if (TheArchiveFileSystem->loadBigFilesFromDirectory(cdRoot, MUSIC_BIG)) {
break;
}
}
}
//============================================================================
// FileSystem::unloadMusicFilesFromCD
//============================================================================
void FileSystem::unloadMusicFilesFromCD()
{
if (!(TheAudio && TheAudio->isMusicPlayingFromCD())) {
return;
}
TheArchiveFileSystem->closeArchiveFile( MUSIC_BIG );
}

View file

@ -0,0 +1,711 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: FunctionLexicon.cpp //////////////////////////////////////////////////////////////////////
// Created: Colin Day, September 2001
// Desc: Collection of function pointers to help us in managing
// and assign callbacks
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/FunctionLexicon.h"
#include "GameClient/GameWindow.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GUICallbacks.h"
#include "GameClient/Gadget.h"
// Popup Ladder Select --------------------------------------------------------------------------
extern void PopupLadderSelectInit( WindowLayout *layout, void *userData );
extern WindowMsgHandledType PopupLadderSelectSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
extern WindowMsgHandledType PopupLadderSelectInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
extern WindowMsgHandledType PopupBuddyNotificationSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// WOL Buddy Overlay Right Click menu callbacks --------------------------------------------------------------
extern void RCGameDetailsMenuInit( WindowLayout *layout, void *userData );
extern WindowMsgHandledType RCGameDetailsMenuSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// Beacon control bar callback --------------------------------------------------------------
extern WindowMsgHandledType BeaconWindowInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// Popup Replay Save Menu ----------------------------------------------------------------------------------
extern void PopupReplayInit( WindowLayout *layout, void *userData );
extern void PopupReplayUpdate( WindowLayout *layout, void *userData );
extern void PopupReplayShutdown( WindowLayout *layout, void *userData );
extern WindowMsgHandledType PopupReplaySystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
extern WindowMsgHandledType PopupReplayInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// Extended MessageBox ----------------------------------------------------------------------------------
extern WindowMsgHandledType ExtendedMessageBoxSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// game window draw table -----------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinDrawTable[] =
{
{ NAMEKEY_INVALID, "IMECandidateMainDraw", IMECandidateMainDraw },
{ NAMEKEY_INVALID, "IMECandidateTextAreaDraw", IMECandidateTextAreaDraw },
{ NAMEKEY_INVALID, NULL, NULL }
};
// game window system table -----------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinSystemTable[] =
{
{ NAMEKEY_INVALID, "PassSelectedButtonsToParentSystem", PassSelectedButtonsToParentSystem },
{ NAMEKEY_INVALID, "PassMessagesToParentSystem", PassMessagesToParentSystem },
{ NAMEKEY_INVALID, "GameWinDefaultSystem", GameWinDefaultSystem },
{ NAMEKEY_INVALID, "GadgetPushButtonSystem", GadgetPushButtonSystem },
{ NAMEKEY_INVALID, "GadgetCheckBoxSystem", GadgetCheckBoxSystem },
{ NAMEKEY_INVALID, "GadgetRadioButtonSystem", GadgetRadioButtonSystem },
{ NAMEKEY_INVALID, "GadgetTabControlSystem", GadgetTabControlSystem },
{ NAMEKEY_INVALID, "GadgetListBoxSystem", GadgetListBoxSystem },
{ NAMEKEY_INVALID, "GadgetComboBoxSystem", GadgetComboBoxSystem },
{ NAMEKEY_INVALID, "GadgetHorizontalSliderSystem", GadgetHorizontalSliderSystem },
{ NAMEKEY_INVALID, "GadgetVerticalSliderSystem", GadgetVerticalSliderSystem },
{ NAMEKEY_INVALID, "GadgetProgressBarSystem", GadgetProgressBarSystem },
{ NAMEKEY_INVALID, "GadgetStaticTextSystem", GadgetStaticTextSystem },
{ NAMEKEY_INVALID, "GadgetTextEntrySystem", GadgetTextEntrySystem },
{ NAMEKEY_INVALID, "MessageBoxSystem", MessageBoxSystem },
{ NAMEKEY_INVALID, "QuitMessageBoxSystem", QuitMessageBoxSystem },
{ NAMEKEY_INVALID, "ExtendedMessageBoxSystem", ExtendedMessageBoxSystem },
{ NAMEKEY_INVALID, "MOTDSystem", MOTDSystem },
{ NAMEKEY_INVALID, "MainMenuSystem", MainMenuSystem },
{ NAMEKEY_INVALID, "OptionsMenuSystem", OptionsMenuSystem },
{ NAMEKEY_INVALID, "SinglePlayerMenuSystem", SinglePlayerMenuSystem },
{ NAMEKEY_INVALID, "QuitMenuSystem", QuitMenuSystem },
{ NAMEKEY_INVALID, "MapSelectMenuSystem", MapSelectMenuSystem },
{ NAMEKEY_INVALID, "ReplayMenuSystem", ReplayMenuSystem },
{ NAMEKEY_INVALID, "CreditsMenuSystem", CreditsMenuSystem },
{ NAMEKEY_INVALID, "LanLobbyMenuSystem", LanLobbyMenuSystem },
{ NAMEKEY_INVALID, "LanGameOptionsMenuSystem", LanGameOptionsMenuSystem },
{ NAMEKEY_INVALID, "LanMapSelectMenuSystem", LanMapSelectMenuSystem },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuSystem", SkirmishGameOptionsMenuSystem },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuSystem", SkirmishMapSelectMenuSystem },
{ NAMEKEY_INVALID, "SaveLoadMenuSystem", SaveLoadMenuSystem },
{ NAMEKEY_INVALID, "PopupCommunicatorSystem", PopupCommunicatorSystem },
{ NAMEKEY_INVALID, "PopupBuddyNotificationSystem", PopupBuddyNotificationSystem },
{ NAMEKEY_INVALID, "PopupReplaySystem", PopupReplaySystem },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuSystem", KeyboardOptionsMenuSystem },
{ NAMEKEY_INVALID, "WOLLadderScreenSystem", WOLLadderScreenSystem },
{ NAMEKEY_INVALID, "WOLLoginMenuSystem", WOLLoginMenuSystem },
{ NAMEKEY_INVALID, "WOLLocaleSelectSystem", WOLLocaleSelectSystem },
{ NAMEKEY_INVALID, "WOLLobbyMenuSystem", WOLLobbyMenuSystem },
{ NAMEKEY_INVALID, "WOLGameSetupMenuSystem", WOLGameSetupMenuSystem },
{ NAMEKEY_INVALID, "WOLMapSelectMenuSystem", WOLMapSelectMenuSystem },
{ NAMEKEY_INVALID, "WOLBuddyOverlaySystem", WOLBuddyOverlaySystem },
{ NAMEKEY_INVALID, "WOLBuddyOverlayRCMenuSystem", WOLBuddyOverlayRCMenuSystem },
{ NAMEKEY_INVALID, "RCGameDetailsMenuSystem", RCGameDetailsMenuSystem },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlaySystem",GameSpyPlayerInfoOverlaySystem },
{ NAMEKEY_INVALID, "WOLMessageWindowSystem", WOLMessageWindowSystem },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuSystem", WOLQuickMatchMenuSystem },
{ NAMEKEY_INVALID, "WOLWelcomeMenuSystem", WOLWelcomeMenuSystem },
{ NAMEKEY_INVALID, "WOLStatusMenuSystem", WOLStatusMenuSystem },
{ NAMEKEY_INVALID, "WOLQMScoreScreenSystem", WOLQMScoreScreenSystem },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenSystem", WOLCustomScoreScreenSystem },
{ NAMEKEY_INVALID, "NetworkDirectConnectSystem", NetworkDirectConnectSystem },
{ NAMEKEY_INVALID, "PopupHostGameSystem", PopupHostGameSystem },
{ NAMEKEY_INVALID, "PopupJoinGameSystem", PopupJoinGameSystem },
{ NAMEKEY_INVALID, "PopupLadderSelectSystem", PopupLadderSelectSystem },
{ NAMEKEY_INVALID, "InGamePopupMessageSystem", InGamePopupMessageSystem },
{ NAMEKEY_INVALID, "ControlBarSystem", ControlBarSystem },
{ NAMEKEY_INVALID, "ControlBarObserverSystem", ControlBarObserverSystem },
{ NAMEKEY_INVALID, "IMECandidateWindowSystem", IMECandidateWindowSystem },
{ NAMEKEY_INVALID, "ReplayControlSystem", ReplayControlSystem },
{ NAMEKEY_INVALID, "InGameChatSystem", InGameChatSystem },
{ NAMEKEY_INVALID, "DisconnectControlSystem", DisconnectControlSystem },
{ NAMEKEY_INVALID, "DiplomacySystem", DiplomacySystem },
{ NAMEKEY_INVALID, "GeneralsExpPointsSystem", GeneralsExpPointsSystem },
{ NAMEKEY_INVALID, "DifficultySelectSystem", DifficultySelectSystem },
{ NAMEKEY_INVALID, "IdleWorkerSystem", IdleWorkerSystem },
{ NAMEKEY_INVALID, "EstablishConnectionsControlSystem", EstablishConnectionsControlSystem },
{ NAMEKEY_INVALID, "GameInfoWindowSystem", GameInfoWindowSystem },
{ NAMEKEY_INVALID, "ScoreScreenSystem", ScoreScreenSystem },
{ NAMEKEY_INVALID, "DownloadMenuSystem", DownloadMenuSystem },
{ NAMEKEY_INVALID, NULL, NULL }
};
// game window input table ------------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinInputTable[] =
{
{ NAMEKEY_INVALID, "GameWinDefaultInput", GameWinDefaultInput },
{ NAMEKEY_INVALID, "GameWinBlockInput", GameWinBlockInput },
{ NAMEKEY_INVALID, "GadgetPushButtonInput", GadgetPushButtonInput },
{ NAMEKEY_INVALID, "GadgetCheckBoxInput", GadgetCheckBoxInput },
{ NAMEKEY_INVALID, "GadgetRadioButtonInput", GadgetRadioButtonInput },
{ NAMEKEY_INVALID, "GadgetTabControlInput", GadgetTabControlInput },
{ NAMEKEY_INVALID, "GadgetListBoxInput", GadgetListBoxInput },
{ NAMEKEY_INVALID, "GadgetListBoxMultiInput", GadgetListBoxMultiInput },
{ NAMEKEY_INVALID, "GadgetComboBoxInput", GadgetComboBoxInput },
{ NAMEKEY_INVALID, "GadgetHorizontalSliderInput", GadgetHorizontalSliderInput },
{ NAMEKEY_INVALID, "GadgetVerticalSliderInput", GadgetVerticalSliderInput },
{ NAMEKEY_INVALID, "GadgetStaticTextInput", GadgetStaticTextInput },
{ NAMEKEY_INVALID, "GadgetTextEntryInput", GadgetTextEntryInput },
{ NAMEKEY_INVALID, "MainMenuInput", MainMenuInput },
{ NAMEKEY_INVALID, "MapSelectMenuInput", MapSelectMenuInput },
{ NAMEKEY_INVALID, "OptionsMenuInput", OptionsMenuInput },
{ NAMEKEY_INVALID, "SinglePlayerMenuInput", SinglePlayerMenuInput },
{ NAMEKEY_INVALID, "LanLobbyMenuInput", LanLobbyMenuInput },
{ NAMEKEY_INVALID, "ReplayMenuInput", ReplayMenuInput },
{ NAMEKEY_INVALID, "CreditsMenuInput", CreditsMenuInput },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuInput", KeyboardOptionsMenuInput },
{ NAMEKEY_INVALID, "PopupCommunicatorInput", PopupCommunicatorInput },
{ NAMEKEY_INVALID, "LanGameOptionsMenuInput", LanGameOptionsMenuInput },
{ NAMEKEY_INVALID, "LanMapSelectMenuInput", LanMapSelectMenuInput },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuInput", SkirmishGameOptionsMenuInput },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuInput", SkirmishMapSelectMenuInput },
{ NAMEKEY_INVALID, "WOLLadderScreenInput", WOLLadderScreenInput },
{ NAMEKEY_INVALID, "WOLLoginMenuInput", WOLLoginMenuInput },
{ NAMEKEY_INVALID, "WOLLocaleSelectInput", WOLLocaleSelectInput },
{ NAMEKEY_INVALID, "WOLLobbyMenuInput", WOLLobbyMenuInput },
{ NAMEKEY_INVALID, "WOLGameSetupMenuInput", WOLGameSetupMenuInput },
{ NAMEKEY_INVALID, "WOLMapSelectMenuInput", WOLMapSelectMenuInput },
{ NAMEKEY_INVALID, "WOLBuddyOverlayInput", WOLBuddyOverlayInput },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayInput", GameSpyPlayerInfoOverlayInput },
{ NAMEKEY_INVALID, "WOLMessageWindowInput", WOLMessageWindowInput },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuInput", WOLQuickMatchMenuInput },
{ NAMEKEY_INVALID, "WOLWelcomeMenuInput", WOLWelcomeMenuInput },
{ NAMEKEY_INVALID, "WOLStatusMenuInput", WOLStatusMenuInput },
{ NAMEKEY_INVALID, "WOLQMScoreScreenInput", WOLQMScoreScreenInput },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenInput", WOLCustomScoreScreenInput },
{ NAMEKEY_INVALID, "NetworkDirectConnectInput", NetworkDirectConnectInput },
{ NAMEKEY_INVALID, "PopupHostGameInput", PopupHostGameInput },
{ NAMEKEY_INVALID, "PopupJoinGameInput", PopupJoinGameInput },
{ NAMEKEY_INVALID, "PopupLadderSelectInput", PopupLadderSelectInput },
{ NAMEKEY_INVALID, "InGamePopupMessageInput", InGamePopupMessageInput },
{ NAMEKEY_INVALID, "ControlBarInput", ControlBarInput },
{ NAMEKEY_INVALID, "ReplayControlInput", ReplayControlInput },
{ NAMEKEY_INVALID, "InGameChatInput", InGameChatInput },
{ NAMEKEY_INVALID, "DisconnectControlInput", DisconnectControlInput },
{ NAMEKEY_INVALID, "DiplomacyInput", DiplomacyInput },
{ NAMEKEY_INVALID, "EstablishConnectionsControlInput", EstablishConnectionsControlInput },
{ NAMEKEY_INVALID, "LeftHUDInput", LeftHUDInput },
{ NAMEKEY_INVALID, "ScoreScreenInput", ScoreScreenInput },
{ NAMEKEY_INVALID, "SaveLoadMenuInput", SaveLoadMenuInput },
{ NAMEKEY_INVALID, "BeaconWindowInput", BeaconWindowInput },
{ NAMEKEY_INVALID, "DifficultySelectInput", DifficultySelectInput },
{ NAMEKEY_INVALID, "PopupReplayInput", PopupReplayInput },
{ NAMEKEY_INVALID, "GeneralsExpPointsInput", GeneralsExpPointsInput},
{ NAMEKEY_INVALID, "DownloadMenuInput", DownloadMenuInput },
{ NAMEKEY_INVALID, "IMECandidateWindowInput", IMECandidateWindowInput },
{ NAMEKEY_INVALID, NULL, NULL }
};
// game window tooltip table ----------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinTooltipTable[] =
{
{ NAMEKEY_INVALID, "GameWinDefaultTooltip", GameWinDefaultTooltip },
{ NAMEKEY_INVALID, NULL, NULL }
};
// window layout init table -----------------------------------------------------------------------
static FunctionLexicon::TableEntry winLayoutInitTable[] =
{
{ NAMEKEY_INVALID, "MainMenuInit", MainMenuInit },
{ NAMEKEY_INVALID, "OptionsMenuInit", OptionsMenuInit },
{ NAMEKEY_INVALID, "SaveLoadMenuInit", SaveLoadMenuInit },
{ NAMEKEY_INVALID, "SaveLoadMenuFullScreenInit", SaveLoadMenuFullScreenInit },
{ NAMEKEY_INVALID, "PopupCommunicatorInit", PopupCommunicatorInit },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuInit", KeyboardOptionsMenuInit },
{ NAMEKEY_INVALID, "SinglePlayerMenuInit", SinglePlayerMenuInit },
{ NAMEKEY_INVALID, "MapSelectMenuInit", MapSelectMenuInit },
{ NAMEKEY_INVALID, "LanLobbyMenuInit", LanLobbyMenuInit },
{ NAMEKEY_INVALID, "ReplayMenuInit", ReplayMenuInit },
{ NAMEKEY_INVALID, "CreditsMenuInit", CreditsMenuInit },
{ NAMEKEY_INVALID, "LanGameOptionsMenuInit", LanGameOptionsMenuInit },
{ NAMEKEY_INVALID, "LanMapSelectMenuInit", LanMapSelectMenuInit },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuInit", SkirmishGameOptionsMenuInit },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuInit", SkirmishMapSelectMenuInit },
{ NAMEKEY_INVALID, "WOLLadderScreenInit", WOLLadderScreenInit },
{ NAMEKEY_INVALID, "WOLLoginMenuInit", WOLLoginMenuInit },
{ NAMEKEY_INVALID, "WOLLocaleSelectInit", WOLLocaleSelectInit },
{ NAMEKEY_INVALID, "WOLLobbyMenuInit", WOLLobbyMenuInit },
{ NAMEKEY_INVALID, "WOLGameSetupMenuInit", WOLGameSetupMenuInit },
{ NAMEKEY_INVALID, "WOLMapSelectMenuInit", WOLMapSelectMenuInit },
{ NAMEKEY_INVALID, "WOLBuddyOverlayInit", WOLBuddyOverlayInit },
{ NAMEKEY_INVALID, "WOLBuddyOverlayRCMenuInit", WOLBuddyOverlayRCMenuInit },
{ NAMEKEY_INVALID, "RCGameDetailsMenuInit", RCGameDetailsMenuInit },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayInit", GameSpyPlayerInfoOverlayInit },
{ NAMEKEY_INVALID, "WOLMessageWindowInit", WOLMessageWindowInit },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuInit", WOLQuickMatchMenuInit },
{ NAMEKEY_INVALID, "WOLWelcomeMenuInit", WOLWelcomeMenuInit },
{ NAMEKEY_INVALID, "WOLStatusMenuInit", WOLStatusMenuInit },
{ NAMEKEY_INVALID, "WOLQMScoreScreenInit", WOLQMScoreScreenInit },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenInit", WOLCustomScoreScreenInit },
{ NAMEKEY_INVALID, "NetworkDirectConnectInit", NetworkDirectConnectInit },
{ NAMEKEY_INVALID, "PopupHostGameInit", PopupHostGameInit },
{ NAMEKEY_INVALID, "PopupJoinGameInit", PopupJoinGameInit },
{ NAMEKEY_INVALID, "PopupLadderSelectInit", PopupLadderSelectInit },
{ NAMEKEY_INVALID, "InGamePopupMessageInit", InGamePopupMessageInit },
{ NAMEKEY_INVALID, "GameInfoWindowInit", GameInfoWindowInit },
{ NAMEKEY_INVALID, "ScoreScreenInit", ScoreScreenInit },
{ NAMEKEY_INVALID, "DownloadMenuInit", DownloadMenuInit },
{ NAMEKEY_INVALID, "DifficultySelectInit", DifficultySelectInit },
{ NAMEKEY_INVALID, "PopupReplayInit", PopupReplayInit },
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
};
// window layout update table ---------------------------------------------------------------------
static FunctionLexicon::TableEntry winLayoutUpdateTable[] =
{
{ NAMEKEY_INVALID, "MainMenuUpdate", MainMenuUpdate },
{ NAMEKEY_INVALID, "OptionsMenuUpdate", OptionsMenuUpdate },
{ NAMEKEY_INVALID, "SinglePlayerMenuUpdate", SinglePlayerMenuUpdate },
{ NAMEKEY_INVALID, "MapSelectMenuUpdate", MapSelectMenuUpdate },
{ NAMEKEY_INVALID, "LanLobbyMenuUpdate", LanLobbyMenuUpdate },
{ NAMEKEY_INVALID, "ReplayMenuUpdate", ReplayMenuUpdate },
{ NAMEKEY_INVALID, "SaveLoadMenuUpdate", SaveLoadMenuUpdate },
{ NAMEKEY_INVALID, "CreditsMenuUpdate", CreditsMenuUpdate },
{ NAMEKEY_INVALID, "LanGameOptionsMenuUpdate", LanGameOptionsMenuUpdate },
{ NAMEKEY_INVALID, "LanMapSelectMenuUpdate", LanMapSelectMenuUpdate },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuUpdate", SkirmishGameOptionsMenuUpdate },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuUpdate", SkirmishMapSelectMenuUpdate },
{ NAMEKEY_INVALID, "WOLLadderScreenUpdate", WOLLadderScreenUpdate },
{ NAMEKEY_INVALID, "WOLLoginMenuUpdate", WOLLoginMenuUpdate },
{ NAMEKEY_INVALID, "WOLLocaleSelectUpdate", WOLLocaleSelectUpdate },
{ NAMEKEY_INVALID, "WOLLobbyMenuUpdate", WOLLobbyMenuUpdate },
{ NAMEKEY_INVALID, "WOLGameSetupMenuUpdate", WOLGameSetupMenuUpdate },
{ NAMEKEY_INVALID, "WOLMapSelectMenuUpdate", WOLMapSelectMenuUpdate },
{ NAMEKEY_INVALID, "WOLBuddyOverlayUpdate", WOLBuddyOverlayUpdate },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayUpdate",GameSpyPlayerInfoOverlayUpdate },
{ NAMEKEY_INVALID, "WOLMessageWindowUpdate", WOLMessageWindowUpdate },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuUpdate", WOLQuickMatchMenuUpdate },
{ NAMEKEY_INVALID, "WOLWelcomeMenuUpdate", WOLWelcomeMenuUpdate },
{ NAMEKEY_INVALID, "WOLStatusMenuUpdate", WOLStatusMenuUpdate },
{ NAMEKEY_INVALID, "WOLQMScoreScreenUpdate", WOLQMScoreScreenUpdate },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenUpdate", WOLCustomScoreScreenUpdate },
{ NAMEKEY_INVALID, "NetworkDirectConnectUpdate", NetworkDirectConnectUpdate },
{ NAMEKEY_INVALID, "ScoreScreenUpdate", ScoreScreenUpdate },
{ NAMEKEY_INVALID, "DownloadMenuUpdate", DownloadMenuUpdate },
{ NAMEKEY_INVALID, "PopupReplayUpdate", PopupReplayUpdate },
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
};
// window layout shutdown table -------------------------------------------------------------------
static FunctionLexicon::TableEntry winLayoutShutdownTable[] =
{
{ NAMEKEY_INVALID, "MainMenuShutdown", MainMenuShutdown },
{ NAMEKEY_INVALID, "OptionsMenuShutdown", OptionsMenuShutdown },
{ NAMEKEY_INVALID, "SaveLoadMenuShutdown", SaveLoadMenuShutdown },
{ NAMEKEY_INVALID, "PopupCommunicatorShutdown", PopupCommunicatorShutdown },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuShutdown", KeyboardOptionsMenuShutdown },
{ NAMEKEY_INVALID, "SinglePlayerMenuShutdown", SinglePlayerMenuShutdown },
{ NAMEKEY_INVALID, "MapSelectMenuShutdown", MapSelectMenuShutdown },
{ NAMEKEY_INVALID, "LanLobbyMenuShutdown", LanLobbyMenuShutdown },
{ NAMEKEY_INVALID, "ReplayMenuShutdown", ReplayMenuShutdown },
{ NAMEKEY_INVALID, "CreditsMenuShutdown", CreditsMenuShutdown },
{ NAMEKEY_INVALID, "LanGameOptionsMenuShutdown", LanGameOptionsMenuShutdown },
{ NAMEKEY_INVALID, "LanMapSelectMenuShutdown", LanMapSelectMenuShutdown },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuShutdown",SkirmishGameOptionsMenuShutdown },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuShutdown", SkirmishMapSelectMenuShutdown },
{ NAMEKEY_INVALID, "WOLLadderScreenShutdown", WOLLadderScreenShutdown },
{ NAMEKEY_INVALID, "WOLLoginMenuShutdown", WOLLoginMenuShutdown },
{ NAMEKEY_INVALID, "WOLLocaleSelectShutdown", WOLLocaleSelectShutdown },
{ NAMEKEY_INVALID, "WOLLobbyMenuShutdown", WOLLobbyMenuShutdown },
{ NAMEKEY_INVALID, "WOLGameSetupMenuShutdown", WOLGameSetupMenuShutdown },
{ NAMEKEY_INVALID, "WOLMapSelectMenuShutdown", WOLMapSelectMenuShutdown },
{ NAMEKEY_INVALID, "WOLBuddyOverlayShutdown", WOLBuddyOverlayShutdown },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayShutdown",GameSpyPlayerInfoOverlayShutdown },
{ NAMEKEY_INVALID, "WOLMessageWindowShutdown", WOLMessageWindowShutdown },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuShutdown", WOLQuickMatchMenuShutdown },
{ NAMEKEY_INVALID, "WOLWelcomeMenuShutdown", WOLWelcomeMenuShutdown },
{ NAMEKEY_INVALID, "WOLStatusMenuShutdown", WOLStatusMenuShutdown },
{ NAMEKEY_INVALID, "WOLQMScoreScreenShutdown", WOLQMScoreScreenShutdown },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenShutdown", WOLCustomScoreScreenShutdown },
{ NAMEKEY_INVALID, "NetworkDirectConnectShutdown", NetworkDirectConnectShutdown },
{ NAMEKEY_INVALID, "ScoreScreenShutdown", ScoreScreenShutdown },
{ NAMEKEY_INVALID, "DownloadMenuShutdown", DownloadMenuShutdown },
{ NAMEKEY_INVALID, "PopupReplayShutdown", PopupReplayShutdown },
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC DATA
///////////////////////////////////////////////////////////////////////////////////////////////////
FunctionLexicon *TheFunctionLexicon = NULL; ///< the function dictionary
//-------------------------------------------------------------------------------------------------
/** Since we have a convenient table to organize our callbacks anyway,
* we'll just use this same storage space to load in any run time
* components we might want to add to the table, such as generating
* a key based off the name supplied in the table for faster access */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::loadTable( TableEntry *table,
TableIndex tableIndex )
{
// sanity
if( table == NULL )
return;
// loop through all entries
TableEntry *entry = table;
while( entry->name )
{
// assign key from name key based on name provided in table
entry->key = TheNameKeyGenerator->nameToKey( AsciiString(entry->name) );
// next table entry please
entry++;
} // end while
// assign table to the index specified
m_tables[ tableIndex ] = table;
} // end loadTable
//-------------------------------------------------------------------------------------------------
/** Search the provided table for a function matching the key */
//-------------------------------------------------------------------------------------------------
void *FunctionLexicon::keyToFunc( NameKeyType key, TableEntry *table )
{
// sanity
if( key == NAMEKEY_INVALID )
return NULL;
// search table for key
TableEntry *entry = table;
while( entry && entry->key != NAMEKEY_INVALID )
{
if( entry->key == key )
return entry->func;
entry++;
} // end if
return NULL; // not found
} // end keyToFunc
//-------------------------------------------------------------------------------------------------
/** Search tables for the function given this key, if the index parameter
* is TABLE_ANY, then ALL tables will be searched. Otherwise index refers
* to only a single table index to be searched */
//-------------------------------------------------------------------------------------------------
void *FunctionLexicon::findFunction( NameKeyType key, TableIndex index )
{
void *func = NULL;
// sanity
if( key == NAMEKEY_INVALID )
return NULL;
// search ALL tables for function if the index paramater allows if
if( index == TABLE_ANY )
{
Int i;
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
{
func = keyToFunc( key, m_tables[ i ] );
if( func )
break; // exit for i
} // end for i
} // end if
else
{
// do NOT search all tables, just the one specified by the parameter
func = keyToFunc( key, m_tables[ index ] );
} // end else
// return function, if found
return func;
} // end findFunction
#ifdef NOT_IN_USE
//-------------------------------------------------------------------------------------------------
/** Search for the function in the specified table */
//-------------------------------------------------------------------------------------------------
const char *FunctionLexicon::funcToName( void *func, TableEntry *table )
{
// sanity
if( func == NULL )
return NULL;
// search the table
TableEntry *entry = table;
while( entry && entry->key != NAMEKEY_INVALID )
{
// is this it
if( entry->func == func )
return entry->name;
// not it, check next
entry++;
} // end while
return NULL; // not found
} // end funcToName
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
FunctionLexicon::FunctionLexicon( void )
{
Int i;
// empty the tables
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
m_tables[ i ] = NULL;
} // end FunctionLexicon
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
FunctionLexicon::~FunctionLexicon( void )
{
} // end ~FunctionLexicon
//-------------------------------------------------------------------------------------------------
/** Initialize our dictionary of funtion pointers and symbols */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::init( void )
{
// if you change this method, check the reset() and make sure it's OK
// initialize the name key identifiers for the lookup table
loadTable( gameWinDrawTable, TABLE_GAME_WIN_DRAW );
loadTable( gameWinSystemTable, TABLE_GAME_WIN_SYSTEM );
loadTable( gameWinInputTable, TABLE_GAME_WIN_INPUT );
loadTable( gameWinTooltipTable, TABLE_GAME_WIN_TOOLTIP );
loadTable( winLayoutInitTable, TABLE_WIN_LAYOUT_INIT );
loadTable( winLayoutUpdateTable, TABLE_WIN_LAYOUT_UPDATE );
loadTable( winLayoutShutdownTable, TABLE_WIN_LAYOUT_SHUTDOWN );
validate();
} // end init
//-------------------------------------------------------------------------------------------------
/** reset */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::reset( void )
{
//
// make sure the ordering of what happens here with respect to derived classes resets is
// all OK since we're cheating and using the init() method
//
// nothing dynamically loaded, just reinit the tables
init();
} // end reset
//-------------------------------------------------------------------------------------------------
/** Update */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::update( void )
{
} // end update
/*
// !NOTE! We can not have this function, see the header for
// more information as to why
//
//-------------------------------------------------------------------------------------------------
// translate a function pointer to its symbolic name
//-------------------------------------------------------------------------------------------------
char *FunctionLexicon::functionToName( void *func )
{
// sanity
if( func == NULL )
return NULL;
// search ALL the tables
Int i;
char *name = NULL;
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
{
name = funcToName( func, m_tables[ i ] );
if( name )
return name;
} // end for i
return NULL; // not found
} // end functionToName
*/
//-------------------------------------------------------------------------------------------------
/** Scan the tables and make sure that each function address is unique.
* We want to do this to prevent accidental entries of two identical
* functions and because the compiler will optimize identical functions
* to the same address (typically in empty functions with no body and the
* same parameters) which we MUST keep separate for when we add code to
* them */
//-------------------------------------------------------------------------------------------------
Bool FunctionLexicon::validate( void )
{
Bool valid = TRUE;
Int i, j;
TableEntry *sourceEntry, *lookAtEntry;
// scan all talbes
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
{
// scan through this table
sourceEntry = m_tables[ i ];
while( sourceEntry && sourceEntry->key != NAMEKEY_INVALID )
{
//
// scan all tables looking for the function in sourceEntry, do not bother
// of source entry is NULL (a valid entry in the table, but not a function)
//
if( sourceEntry->func )
{
// scan all tables
for( j = 0; j < MAX_FUNCTION_TABLES; j++ )
{
// scan all entries in this table
lookAtEntry = m_tables[ j ];
while( lookAtEntry && lookAtEntry->key != NAMEKEY_INVALID )
{
//
// check for match, do not match the entry source with itself
//
if( sourceEntry != lookAtEntry )
if( sourceEntry->func == lookAtEntry->func )
{
DEBUG_LOG(( "WARNING! Function lexicon entries match same address! '%s' and '%s'\n",
sourceEntry->name, lookAtEntry->name ));
valid = FALSE;
} // end if
// next entry in this target table
lookAtEntry++;
} // end while
} // end for j
} // end if
// next source entry
sourceEntry++;
} // end while
} // end for i
// return the valid state of our tables
return valid;
} // end validate
//============================================================================
// FunctionLexicon::gameWinDrawFunc
//============================================================================
GameWinDrawFunc FunctionLexicon::gameWinDrawFunc( NameKeyType key, TableIndex index )
{
if ( index == TABLE_ANY )
{
// first search the device depended table then the device independent table
GameWinDrawFunc func;
func = (GameWinDrawFunc)findFunction( key, TABLE_GAME_WIN_DEVICEDRAW );
if ( func == NULL )
{
func = (GameWinDrawFunc)findFunction( key, TABLE_GAME_WIN_DRAW );
}
return func;
}
// search the specified table
return (GameWinDrawFunc)findFunction( key, index );
}
WindowLayoutInitFunc FunctionLexicon::winLayoutInitFunc( NameKeyType key, TableIndex index )
{
if ( index == TABLE_ANY )
{
// first search the device depended table then the device independent table
WindowLayoutInitFunc func;
func = (WindowLayoutInitFunc)findFunction( key, TABLE_WIN_LAYOUT_DEVICEINIT );
if ( func == NULL )
{
func = (WindowLayoutInitFunc)findFunction( key, TABLE_WIN_LAYOUT_INIT );
}
return func;
}
// search the specified table
return (WindowLayoutInitFunc)findFunction( key, index );
}

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/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// GameCommon.h
// Part of header detangling
// John McDonald, Aug 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameCommon.h"
const char *TheVeterancyNames[] =
{
"REGULAR",
"VETERAN",
"ELITE",
"HEROIC",
NULL
};
const char *TheRelationshipNames[] =
{
"ENEMIES",
"NEUTRAL",
"ALLIES",
NULL
};
//-------------------------------------------------------------------------------------------------
Real normalizeAngle(Real angle)
{
DEBUG_ASSERTCRASH(!_isnan(angle), ("Angle is NAN in normalizeAngle!\n"));
if( _isnan(angle) )
return 0;// ARGH!!!! Don't assert and then not handle it! Error bad! Fix error!
while (angle > PI)
angle -= 2*PI;
while (angle <= -PI)
angle += 2*PI;
return angle;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// GameType.cpp ///////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
char *TimeOfDayNames[] =
{
"NONE",
"MORNING",
"AFTERNOON",
"EVENING",
"NIGHT",
NULL
};
char *WeatherNames[] =
{
"NORMAL",
"SNOWY",
NULL
};

View file

@ -0,0 +1,558 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Geometry.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Steven Johnson, Aug 2002
// Desc:
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_GEOMETRY_NAMES
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "Common/Geometry.h"
#include "Common/INI.h"
#include "Common/RandomValue.h"
#include "Common/Xfer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryType( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_type = (GeometryType)INI::scanIndexList(ini->getNextToken(), GeometryNames);
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryIsSmall( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_isSmall = INI::scanBool(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryHeight( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_height = INI::scanReal(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryMajorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_majorRadius = INI::scanReal(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryMinorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_minorRadius = INI::scanReal(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//-----------------------------------------------------------------------------
void GeometryInfo::set(GeometryType type, Bool isSmall, Real height, Real majorRadius, Real minorRadius)
{
m_type = type;
m_isSmall = isSmall;
switch(m_type)
{
case GEOMETRY_SPHERE:
m_majorRadius = majorRadius;
m_minorRadius = majorRadius;
m_height = majorRadius;
break;
case GEOMETRY_CYLINDER:
m_majorRadius = majorRadius;
m_minorRadius = majorRadius;
m_height = height;
break;
case GEOMETRY_BOX:
m_majorRadius = majorRadius;
m_minorRadius = minorRadius;
m_height = height;
break;
};
calcBoundingStuff();
}
//-----------------------------------------------------------------------------
static Real calcDotProduct(const Coord3D& a, const Coord3D& b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
//-----------------------------------------------------------------------------
static Real calcDistSquared(const Coord3D& a, const Coord3D& b)
{
return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
}
//-----------------------------------------------------------------------------
static Real calcPointToLineDistSquared(const Coord3D& pt, const Coord3D& lineStart, const Coord3D& lineEnd)
{
Coord3D line, lineToPt, closest;
line.x = lineEnd.x - lineStart.x;
line.y = lineEnd.y - lineStart.y;
line.z = lineEnd.z - lineStart.z;
lineToPt.x = pt.x - lineStart.x;
lineToPt.y = pt.y - lineStart.y;
lineToPt.z = pt.z - lineStart.z;
Real dot = calcDotProduct(lineToPt, line);
if (dot <= 0.0f)
{
// angle is obtuse
return calcDistSquared(pt, lineStart);
}
Real lineLenSqr = calcDistSquared(lineStart, lineEnd);
DEBUG_ASSERTCRASH(lineLenSqr==calcDotProduct(line,line),("hmm"));
if (lineLenSqr <= dot)
{
return calcDistSquared(pt, lineEnd);
}
Real tmp = dot / lineLenSqr;
closest.x = lineStart.x + tmp * line.x;
closest.y = lineStart.y + tmp * line.y;
closest.z = lineStart.z + tmp * line.z;
return calcDistSquared(pt, closest);
}
//=============================================================================
Bool GeometryInfo::isIntersectedByLineSegment(const Coord3D& loc, const Coord3D& from, const Coord3D& to) const
{
DEBUG_CRASH(("this call does not work properly for nonspheres yet. use with caution."));
/// @todo srj -- treats everything as a sphere for now. fix.
Real distSquared = calcPointToLineDistSquared(loc, from, to);
return distSquared <= sqr(getBoundingSphereRadius());
}
//=============================================================================
// given an object with this geom, located at 'pos', and another obj with the given
// pos & geom, calc the min and max pitches from this to that.
void GeometryInfo::calcPitches(const Coord3D& thisPos, const GeometryInfo& that, const Coord3D& thatPos,
Real& minPitch, Real& maxPitch) const
{
Coord3D thisCenter;
getCenterPosition(thisPos, thisCenter);
Real dxy = sqrt(sqr(thatPos.x - thisCenter.x) + sqr(thatPos.y - thisCenter.y));
Real dz;
/** @todo srj -- this could be better, by calcing it for all the corners, not just top-center
and bottom-center... oh well */
dz = (thatPos.z + that.getMaxHeightAbovePosition()) - thisCenter.z;
maxPitch = atan2(dz, dxy);
dz = (thatPos.z - that.getMaxHeightBelowPosition()) - thisCenter.z;
minPitch = atan2(dz, dxy);
}
//=============================================================================
// given an object with this geom, SET how far above the object's canonical position its max z should extend.
void GeometryInfo::setMaxHeightAbovePosition(Real z)
{
switch(m_type)
{
case GEOMETRY_SPHERE:
m_majorRadius = z;
break;
case GEOMETRY_BOX:
case GEOMETRY_CYLINDER:
m_height = z;
break;
};
calcBoundingStuff();
}
//=============================================================================
// given an object with this geom, how far above the object's canonical position does its max z extend?
Real GeometryInfo::getMaxHeightAbovePosition() const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
return m_majorRadius;
case GEOMETRY_BOX:
case GEOMETRY_CYLINDER:
return m_height;
};
return 0.0f;
}
//=============================================================================
// given an object with this geom, how far below the object's canonical position does its max z extend?
Real GeometryInfo::getMaxHeightBelowPosition() const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
return m_majorRadius;
case GEOMETRY_BOX:
case GEOMETRY_CYLINDER:
return 0.0f;
};
return 0.0f;
}
//=============================================================================
// given an object with this geom, located at 'pos', where is the "center" of the geometry?
Real GeometryInfo::getZDeltaToCenterPosition() const
{
return (m_type == GEOMETRY_SPHERE) ? 0.0f : (m_height * 0.5f);
}
//=============================================================================
// given an object with this geom, located at 'pos', where is the "center" of the geometry?
void GeometryInfo::getCenterPosition(const Coord3D& pos, Coord3D& center) const
{
center = pos;
center.z += getZDeltaToCenterPosition();
}
//=============================================================================
void GeometryInfo::expandFootprint(Real radius)
{
m_majorRadius += radius;
m_minorRadius += radius;
calcBoundingStuff();
}
//=============================================================================
void GeometryInfo::get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
bounds.lo.x = geomCenter.x - m_majorRadius;
bounds.lo.y = geomCenter.y - m_majorRadius;
bounds.hi.x = geomCenter.x + m_majorRadius;
bounds.hi.y = geomCenter.y + m_majorRadius;
break;
}
case GEOMETRY_BOX:
{
Real c = (Real)cos(angle);
Real s = (Real)sin(angle);
Real exc = m_majorRadius*c;
Real eyc = m_minorRadius*c;
Real exs = m_majorRadius*s;
Real eys = m_minorRadius*s;
Real x,y;
x = geomCenter.x - exc - eys;
y = geomCenter.y + eyc - exs;
bounds.lo.x = x;
bounds.lo.y = y;
bounds.hi.x = x;
bounds.hi.y = y;
x = geomCenter.x + exc - eys;
y = geomCenter.y + eyc + exs;
if (bounds.lo.x > x) bounds.lo.x = x;
if (bounds.lo.y > y) bounds.lo.y = y;
if (bounds.hi.x < x) bounds.hi.x = x;
if (bounds.hi.y < y) bounds.hi.y = y;
x = geomCenter.x + exc + eys;
y = geomCenter.y - eyc + exs;
if (bounds.lo.x > x) bounds.lo.x = x;
if (bounds.lo.y > y) bounds.lo.y = y;
if (bounds.hi.x < x) bounds.hi.x = x;
if (bounds.hi.y < y) bounds.hi.y = y;
x = geomCenter.x - exc + eys;
y = geomCenter.y - eyc - exs;
if (bounds.lo.x > x) bounds.lo.x = x;
if (bounds.lo.y > y) bounds.lo.y = y;
if (bounds.hi.x < x) bounds.hi.x = x;
if (bounds.hi.y < y) bounds.hi.y = y;
break;
}
}
}
//=============================================================================
void GeometryInfo::clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
Real dx = ptToClip.x - geomCenter.x;
Real dy = ptToClip.y - geomCenter.y;
Real radius = sqrt(sqr(dx) + sqr(dy));
if (radius > m_majorRadius)
{
Real ratio = m_majorRadius / radius;
ptToClip.x = geomCenter.x + dx * ratio;
ptToClip.y = geomCenter.y + dy * ratio;
}
break;
}
case GEOMETRY_BOX:
{
ptToClip.x = clamp(geomCenter.x - m_majorRadius, ptToClip.x, geomCenter.x + m_majorRadius);
ptToClip.y = clamp(geomCenter.y - m_minorRadius, ptToClip.y, geomCenter.y + m_minorRadius);
break;
}
};
}
//=============================================================================
inline Bool isWithin(Real a, Real b, Real c) { return a<=b && b<=c; }
//=============================================================================
Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D& pt) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
Real dx = pt.x - geomCenter.x;
Real dy = pt.y - geomCenter.y;
Real radius = sqrt(sqr(dx) + sqr(dy));
return (radius <= m_majorRadius);
break;
}
case GEOMETRY_BOX:
{
return isWithin(geomCenter.x - m_majorRadius, pt.x, geomCenter.x + m_majorRadius) &&
isWithin(geomCenter.y - m_minorRadius, pt.y, geomCenter.y + m_minorRadius);
}
};
return false;
}
//=============================================================================
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
#if 1
// this is a better technique than the more obvious radius-and-angle
// one, below, because the latter tends to clump more towards the center.
Real maxDistSqr = sqr(m_majorRadius);
Real distSqr;
do
{
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.z = 0.0f;
distSqr = sqr(pt.x) + sqr(pt.y);
} while (distSqr > maxDistSqr);
#else
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
Real angle = GameLogicRandomValueReal(-PI, PI);
pt.x = radius * Cos(angle);
pt.y = radius * Sin(angle);
pt.z = 0.0f;
#endif
break;
}
case GEOMETRY_BOX:
{
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
pt.z = 0.0f;
break;
}
};
}
//=============================================================================
Real GeometryInfo::getFootprintArea() const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
return PI * sqr(m_boundingCircleRadius);
}
case GEOMETRY_BOX:
{
return 4.0f * m_majorRadius * m_minorRadius;
}
};
DEBUG_CRASH(("should never get here"));
return 0.0f;
}
//=============================================================================
void GeometryInfo::calcBoundingStuff()
{
switch(m_type)
{
case GEOMETRY_SPHERE:
{
m_boundingSphereRadius = m_majorRadius;
m_boundingCircleRadius = m_majorRadius;
break;
}
case GEOMETRY_CYLINDER:
{
m_boundingCircleRadius = m_majorRadius;
m_boundingSphereRadius = m_height*0.5;
if (m_boundingSphereRadius < m_majorRadius)
m_boundingSphereRadius = m_majorRadius;
break;
}
case GEOMETRY_BOX:
{
m_boundingCircleRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius));
m_boundingSphereRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius) + sqr(m_height*0.5));
break;
}
};
}
#if defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
void GeometryInfo::tweakExtents(ExtentModType extentModType, Real extentModAmount)
{
switch(extentModType)
{
case EXTENTMOD_HEIGHT:
m_height += extentModAmount;
break;
case EXTENTMOD_MAJOR:
m_majorRadius += extentModAmount;
break;
case EXTENTMOD_MINOR:
m_minorRadius += extentModAmount;
break;
case EXTENTMOD_TYPE:
m_type = (GeometryType)((m_type + ((extentModType == EXTENTMOD_TYPE)?1:0)) % GEOMETRY_NUM_TYPES);
break;
}
m_isSmall = false;
calcBoundingStuff();
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
AsciiString GeometryInfo::getDescriptiveString() const
{
AsciiString msg;
msg.format("%d/%d(%g %g %g)", (Int)m_type, (Int)m_isSmall, m_majorRadius, m_minorRadius, m_height);
return msg;
}
#endif
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void GeometryInfo::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void GeometryInfo::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// type
xfer->xferUser( &m_type, sizeof( GeometryType ) );
// is small
xfer->xferBool( &m_isSmall );
// height
xfer->xferReal( &m_height );
// major radius
xfer->xferReal( &m_majorRadius );
// minor radius
xfer->xferReal( &m_minorRadius );
// bouncing circle radius
xfer->xferReal( &m_boundingCircleRadius );
// bounding sphere radius
xfer->xferReal( &m_boundingSphereRadius );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void GeometryInfo::loadPostProcess( void )
{
} // end loadPostProcess

View file

@ -0,0 +1,137 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// Kindof.cpp /////////////////////////////////////////////////////////////////////////////////////
// Part of header detangling
// John McDonald, Aug 2002
#include "PreRTS.h"
#include "Common/KindOf.h"
#include "Common/BitFlagsIO.h"
const char* KindOfMaskType::s_bitNameList[] =
{
"OBSTACLE",
"SELECTABLE",
"IMMOBILE",
"CAN_ATTACK",
"STICK_TO_TERRAIN_SLOPE",
"CAN_CAST_REFLECTIONS",
"SHRUBBERY",
"STRUCTURE",
"INFANTRY",
"VEHICLE",
"AIRCRAFT",
"HUGE_VEHICLE",
"DOZER",
"HARVESTER",
"COMMANDCENTER",
#ifdef ALLOW_SURRENDER
"PRISON",
"COLLECTS_PRISON_BOUNTY",
"POW_TRUCK",
#endif
"LINEBUILD",
"SALVAGER",
"WEAPON_SALVAGER",
"TRANSPORT",
"BRIDGE",
"LANDMARK_BRIDGE",
"BRIDGE_TOWER",
"PROJECTILE",
"PRELOAD",
"NO_GARRISON",
"WAVEGUIDE",
"WAVE_EFFECT",
"NO_COLLIDE",
"REPAIR_PAD",
"HEAL_PAD",
"STEALTH_GARRISON",
"CASH_GENERATOR",
"AIRFIELD",
"DRAWABLE_ONLY",
"MP_COUNT_FOR_VICTORY",
"REBUILD_HOLE",
"SCORE",
"SCORE_CREATE",
"SCORE_DESTROY",
"NO_HEAL_ICON",
"CAN_RAPPEL",
"PARACHUTABLE",
#ifdef ALLOW_SURRENDER
"CAN_SURRENDER",
#endif
"CAN_BE_REPULSED",
"MOB_NEXUS",
"IGNORED_IN_GUI",
"CRATE",
"CAPTURABLE",
"CLEARED_BY_BUILD",
"SMALL_MISSILE",
"ALWAYS_VISIBLE",
"UNATTACKABLE",
"MINE",
"CLEANUP_HAZARD",
"PORTABLE_STRUCTURE",
"ALWAYS_SELECTABLE",
"ATTACK_NEEDS_LINE_OF_SIGHT",
"WALK_ON_TOP_OF_WALL",
"DEFENSIVE_WALL",
"FS_POWER",
"FS_FACTORY",
"FS_BASE_DEFENSE",
"FS_TECHNOLOGY",
"AIRCRAFT_PATH_AROUND",
"LOW_OVERLAPPABLE",
"FORCEATTACKABLE",
"AUTO_RALLYPOINT",
"TECH_BUILDING",
"POWERED",
"PRODUCED_AT_HELIPAD",
"DRONE",
"CAN_SEE_THROUGH_STRUCTURE",
"BALLISTIC_MISSILE",
"CLICK_THROUGH",
"SUPPLY_SOURCE_ON_PREVIEW",
"PARACHUTE",
"GARRISONABLE_UNTIL_DESTROYED",
"BOAT",
"IMMUNE_TO_CAPTURE",
"HULK",
"SHOW_PORTRAIT_WHEN_CONTROLLED",
"SPAWNS_ARE_THE_WEAPONS",
"CANNOT_BUILD_NEAR_SUPPLIES",
"SUPPLY_SOURCE",
"REVEAL_TO_ALL",
"DISGUISER",
"INERT",
"HERO",
"IGNORES_SELECT_ALL",
"DONT_AUTO_CRUSH_INFANTRY",
NULL
};
KindOfMaskType KINDOFMASK_NONE; // inits to all zeroes

View file

@ -0,0 +1,451 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: List
//
// File name: WSYS_List.cpp
//
// Created: 10/31/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/List.h"
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// LList::LList
//============================================================================
LList::LList( )
: m_sortMode(DESCENDING)
{
m_head.setItem( &m_head.m_item);
};
//=================================================================
// LList::add
//=================================================================
void LList::add( LListNode* new_node )
{
LListNode* node;
Int pri;
if ( m_addToEndOfGroup )
{
pri = new_node->priority();
node = &m_head;
while( (node = node->prev() ))
{
if( (m_sortMode == ASCENDING && node->priority() >= pri)
|| (m_sortMode == DESCENDING && node->priority() <= pri) )
{
node->append( new_node );
return;
}
}
m_head.append( new_node );
}
else
{
pri = new_node->priority();
node = &m_head;
while( (node = node->next() ))
{
if( (m_sortMode == ASCENDING && node->priority() <= pri)
|| (m_sortMode == DESCENDING && node->priority() >= pri) )
{
node->insert( new_node );
return;
}
}
m_head.insert( new_node );
}
}
//============================================================================
// LList::addGDFNode
//============================================================================
void LList::addItem( Int pri, void* item )
{
LListNode *node = NEW LListNode(); // poolify
if ( node )
{
node->setPriority( pri );
node->setItem( item );
node->autoDelete();
add( node );
}
}
//============================================================================
// LList::addGDFNodeToHead
//============================================================================
void LList::addItemToHead( void *item )
{
LListNode *node = NEW LListNode();
if ( node )
{
node->setItem( item );
node->autoDelete();
addToHead( node );
}
}
//============================================================================
// LList::addGDFNodeToTail
//============================================================================
void LList::addItemToTail( void *item )
{
LListNode *node = NEW LListNode();
if ( node )
{
node->setItem( item );
node->autoDelete();
addToTail( node );
}
}
//============================================================================
// LList::Clear
//============================================================================
void LList::clear( void )
{
LListNode *node;
while ( (node = firstNode()) != NULL )
{
node->remove();
node->destroy();
}
}
//=================================================================
// LList::nodeCount
//=================================================================
Int LList::nodeCount( void )
{
LListNode* node;
Int count = 0;
node = firstNode();
while(node)
{
count++;
node = node->next();
}
return count;
}
//=================================================================
// LList::getNode
//=================================================================
LListNode* LList::getNode( Int index )
{
LListNode* node;
node = firstNode();
while( node && index >= 0 )
{
if( index-- == 0 )
{
return node;
}
node = node->next();
}
return NULL;
}
//============================================================================
// LList::merge
//============================================================================
void LList::merge( LList *list )
{
if ( list == NULL || list->isEmpty() )
{
return;
}
m_head.m_prev->m_next = list->m_head.m_next;
list->m_head.m_next->m_prev = m_head.m_prev;
list->m_head.m_prev->m_next = &m_head;
m_head.m_prev = list->m_head.m_prev;
list->m_head.m_next = &list->m_head;
list->m_head.m_prev = &list->m_head;
}
//============================================================================
// LList::hasReference
//============================================================================
Bool LList::hasItem( void *item )
{
return findItem( item ) != NULL;
}
//============================================================================
// LList::findReference
//============================================================================
LListNode* LList::findItem( void *item )
{
LListNode* node;
node = firstNode();
while( node )
{
if( node->item() == item )
{
return node;
}
node = node->next();
}
return NULL;
}
//============================================================================
// LListNode::LListNode
//============================================================================
LListNode::LListNode()
: m_pri(0),
m_item(NULL),
m_autoDelete(FALSE)
{
m_next = m_prev = this;
};
//=================================================================
// LListNode::insert
//=================================================================
void LListNode::insert( LListNode* new_node )
{
new_node->m_prev = m_prev;
new_node->m_next = this;
m_prev = new_node;
new_node->m_prev->m_next = new_node;
}
//=================================================================
// LListNode::append
//=================================================================
void LListNode::append( LListNode* new_node )
{
new_node->m_prev = this;
new_node->m_next = m_next;
this->m_next = new_node;
new_node->m_next->m_prev = new_node;
}
//=================================================================
// LListNode::remove
//=================================================================
void LListNode::remove( void )
{
m_prev->m_next = m_next;
m_next->m_prev = m_prev;
m_prev = m_next = this; // so we know that the node is not in a list
}
//=================================================================
// LListNode::next
//=================================================================
LListNode* LListNode::next( void )
{
if( m_next->isHead( ))
{
return NULL;
}
return m_next;
}
//=================================================================
// LListNode::prev
//=================================================================
LListNode* LListNode::prev( void )
{
if( m_prev->isHead())
{
return NULL;
}
return m_prev;
}
//=================================================================
// LListNode::loopNext
//=================================================================
LListNode* LListNode::loopNext( void )
{
LListNode* next;
next = m_next;
if( next->isHead( ))
{
// skip head node
next = next->m_next;
if( next->isHead( ))
{
return NULL; // it is an empty list
}
}
return next;
}
//=================================================================
// LListNode::loopPrev
//=================================================================
LListNode* LListNode::loopPrev( void )
{
LListNode* prev;
prev = m_prev;
if( prev->isHead())
{
// skip head node
prev = prev->m_prev;
if( prev->isHead())
{
return NULL; // it is an empty list
}
}
return prev;
}
//============================================================================
// LListNode::destroy
//============================================================================
void LListNode::destroy( void )
{
if ( m_autoDelete )
{
delete this;
}
}
//============================================================================
// LList::addToEndOfGroup
//============================================================================
void LList::addToEndOfGroup( Bool yes )
{
m_addToEndOfGroup = yes;
}

View file

@ -0,0 +1,627 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO_
//
// File name: IO_LocalFile.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <ctype.h>
#include "Common/LocalFile.h"
#include "Common/RAMFile.h"
#include "Lib/BaseType.h"
#include "Common/PerfTimer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
static Int s_totalOpen = 0;
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//=================================================================
// LocalFile::LocalFile
//=================================================================
LocalFile::LocalFile()
#ifdef USE_BUFFERED_IO
: m_file(NULL)
#else
: m_handle(-1)
#endif
{
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//=================================================================
// LocalFile::~LocalFile
//=================================================================
LocalFile::~LocalFile()
{
#ifdef USE_BUFFERED_IO
if (m_file)
{
fclose(m_file);
m_file = NULL;
--s_totalOpen;
}
#else
if( m_handle != -1 )
{
_close( m_handle );
m_handle = -1;
--s_totalOpen;
}
#endif
File::close();
}
//=================================================================
// LocalFile::open
//=================================================================
/**
* This function opens a file using the standard C open() call. Access flags
* are mapped to the appropriate open flags. Returns true if file was opened
* successfully.
*/
//=================================================================
//DECLARE_PERF_TIMER(LocalFile)
Bool LocalFile::open( const Char *filename, Int access )
{
//USE_PERF_TIMER(LocalFile)
if( !File::open( filename, access) )
{
return FALSE;
}
/* here we translate WSYS file access to the std C equivalent */
#ifdef USE_BUFFERED_IO
char mode[32];
char* m = mode;
if (m_access & APPEND)
{
DEBUG_CRASH(("not yet supported by buffered mode"));
}
if (m_access & TRUNCATE)
{
DEBUG_CRASH(("not yet supported by buffered mode"));
}
if((m_access & READWRITE ) == READWRITE )
{
if (m_access & CREATE)
{
*m++ = 'w';
*m++ = '+';
}
else
{
*m++ = 'r';
*m++ = '+';
}
}
else if(m_access & WRITE)
{
*m++ = 'w';
}
else
{
*m++ = 'r';
DEBUG_ASSERTCRASH(!(m_access & TRUNCATE), ("cannot truncate with read-only"));
}
if (m_access & TEXT)
{
*m++ = 't';
}
if (m_access & BINARY)
{
*m++ = 'b';
}
*m++ = 0;
m_file = fopen(filename, mode);
if (m_file == NULL)
{
goto error;
}
//setvbuf(m_file, m_vbuf, _IOFBF, sizeof(BUF_SIZE));
#else
int flags = 0;
if (m_access & CREATE)
{
flags |= _O_CREAT;
}
if (m_access & TRUNCATE)
{
flags |= _O_TRUNC;
}
if (m_access & APPEND)
{
flags |= _O_APPEND;
}
if (m_access & TEXT)
{
flags |= _O_TEXT;
}
if (m_access & BINARY)
{
flags |= _O_BINARY;
}
if((m_access & READWRITE )== READWRITE )
{
flags |= _O_RDWR;
}
else if(m_access & WRITE)
{
flags |= _O_WRONLY;
flags |= _O_CREAT;
}
else
{
flags |= _O_RDONLY;
}
m_handle = _open( filename, flags , _S_IREAD | _S_IWRITE);
if( m_handle == -1 )
{
goto error;
}
#endif
++s_totalOpen;
/// DEBUG_LOG(("LocalFile::open %s (total %d)\n",filename,s_totalOpen));
if ( m_access & APPEND )
{
if ( seek ( 0, END ) < 0 )
{
goto error;
}
}
return TRUE;
error:
close();
return FALSE;
}
//=================================================================
// LocalFile::close
//=================================================================
/**
* Closes the current file if it is open.
* Must call LocalFile::close() for each successful LocalFile::open() call.
*/
//=================================================================
void LocalFile::close( void )
{
File::close();
}
//=================================================================
// LocalFile::read
//=================================================================
Int LocalFile::read( void *buffer, Int bytes )
{
//USE_PERF_TIMER(LocalFile)
if( !m_open )
{
return -1;
}
if (buffer == NULL)
{
#ifdef USE_BUFFERED_IO
fseek(m_file, bytes, SEEK_CUR);
#else
_lseek(m_handle, bytes, SEEK_CUR);
#endif
return bytes;
}
#ifdef USE_BUFFERED_IO
Int ret = fread(buffer, 1, bytes, m_file);
#else
Int ret = _read( m_handle, buffer, bytes );
#endif
return ret;
}
//=================================================================
// LocalFile::write
//=================================================================
Int LocalFile::write( const void *buffer, Int bytes )
{
if( !m_open || !buffer )
{
return -1;
}
#ifdef USE_BUFFERED_IO
Int ret = fwrite(buffer, 1, bytes, m_file);
#else
Int ret = _write( m_handle, buffer, bytes );
#endif
return ret;
}
//=================================================================
// LocalFile::seek
//=================================================================
Int LocalFile::seek( Int pos, seekMode mode)
{
int lmode;
switch( mode )
{
case START:
lmode = SEEK_SET;
break;
case CURRENT:
lmode = SEEK_CUR;
break;
case END:
DEBUG_ASSERTCRASH(pos <= 0, ("LocalFile::seek - pos should be <= 0 for a seek starting at the end of the file"));
lmode = SEEK_END;
break;
default:
// bad seek mode
return -1;
}
#ifdef USE_BUFFERED_IO
Int ret = fseek(m_file, pos, lmode);
if (ret == 0)
return ftell(m_file);
else
return -1;
#else
Int ret = _lseek( m_handle, pos, lmode );
#endif
return ret;
}
//=================================================================
// LocalFile::scanInt
//=================================================================
// skips preceding whitespace and stops at the first non-number
// or at EOF
Bool LocalFile::scanInt(Int &newInt)
{
newInt = 0;
AsciiString tempstr;
Char c;
Int val;
// skip preceding non-numeric characters
do {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read( m_handle, &c, 1);
#endif
} while ((val != 0) && (((c < '0') || (c > '9')) && (c != '-')));
if (val == 0) {
return FALSE;
}
do {
tempstr.concat(c);
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read( m_handle, &c, 1);
#endif
} while ((val != 0) && ((c >= '0') && (c <= '9')));
// put the last read char back, since we didn't use it.
if (val != 0) {
#ifdef USE_BUFFERED_IO
fseek(m_file, -1, SEEK_CUR);
#else
_lseek(m_handle, -1, SEEK_CUR);
#endif
}
newInt = atoi(tempstr.str());
return TRUE;
}
//=================================================================
// LocalFile::scanReal
//=================================================================
// skips preceding whitespace and stops at the first non-number
// or at EOF
Bool LocalFile::scanReal(Real &newReal)
{
newReal = 0.0;
AsciiString tempstr;
Char c;
Int val;
Bool sawDec = FALSE;
// skip the preceding white space
do {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read( m_handle, &c, 1);
#endif
} while ((val != 0) && (((c < '0') || (c > '9')) && (c != '-') && (c != '.')));
if (val == 0) {
return FALSE;
}
do {
tempstr.concat(c);
if (c == '.') {
sawDec = TRUE;
}
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} while ((val != 0) && (((c >= '0') && (c <= '9')) || ((c == '.') && !sawDec)));
if (val != 0) {
#ifdef USE_BUFFERED_IO
fseek(m_file, -1, SEEK_CUR);
#else
_lseek(m_handle, -1, SEEK_CUR);
#endif
}
newReal = atof(tempstr.str());
return TRUE;
}
//=================================================================
// LocalFile::scanString
//=================================================================
// skips preceding whitespace and stops at the first whitespace
// or at EOF
Bool LocalFile::scanString(AsciiString &newString)
{
Char c;
Int val;
newString.clear();
// skip the preceding whitespace
do {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} while ((val != 0) && (isspace(c)));
if (val == 0) {
return FALSE;
}
do {
newString.concat(c);
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} while ((val != 0) && (!isspace(c)));
if (val != 0) {
#ifdef USE_BUFFERED_IO
fseek(m_file, -1, SEEK_CUR);
#else
_lseek(m_handle, -1, SEEK_CUR);
#endif
}
return TRUE;
}
//=================================================================
// LocalFile::nextLine
//=================================================================
// scans to the first character after a new-line or at EOF
void LocalFile::nextLine(Char *buf, Int bufSize)
{
Char c = 0;
Int val;
Int i = 0;
// seek to the next new-line.
do {
if ((buf == NULL) || (i >= (bufSize-1))) {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} else {
#ifdef USE_BUFFERED_IO
val = fread(buf + i, 1, 1, m_file);
#else
val = _read(m_handle, buf + i, 1);
#endif
c = buf[i];
}
++i;
} while ((val != 0) && (c != '\n'));
if (buf != NULL) {
if (i < bufSize) {
buf[i] = 0;
} else {
buf[bufSize] = 0;
}
}
}
//=================================================================
//=================================================================
File* LocalFile::convertToRAMFile()
{
RAMFile *ramFile = newInstance( RAMFile );
if (ramFile->open(this))
{
if (this->m_deleteOnClose)
{
ramFile->deleteOnClose();
this->close(); // is deleteonclose, so should delete.
}
else
{
this->close();
this->deleteInstance();
}
return ramFile;
}
else
{
ramFile->close();
ramFile->deleteInstance();
return this;
}
}
//=================================================================
// LocalFile::readEntireAndClose
//=================================================================
/**
Allocate a buffer large enough to hold entire file, read
the entire file into the buffer, then close the file.
the buffer is owned by the caller, who is responsible
for freeing is (via delete[]). This is a Good Thing to
use because it minimizes memory copies for BIG files.
*/
char* LocalFile::readEntireAndClose()
{
UnsignedInt fileSize = size();
char* buffer = NEW char[fileSize];
read(buffer, fileSize);
close();
return buffer;
}

View file

@ -0,0 +1,103 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Game Engine
//
// Module: IO
//
// File name: LocalFileSystem.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/LocalFileSystem.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
LocalFileSystem *TheLocalFileSystem = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
void LocalFileSystem::init() {
}
void LocalFileSystem::reset() {
}
void LocalFileSystem::update() {
}

View file

@ -0,0 +1,754 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: MemoryInit.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: MemoryInit.cpp
//
// Created: Steven Johnson, August 2001
//
// Desc: Memory manager
//
// ----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
// SYSTEM INCLUDES
// USER INCLUDES
#include "Lib/BaseType.h"
#include "Common/GameMemory.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
void userMemoryManagerGetDmaParms(Int *numSubPools, const PoolInitRec **pParms)
{
static const PoolInitRec defaultDMA[7] =
{
// name, allocsize, initialcount, overflowcount
{ "dmaPool_16", 16, 65536, 1024 },
{ "dmaPool_32", 32, 150000, 1024 },
{ "dmaPool_64", 64, 60000, 1024 },
{ "dmaPool_128", 128, 32768, 1024 },
{ "dmaPool_256", 256, 8192, 1024 },
{ "dmaPool_512", 512, 8192, 1024 },
{ "dmaPool_1024", 1024, 24000, 1024 }
};
*numSubPools = 7;
*pParms = defaultDMA;
}
//-----------------------------------------------------------------------------
struct PoolSizeRec
{
const char* name;
Int initial;
Int overflow;
};
//-----------------------------------------------------------------------------
// And please be careful of duplicates. They are not rejected.
// not const -- we might override from INI
static PoolSizeRec sizes[] =
{
{ "PartitionContactListNode", 2048, 512 },
{ "BattleshipUpdate", 32, 32 },
{ "FlyToDestAndDestroyUpdate", 32, 32 },
{ "MusicTrack", 32, 32 },
{ "PositionalSoundPool", 32, 32 },
{ "GameMessage", 2048, 32 },
{ "NameKeyBucketPool", 4096, 32 },
{ "ObjectSellInfo", 16, 16 },
{ "ProductionPrerequisitePool", 1024, 32 },
{ "RadarObject", 512, 32 },
{ "ResourceGatheringManager", 16, 16 },
{ "SightingInfo", 8192, 2048 },// Looks big, but all 3000 objects used to have 4 just built in.
{ "SpecialPowerTemplate", 64, 32 },
{ "StateMachinePool", 32, 32 },
{ "TeamPool", 128, 32 }, // if you increase this, increase player/team relation map pools
{ "PlayerRelationMapPool", 128, 32 },
{ "TeamRelationMapPool", 128, 32 },
{ "TeamPrototypePool", 256, 32 },
{ "TerrainType", 256, 32 },
{ "ThingTemplatePool", 1200, 32 },
{ "TunnelTracker", 16, 16 },
{ "Upgrade", 16, 16 },
{ "UpgradeTemplate", 128, 16 },
{ "Anim2D", 32, 32 },
{ "CommandButton", 300, 16 },
{ "CommandSet", 256, 16 },
{ "DisplayString", 32, 32 },
{ "WebBrowserURL", 16, 16 },
{ "Drawable", 4096, 32 },
{ "Image", 2048, 32 },
{ "ParticlePool", 4096, 256 },
{ "ParticleSystemTemplatePool", 768, 32 },
{ "ParticleSystemPool", 1024, 32 },
{ "TerrainRoadType", 64, 64, },
{ "WindowLayoutPool", 32, 32 },
{ "AnimatedParticleSysBoneClientUpdate", 16, 16 },
{ "SwayClientUpdate", 4096, 32 },
{ "BeaconClientUpdate", 64, 32 },
{ "AIGroupPool", 64, 32 },
{ "AIDockMachinePool", 256, 32 },
{ "AIGuardMachinePool", 32, 32 },
{ "AITNGuardMachinePool", 32, 32 },
{ "PathNodePool", 8192, 1024 },
{ "PathPool", 256, 16 },
{ "WorkOrder", 32, 32 },
{ "TeamInQueue", 32, 32 },
{ "AIPlayer", 8, 8 },
{ "AISkirmishPlayer", 8, 8 },
{ "AIStateMachine", 600, 32 },
{ "JetAIStateMachine", 64, 32 },
{ "HeliAIStateMachine", 64, 32 },
{ "AIAttackMoveStateMachine", 2048, 32 },
{ "AIAttackThenIdleStateMachine", 512, 32 },
{ "AttackStateMachine", 512, 32 },
{ "CrateTemplate", 32, 32 },
{ "ExperienceTrackerPool", 4096, 256 },
{ "FiringTrackerPool", 4096, 256 },
{ "ObjectRepulsorHelper", 1024, 256 },
{ "ObjectSMCHelperPool", 4096, 256 },
{ "ObjectWeaponStatusHelperPool", 4096, 256 },
{ "ObjectDefectionHelperPool", 4096, 256 },
{ "Locomotor", 2048, 32 },
{ "LocomotorTemplate", 128, 32 },
{ "ObjectPool", 4096, 32 },
{ "SimpleObjectIteratorPool", 32, 32 },
{ "SimpleObjectIteratorClumpPool", 4096, 32 },
{ "PartitionDataPool", 4096, 32 },
{ "BuildEntry", 32, 32 },
{ "Weapon", 4096, 32 },
{ "WeaponTemplate", 192, 32 },
{ "AIUpdateInterface", 600, 32 },
{ "ActiveBody", 1024, 32 },
{ "ActiveShroudUpgrade", 32, 32 },
{ "AssistedTargetingUpdate", 32, 32 },
{ "AudioEventInfo", 1200, 64 },
{ "AudioRequest", 256, 8 },
{ "AutoHealBehavior", 4096, 32 },
{ "BaseRegenerateUpdate", 64, 32 },
{ "BoneFXDamage", 64, 32 },
{ "BoneFXUpdate", 64, 32 },
{ "BridgeBehavior", 32, 32 },
{ "BridgeTowerBehavior", 32, 32 },
{ "BridgeScaffoldBehavior", 32, 32 },
{ "CaveContain", 16, 16 },
{ "HealContain", 32, 32 },
{ "CreateCrateDie", 256, 128 },
{ "CreateObjectDie", 1024, 32 },
{ "EjectPilotDie", 1024, 32 },
{ "CrushDie", 1024, 32 },
{ "DamDie", 8, 8 },
{ "DelayedUpgrade", 32, 32 },
{ "DelayedWeaponSetUpgradeUpdate", 32, 32 },
{ "DeliverPayloadStateMachine", 32, 32 },
{ "DeliverPayloadAIUpdate", 32, 32 },
{ "DeletionUpdate", 128, 32 },
{ "HackInternetStateMachine", 32, 32 },
{ "HackInternetAIUpdate", 32, 32 },
{ "MissileAIUpdate", 512, 32 },
{ "DumbProjectileBehavior", 64, 32 },
{ "DestroyDie", 1024, 32 },
{ "UpgradeDie", 128, 32 },
{ "KeepObjectDie", 128, 32 },
{ "DozerAIUpdate", 32, 32 },
{ "DynamicGeometryInfoUpdate", 16, 16 },
{ "DynamicShroudClearingRangeUpdate", 128, 16 },
{ "FXListDie", 1024, 32 },
{ "FireSpreadUpdate", 2048, 128 },
{ "FirestormDynamicGeometryInfoUpdate", 16, 16 },
{ "FireWeaponCollide", 2048, 32 },
{ "FireWeaponUpdate", 32, 32 },
{ "FlammableUpdate", 4096, 256 },
{ "FloatUpdate", 512, 128 },
{ "TensileFormationUpdate", 256, 32 },
{ "GarrisonContain", 256, 32 },
{ "HealCrateCollide", 32, 32 },
{ "HeightDieUpdate", 32, 32 },
{ "FireWeaponWhenDamagedBehavior", 32, 32 },
{ "FireWeaponWhenDeadBehavior", 64, 32 },
{ "GenerateMinefieldBehavior", 32, 32 },
{ "HelicopterSlowDeathBehavior", 64, 32 },
{ "ParkingPlaceBehavior", 32, 32 },
#ifdef ALLOW_SURRENDER
{ "POWTruckAIUpdate", 32, 32, },
{ "POWTruckBehavior", 32, 32, },
{ "PrisonBehavior", 32, 32 },
{ "PrisonVisual", 32, 32 },
{ "PropagandaCenterBehavior", 16, 16 },
#endif
{ "PropagandaTowerBehavior", 16, 16 },
{ "ObjectTracker", 128, 32 },
{ "OCLUpdate", 16, 16 },
{ "BodyParticleSystem", 128, 64 },
{ "HighlanderBody", 2048, 128 },
{ "HordeUpdate", 128, 32 },
{ "ImmortalBody", 2048, 128 },
{ "InactiveBody", 2048, 32 },
{ "InstantDeathBehavior", 512, 32 },
{ "LaserUpdate", 32, 32 },
{ "PointDefenseLaserUpdate", 32, 32 },
{ "CleanupHazardUpdate", 32, 32 },
{ "AutoFindHealingUpdate", 256, 32 },
{ "CommandButtonHuntUpdate", 512, 8 },
{ "PilotFindVehicleUpdate", 256, 32 },
{ "DemoTrapUpdate", 32, 32 },
{ "ParticleUplinkCannonUpdate", 16, 16 },
{ "BaikonurLaunchPower", 4, 4 },
{ "RadiusDecalUpdate", 16, 16 },
{ "BattlePlanUpdate", 32, 32 },
{ "LifetimeUpdate", 256, 32 },
{ "LocomotorSetUpgrade", 512, 128 },
{ "AutoDepositUpdate", 256, 32 },
{ "NeutronMissileUpdate", 512, 32 },
{ "MoneyCrateCollide", 32, 32 },
{ "NeutronMissileSlowDeathBehavior", 8, 8 },
{ "OpenContain", 128, 32 },
{ "OverchargeBehavior", 32, 32 },
{ "OverlordContain", 32, 32 },
{ "ParachuteContain", 128, 32 },
{ "PhysicsBehavior", 600, 32 },
{ "PoisonedBehavior", 512, 64 },
{ "ProductionEntry", 32, 32 },
{ "ProductionUpdate", 256, 32 },
{ "ProjectileStreamUpdate", 32, 32 },
{ "ProneUpdate", 128, 32 },
{ "QueueProductionExitUpdate", 32, 32 },
{ "RadarUpdate", 16, 16 },
{ "RadarUpgrade", 16, 16 },
{ "SupplyWarehouseCripplingBehavior", 16, 16 },
{ "CostModifierUpgrade", 32, 32 },
{ "CashBountyPower", 32, 32 },
{ "CleanupAreaPower", 32, 32 },
{ "ObjectCreationUpgrade", 128, 32 },
{ "MinefieldBehavior", 256, 32 },
{ "JetSlowDeathBehavior", 64, 32 },
{ "RebuildHoleBehavior", 64, 32 },
{ "RebuildHoleExposeDie", 64, 32 },
{ "RepairDockUpdate", 32, 32 },
#ifdef ALLOW_SURRENDER
{ "PrisonDockUpdate", 32, 32 },
#endif
{ "RailedTransportDockUpdate", 16, 16 },
{ "RailedTransportAIUpdate", 16, 16 },
{ "RailedTransportContain", 16, 16 },
{ "RailroadBehavior", 16, 16 },
{ "SalvageCrateCollide", 32, 32 },
{ "ShroudCrateCollide", 32, 32 },
{ "SlavedUpdate", 64, 32 },
{ "SlowDeathBehavior", 4096, 32 },
{ "SpyVisionUpdate", 16, 16 },
{ "DefaultProductionExitUpdate", 32, 32 },
{ "SpawnPointProductionExitUpdate", 32, 32 },
{ "SpawnBehavior", 32, 32 },
{ "SpecialPowerCompletionDie", 32, 32 },
{ "SpecialPowerCreate", 32, 32 },
{ "PreorderCreate", 32, 32 },
{ "SpecialAbility", 512, 32 },
{ "SpecialAbilityUpdate", 512, 32 },
{ "MissileLauncherBuildingUpdate", 32, 32 },
{ "SquishCollide", 512, 32 },
{ "StructureBody", 512, 64 },
{ "HiveStructureBody", 64, 32 }, //Stinger sites
{ "StructureCollapseUpdate", 32, 32 },
{ "StructureToppleUpdate", 32, 32 },
{ "SupplyCenterCreate", 32, 32 },
{ "SupplyCenterDockUpdate", 32, 32 },
{ "SupplyCenterProductionExitUpdate", 32, 32 },
{ "SupplyTruckStateMachine", 256, 32 },
{ "SupplyTruckAIUpdate", 32, 32 },
{ "SupplyWarehouseCreate", 32, 32 },
{ "SupplyWarehouseDockUpdate", 32, 32 },
{ "EnemyNearUpdate", 1024, 32 },
{ "TechBuildingBehavior", 32, 32 },
{ "ToppleUpdate", 2048, 32 },
{ "TransitionDamageFX", 256, 32 },
{ "TransportAIUpdate", 64, 32 },
{ "TransportContain", 128, 32 },
{ "TunnelContain", 16, 16 },
{ "TunnelContainDie", 32, 32 },
{ "TunnelCreate", 32, 32 },
{ "TurretAI", 256, 32 },
{ "TurretStateMachine", 128, 32 },
{ "TurretSwapUpgrade", 512, 128 },
{ "UnitCrateCollide", 32, 32 },
{ "UnpauseSpecialPowerUpgrade", 32, 32 },
{ "VeterancyCrateCollide", 32, 32 },
{ "VeterancyGainCreate", 512, 128 },
{ "ConvertToCarBombCrateCollide", 32, 32 },
{ "ConvertToHijackedVehicleCrateCollide", 32, 32 },
{ "JetAIUpdate", 64, 32 },
{ "ChinookAIUpdate", 32, 32 },
{ "WanderAIUpdate", 32, 32 },
{ "WaveGuideUpdate", 16, 16 },
{ "WeaponBonusUpgrade", 512, 128 },
{ "WeaponSetUpgrade", 512, 128 },
{ "ArmorUpgrade", 512, 128 },
{ "WorkerAIUpdate", 128, 128 },
{ "WorkerStateMachine", 128, 128 },
{ "ChinookAIStateMachine", 32, 32 },
{ "DeployStyleAIUpdate", 32, 32 },
{ "AssaultTransportAIUpdate", 64, 32 },
{ "StreamingArchiveFile", 8, 8 },
{ "DozerActionStateMachine", 256, 32 },
{ "DozerPrimaryStateMachine", 256, 32 },
{ "W3DDisplayString", 1024, 128 },
{ "W3DDefaultDraw", 1024, 128 },
{ "W3DDebrisDraw", 1024, 128 },
{ "W3DDependencyModelDraw", 64, 64 },
{ "W3DLaserDraw", 32, 32 },
{ "W3DModelDraw", 4096, 128 },
{ "W3DOverlordTankDraw", 64, 64 },
{ "W3DPoliceCarDraw", 32, 32 },
{ "W3DProjectileStreamDraw", 32, 32 },
{ "W3DRopeDraw", 32, 32 },
{ "W3DScienceModelDraw", 32, 32 },
{ "W3DSupplyDraw", 32, 32 },
{ "W3DTankDraw", 256, 32 },
{ "W3DTracerDraw", 64, 32 },
{ "W3DTruckDraw", 128, 32 },
{ "W3DTankTruckDraw", 32, 16 },
{ "DefaultSpecialPower", 32, 32 },
{ "OCLSpecialPower", 32, 32 },
#ifdef ALLOW_DEMORALIZE
{ "DemoralizeSpecialPower", 16, 16, },
#endif
{ "CashHackSpecialPower", 32, 32 },
{ "CommandSetUpgrade", 32, 32 },
{ "GrantUpgradeCreate", 256, 32 },
{ "SpyVisionSpecialPower", 256, 32 },
{ "StealthDetectorUpdate", 256, 32 },
{ "StealthUpdate", 256, 32 },
{ "StealthUpgrade", 256, 32 },
{ "StatusBitsUpgrade", 128, 128 },
{ "SubObjectsUpgrade", 128, 128 },
{ "ExperienceScalarUpgrade", 256, 128 },
{ "MaxHealthUpgrade", 128, 128 },
{ "WeaponBonusUpgrade", 128, 64 },
{ "StickyBombUpdate", 64, 32 },
{ "FireOCLAfterWeaponCooldownUpdate", 64, 32 },
{ "HijackerUpdate", 64, 32 },
{ "ChinaMinesUpgrade", 64, 32 },
{ "PowerPlantUpdate", 16, 16 },
{ "PowerPlantUpgrade", 16, 16 },
{ "DefectorSpecialPower", 16, 16 },
{ "CheckpointUpdate", 16, 16 },
{ "MobNexusContain", 128, 32 },
{ "MobMemberSlavedUpdate", 64, 32 },
{ "EMPUpdate", 64, 32 },
{ "Overridable", 32, 32 },
{ "W3DGameWindow", 1024, 32 },
{ "SuccessState", 32, 32 },
{ "FailureState", 32, 32 },
{ "ContinueState", 32, 32 },
{ "SleepState", 32, 32 },
{ "AIDockWaitForClearanceState", 256, 32 },
{ "AIDockProcessDockState", 256, 32 },
{ "AIGuardInnerState", 32, 32 },
{ "AIGuardIdleState", 32, 32 },
{ "AIGuardOuterState", 32, 32 },
{ "AIGuardReturnState", 32, 32 },
{ "AIGuardPickUpCrateState", 32, 32 },
{ "AIGuardAttackAggressorState", 32, 32 },
{ "AITNGuardInnerState", 32, 32 },
{ "AITNGuardIdleState", 32, 32 },
{ "AITNGuardOuterState", 32, 32 },
{ "AITNGuardReturnState", 32, 32 },
{ "AITNGuardPickUpCrateState", 32, 32 },
{ "AITNGuardAttackAggressorState", 32, 32 },
{ "AIIdleState", 2400, 32 },
{ "AIRappelState", 600, 32 },
{ "AIBusyState", 600, 32 },
{ "AIWaitState", 600, 32 },
{ "AIAttackState", 4096, 32 },
{ "AIAttackSquadState", 600, 32 },
{ "AIDeadState", 600, 32 },
{ "AIDockState", 600, 32 },
{ "AIExitState", 600, 32 },
{ "AIGuardState", 600, 32 },
{ "AITunnelNetworkGuardState", 600, 32 },
{ "AIHuntState", 600, 32 },
{ "AIAttackAreaState", 600, 32 },
{ "AIFaceState", 1200, 32 },
{ "ApproachState", 600, 32 },
{ "DeliveringState", 600, 32 },
{ "ConsiderNewApproachState", 600, 32 },
{ "RecoverFromOffMapState", 600, 32 },
{ "HeadOffMapState", 600, 32 },
{ "CleanUpState", 600, 32 },
{ "HackInternetState", 600, 32 },
{ "PackingState", 600, 32 },
{ "UnpackingState", 600, 32 },
{ "SupplyTruckWantsToPickUpOrDeliverBoxesState", 600, 32 },
{ "RegroupingState", 600, 32 },
{ "DockingState", 600, 32 },
{ "ChinookEvacuateState", 32, 32 },
{ "ChinookHeadOffMapState", 32, 32 },
{ "ChinookTakeoffOrLandingState", 32, 32 },
{ "ChinookCombatDropState", 32, 32 },
{ "DozerActionPickActionPosState", 256, 32 },
{ "DozerActionMoveToActionPosState", 256, 32 },
{ "DozerActionDoActionState", 256, 32 },
{ "DozerPrimaryIdleState", 256, 32 },
{ "DozerActionState", 256, 32 },
{ "DozerPrimaryGoingHomeState", 256, 32 },
{ "JetAwaitingRunwayState", 64, 32 },
{ "JetOrHeliCirclingDeadAirfieldState", 64, 32 },
{ "HeliTakeoffOrLandingState", 64, 32 },
{ "JetOrHeliParkOrientState", 64, 32 },
{ "JetOrHeliReloadAmmoState", 64, 32 },
{ "SupplyTruckBusyState", 600, 32 },
{ "SupplyTruckIdleState", 600, 32 },
{ "ActAsDozerState", 600, 32 },
{ "ActAsSupplyTruckState", 600, 32 },
{ "AIDockApproachState", 256, 32 },
{ "AIDockAdvancePositionState", 256, 32 },
{ "AIDockMoveToEntryState", 256, 32 },
{ "AIDockMoveToDockState", 256, 32 },
{ "AIDockMoveToExitState", 256, 32 },
{ "AIDockMoveToRallyState", 256, 32 },
{ "AIMoveToState", 600, 32 },
{ "AIMoveOutOfTheWayState", 600, 32 },
{ "AIMoveAndTightenState", 600, 32 },
{ "AIMoveAwayFromRepulsorsState", 600, 32 },
{ "AIAttackApproachTargetState", 96, 32 },
{ "AIAttackPursueTargetState", 96, 32 },
{ "AIAttackAimAtTargetState", 96, 32 },
{ "AIAttackFireWeaponState", 256, 32 },
{ "AIPickUpCrateState", 4096, 32 },
{ "AIFollowWaypointPathState", 1200, 32 },
{ "AIFollowWaypointPathExactState", 1200, 32 },
{ "AIWanderInPlaceState", 600, 32 },
{ "AIFollowPathState", 1200, 32 },
{ "AIMoveAndEvacuateState", 1200, 32 },
{ "AIMoveAndDeleteState", 600, 32 },
{ "AIEnterState", 600, 32 },
{ "JetOrHeliReturningToDeadAirfieldState", 64, 32 },
{ "JetOrHeliReturnForLandingState", 64, 32 },
{ "TurretAIIdleState", 600, 32 },
{ "TurretAIIdleScanState", 600, 32 },
{ "TurretAIAimTurretState", 600, 32 },
{ "TurretAIRecenterTurretState", 600, 32 },
{ "TurretAIHoldTurretState", 600, 32 },
{ "JetOrHeliTaxiState", 64, 32 },
{ "JetTakeoffOrLandingState", 64, 32 },
{ "JetPauseBeforeTakeoffState", 64, 32 },
{ "AIAttackMoveToState", 600, 32 },
{ "AIAttackFollowWaypointPathState", 1200, 32 },
{ "AIWanderState", 600, 32 },
{ "AIPanicState", 600, 32 },
{ "ChinookMoveToBldgState", 32, 32 },
{ "ScienceInfo", 64, 32 },
{ "RankInfo", 32, 32 },
{ "FireWeaponNugget", 32, 32 },
{ "AttackNugget", 32, 32 },
{ "DeliverPayloadNugget", 32, 32 },
{ "ApplyRandomForceNugget", 32, 32 },
{ "GenericObjectCreationNugget", 512, 32 },
{ "SoundFXNugget", 256, 32 },
{ "TracerFXNugget", 32, 32 },
{ "RayEffectFXNugget", 32, 32 },
{ "LightPulseFXNugget", 64, 32 },
{ "ViewShakeFXNugget", 128, 32 },
{ "TerrainScorchFXNugget", 32, 32 },
{ "ParticleSystemFXNugget", 600, 32 },
{ "FXListAtBonePosFXNugget", 32, 32 },
{ "Squad", 256, 32 },
{ "BuildListInfo", 256, 32 },
{ "ScriptGroup", 128, 32 },
{ "OrCondition", 1024, 256 },
{ "ScriptAction", 2048, 512 },
{ "Script", 1024, 256 },
{ "Parameter", 8192, 1024 },
{ "Condition", 2048, 256 },
{ "Template", 32, 32 },
{ "ScriptList", 32, 32 },
{ "AttackPriorityInfo", 32, 32 },
{ "SequentialScript", 32, 32 },
{ "Win32LocalFile", 1024, 256 },
{ "RAMFile", 32, 32 },
{ "BattlePlanBonuses", 32, 32 },
{ "KindOfPercentProductionChange", 32, 32 },
{ "UserParser", 4096, 256 },
{ "XferBlockData", 32, 32 },
{ "EvaCheckInfo", 32, 32 },
{ "SuperweaponInfo", 32, 32 },
{ "NamedTimerInfo", 32, 32 },
{ "PopupMessageData", 32, 32 },
{ "FloatingTextData", 32, 32 },
{ "MapObject", 4096, 32 },
{ "Waypoint", 1024, 32 },
{ "PolygonTrigger", 128, 32 },
{ "Bridge", 32, 32 },
{ "Mapping", 128, 32 },
{ "OutputChunk", 32, 32 },
{ "InputChunk", 32, 32 },
{ "AnimateWindow", 32, 32 },
{ "GameFont", 32, 32 },
{ "NetCommandRef", 256, 32 },
{ "GameMessageArgument", 128, 32 },
{ "GameMessageParserArgumentType", 32, 32 },
{ "GameMessageParser", 32, 32 },
{ "WeaponBonusSet", 32, 32 },
{ "Campaign", 32, 32 },
{ "Mission", 32, 32 },
{ "ModalWindow", 32, 32 },
{ "NetPacket", 32, 32 },
{ "AISideInfo", 32, 32 },
{ "AISideBuildList", 32, 32 },
{ "MetaMapRec", 256, 32 },
{ "TransportStatus", 32, 32 },
{ "Anim2DTemplate", 32, 32 },
{ "ObjectTypes", 32, 32 },
{ "NetCommandList", 512, 32 },
{ "TurretAIData", 256, 32 },
{ "NetCommandMsg", 32, 32 },
{ "NetGameCommandMsg", 64, 32 },
{ "NetAckBothCommandMsg", 32, 32 },
{ "NetAckStage1CommandMsg", 32, 32 },
{ "NetAckStage2CommandMsg", 32, 32 },
{ "NetFrameCommandMsg", 32, 32 },
{ "NetPlayerLeaveCommandMsg", 32, 32 },
{ "NetRunAheadMetricsCommandMsg", 32, 32 },
{ "NetRunAheadCommandMsg", 32, 32 },
{ "NetDestroyPlayerCommandMsg", 32, 32 },
{ "NetDisconnectFrameCommandMsg", 32, 32 },
{ "NetDisconnectScreenOffCommandMsg", 32, 32 },
{ "NetFrameResendRequestCommandMsg", 32, 32 },
{ "NetKeepAliveCommandMsg", 32, 32 },
{ "NetDisconnectKeepAliveCommandMsg", 32, 32 },
{ "NetDisconnectPlayerCommandMsg", 32, 32 },
{ "NetPacketRouterQueryCommandMsg", 32, 32 },
{ "NetPacketRouterAckCommandMsg", 32, 32 },
{ "NetDisconnectChatCommandMsg", 32, 32 },
{ "NetChatCommandMsg", 32, 32 },
{ "NetDisconnectVoteCommandMsg", 32, 32 },
{ "NetProgressCommandMsg", 32, 32 },
{ "NetWrapperCommandMsg", 32, 32 },
{ "NetFileCommandMsg", 32, 32 },
{ "NetFileAnnounceCommandMsg", 32, 32 },
{ "NetFileProgressCommandMsg", 32, 32 },
{ "NetCommandWrapperListNode", 32, 32 },
{ "NetCommandWrapperList", 32, 32 },
{ "Connection", 32, 32 },
{ "User", 32, 32 },
{ "FrameDataManager", 32, 32 },
{ "DrawableIconInfo", 32, 32 },
{ "TintEnvelope", 128, 32 },
{ "DynamicAudioEventRTS", 1024, 256 },
{ "DrawableLocoInfo", 128, 32 },
{ "W3DPrototypeClass", 2048, 32 },
{ "EnumeratedIP", 32, 32 },
{ "WaterTransparencySetting", 4, 4 },
// W3D pools!
{ "BoxPrototypeClass", 512, 32 },
{ "SpherePrototypeClass", 32, 32 },
{ "SoundRenderObjPrototypeClass", 32, 32 },
{ "RingPrototypeClass", 32, 32 },
{ "PrimitivePrototypeClass", 8192, 32 },
{ "HModelPrototypeClass", 256, 32 },
{ "ParticleEmitterPrototypeClass", 32, 32 },
{ "NullPrototypeClass", 32, 32 },
{ "HLodPrototypeClass", 512, 32 },
{ "DistLODPrototypeClass", 32, 32 },
{ "DazzlePrototypeClass", 32, 32 },
{ "CollectionPrototypeClass", 32, 32 },
{ "BoxPrototypeClass", 256, 32 },
{ "AggregatePrototypeClass", 32, 32 },
{ "OBBoxRenderObjClass", 16384, 32 },
{ "AABoxRenderObjClass", 32, 32 },
{ "VertexMaterialClass", 16384, 32 },
{ "TextureClass", 1024, 32 },
{ "CloudMapTerrainTextureClass", 32, 32 },
{ "ScorchTextureClass", 32, 32 },
{ "LightMapTerrainTextureClass", 32, 32 },
{ "AlphaEdgeTextureClass", 32, 32 },
{ "AlphaTerrainTextureClass", 32, 32 },
{ "TerrainTextureClass", 32, 32 },
{ "MeshClass", 16384, 1024 },
{ "HTreeClass", 8192, 32 },
{ "HLodDefClass", 512, 32 },
{ "HLodClass", 4096, 32 },
{ "MeshModelClass", 8192, 32 },
{ "ShareBufferClass", 32768, 1024 },
{ "AABTreeClass", 32, 32 },
{ "MotionChannelClass", 16384, 32 },
{ "BitChannelClass", 64, 32 },
{ "TimeCodedMotionChannelClass", 32, 32 },
{ "AdaptiveDeltaMotionChannelClass", 32, 32 },
{ "TimeCodedBitChannelClass", 32, 32 },
{ "UVBufferClass", 8192, 32 },
{ "TexBufferClass", 512, 32 },
{ "MatBufferClass", 512, 32 },
{ "MatrixMapperClass", 32, 32 },
{ "ScaleTextureMapperClass", 32, 32 },
{ "LinearOffsetTextureMapperClass", 32, 32 },
{ "GridTextureMapperClass", 32, 32 },
{ "RotateTextureMapperClass", 32, 32 },
{ "SineLinearOffsetTextureMapperClass", 32, 32 },
{ "StepLinearOffsetTextureMapperClass", 32, 32 },
{ "ZigZagLinearOffsetTextureMapperClass", 32, 32 },
{ "ClassicEnvironmentMapperClass", 32, 32 },
{ "EnvironmentMapperClass", 256, 32 },
{ "EdgeMapperClass", 32, 32 },
{ "WSClassicEnvironmentMapperClass", 32, 32 },
{ "WSEnvironmentMapperClass", 32, 32 },
{ "GridClassicEnvironmentMapperClass", 32, 32 },
{ "GridEnvironmentMapperClass", 32, 32 },
{ "ScreenMapperClass", 32, 32 },
{ "RandomTextureMapperClass", 32, 32 },
{ "BumpEnvTextureMapperClass", 32, 32 },
{ "MeshLoadContextClass", 32, 32 },
{ "MaterialInfoClass", 8192, 32 },
{ "MeshMatDescClass", 8192, 32 },
{ "TextureLoadTaskClass", 256, 32 },
{ "SortingNodeStruct", 256, 32 },
{ "ProxyArrayClass", 32, 32 },
{ "Line3DClass", 128, 32 },
{ "Render2DClass", 64, 32 },
{ "SurfaceClass", 128, 32 },
{ "FontCharsClassCharDataStruct", 1024, 32 },
{ "FontCharsBuffer", 16, 4 },
{ "FVFInfoClass", 128, 32 },
{ "TerrainTracksRenderObjClass", 128, 32 },
{ "DynamicIBAccessClass", 32, 32 },
{ "DX8IndexBufferClass", 128, 32 },
{ "SortingIndexBufferClass", 32, 32 },
{ "DX8VertexBufferClass", 128, 32 },
{ "SortingVertexBufferClass", 32, 32 },
{ "DynD3DMATERIAL8", 8192, 32 },
{ "DynamicMatrix3D", 512, 32 },
{ "MeshGeometryClass", 32, 32 },
{ "DynamicMeshModel", 32, 32 },
{ "GapFillerClass", 32, 32 },
{ "FontCharsClass", 64, 32 },
{ 0, 0, 0 }
};
//-----------------------------------------------------------------------------
void userMemoryAdjustPoolSize(const char *poolName, Int& initialAllocationCount, Int& overflowAllocationCount)
{
if (initialAllocationCount > 0)
return;
for (const PoolSizeRec* p = sizes; p->name != NULL; ++p)
{
if (strcmp(p->name, poolName) == 0)
{
initialAllocationCount = p->initial;
overflowAllocationCount = p->overflow;
return;
}
}
DEBUG_CRASH(("Initial size for pool %s not found -- you should add it to MemoryInit.cpp\n",poolName));
}
//-----------------------------------------------------------------------------
static Int roundUpMemBound(Int i)
{
const int MEM_BOUND_ALIGNMENT = 4;
if (i < MEM_BOUND_ALIGNMENT)
return MEM_BOUND_ALIGNMENT;
else
return (i + (MEM_BOUND_ALIGNMENT-1)) & ~(MEM_BOUND_ALIGNMENT-1);
}
//-----------------------------------------------------------------------------
void userMemoryManagerInitPools()
{
// note that we MUST use stdio stuff here, and not the normal game file system
// (with bigfile support, etc), because that relies on memory pools, which
// aren't yet initialized properly! so rely ONLY on straight stdio stuff here.
// (not even AsciiString. thanks.)
// since we're called prior to main, the cur dir might not be what
// we expect. so do it the hard way.
char buf[_MAX_PATH];
::GetModuleFileName(NULL, buf, sizeof(buf));
char* pEnd = buf + strlen(buf);
while (pEnd != buf)
{
if (*pEnd == '\\')
{
*pEnd = 0;
break;
}
--pEnd;
}
strcat(buf, "\\Data\\INI\\MemoryPools.ini");
FILE* fp = fopen(buf, "r");
if (fp)
{
char poolName[256];
int initial, overflow;
while (fgets(buf, _MAX_PATH, fp))
{
if (buf[0] == ';')
continue;
if (sscanf(buf, "%s %d %d", poolName, &initial, &overflow ) == 3)
{
for (PoolSizeRec* p = sizes; p->name != NULL; ++p)
{
if (stricmp(p->name, poolName) == 0)
{
// currently, these must be multiples of 4. so round up.
p->initial = roundUpMemBound(initial);
p->overflow = roundUpMemBound(overflow);
break; // from for-p
}
}
}
}
fclose(fp);
}
}

View file

@ -0,0 +1,82 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: QuickTrig.cpp ////////////////////////////////////////////////////////////////////////////////
// Author: Mark Lorenzen (adapted by srj)
// Desc: fast trig
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/QuickTrig.h"
// GLOBALS ////////////////////////////////////////////////////////////////////////////////////////
Real TheQuickSinTable[] =
{
0.00000f, 0.01237f, 0.02473f, 0.03710f, 0.04945f, 0.06180f, 0.07414f, 0.08647f,
0.09879f, 0.11109f, 0.12337f, 0.13563f, 0.14788f, 0.16010f, 0.17229f, 0.18446f,
0.19661f, 0.20872f, 0.22080f, 0.23284f, 0.24485f, 0.25683f, 0.26876f, 0.28065f,
0.29250f, 0.30431f, 0.31607f, 0.32778f, 0.33944f, 0.35104f, 0.36260f, 0.37410f,
0.38554f, 0.39692f, 0.40824f, 0.41950f, 0.43070f, 0.44183f, 0.45289f, 0.46388f,
0.47480f, 0.48565f, 0.49643f, 0.50712f, 0.51774f, 0.52829f, 0.53875f, 0.54913f,
0.55942f, 0.56963f, 0.57975f, 0.58978f, 0.59973f, 0.60958f, 0.61934f, 0.62900f,
0.63857f, 0.64804f, 0.65741f, 0.66668f, 0.67584f, 0.68491f, 0.69387f, 0.70272f,
0.71147f, 0.72010f, 0.72863f, 0.73705f, 0.74535f, 0.75354f, 0.76161f, 0.76957f,
0.77741f, 0.78513f, 0.79273f, 0.80020f, 0.80756f, 0.81479f, 0.82190f, 0.82888f,
0.83574f, 0.84247f, 0.84907f, 0.85554f, 0.86187f, 0.86808f, 0.87415f, 0.88009f,
0.88590f, 0.89157f, 0.89710f, 0.90250f, 0.90775f, 0.91287f, 0.91785f, 0.92269f,
0.92739f, 0.93195f, 0.93636f, 0.94063f, 0.94476f, 0.94874f, 0.95257f, 0.95626f,
0.95981f, 0.96321f, 0.96646f, 0.96956f, 0.97251f, 0.97532f, 0.97798f, 0.98048f,
0.98284f, 0.98505f, 0.98710f, 0.98901f, 0.99076f, 0.99236f, 0.99381f, 0.99511f,
0.99625f, 0.99725f, 0.99809f, 0.99878f, 0.99931f, 0.99969f, 0.99992f, 1.00000f,
0.99992f
};
Real TheQuickTanTable[] =
{
0.00000f, 0.00787f, 0.01575f, 0.02363f, 0.03151f, 0.03939f, 0.04728f, 0.05517f,
0.06308f, 0.07099f, 0.07890f, 0.08683f, 0.09477f, 0.10272f, 0.11068f, 0.11866f,
0.12666f, 0.13466f, 0.14269f, 0.15073f, 0.15880f, 0.16688f, 0.17498f, 0.18311f,
0.19126f, 0.19943f, 0.20763f, 0.21586f, 0.22412f, 0.23240f, 0.24071f, 0.24906f,
0.25744f, 0.26585f, 0.27430f, 0.28279f, 0.29131f, 0.29987f, 0.30847f, 0.31712f,
0.32581f, 0.33454f, 0.34332f, 0.35214f, 0.36102f, 0.36994f, 0.37892f, 0.38795f,
0.39704f, 0.40618f, 0.41539f, 0.42465f, 0.43398f, 0.44337f, 0.45282f, 0.46234f,
0.47194f, 0.48160f, 0.49134f, 0.50115f, 0.51104f, 0.52101f, 0.53106f, 0.54120f,
0.55143f, 0.56174f, 0.57214f, 0.58264f, 0.59324f, 0.60393f, 0.61473f, 0.62563f,
0.63664f, 0.64777f, 0.65900f, 0.67035f, 0.68183f, 0.69342f, 0.70515f, 0.71700f,
0.72899f, 0.74112f, 0.75339f, 0.76581f, 0.77838f, 0.79110f, 0.80398f, 0.81703f,
0.83025f, 0.84363f, 0.85720f, 0.87096f, 0.88490f, 0.89904f, 0.91338f, 0.92793f,
0.94269f, 0.95767f, 0.97288f, 0.98833f, 1.00401f, 1.01995f, 1.03615f, 1.05261f,
1.06935f, 1.08637f, 1.10368f, 1.12130f, 1.13924f, 1.15749f, 1.17609f, 1.19503f,
1.21433f, 1.23400f, 1.25406f, 1.27452f, 1.29540f, 1.31670f, 1.33845f, 1.36067f,
1.38336f, 1.40656f, 1.43027f, 1.45453f, 1.47935f, 1.50475f, 1.53076f, 1.55741f,
1.58471f
};
// yes, Real. No, really.
Real TheQuickSinTableCount = sizeof(TheQuickSinTable) / sizeof(Real);
Real TheQuickTanTableCount = sizeof(TheQuickTanTable) / sizeof(Real);

View file

@ -0,0 +1,205 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: QuotedPrintable.cpp /////////////////////////////////////////////////////////
// Author: Matt Campbell, February 2002
// Description: Quoted-printable encode/decode
////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/QuotedPrintable.h"
#define MAGIC_CHAR '_'
// takes an integer and returns an ASCII representation
static char intToHexDigit(int num)
{
if (num<0 || num >15) return '\0';
if (num<10)
{
return '0' + num;
}
return 'A' + (num-10);
}
// convert an ASCII representation of a hex digit into the digit itself
static int hexDigitToInt(char c)
{
if (c <= '9' && c >= '0') return (c - '0');
if (c <= 'f' && c >= 'a') return (c - 'a' + 10);
if (c <= 'F' && c >= 'A') return (c - 'A' + 10);
return 0;
}
// Convert unicode strings into ascii quoted-printable strings
AsciiString UnicodeStringToQuotedPrintable(UnicodeString original)
{
static char dest[1024];
const char *src = (const char *)original.str();
int i=0;
while ( !(src[0]=='\0' && src[1]=='\0') && i<1021 )
{
if (!isalnum(*src))
{
dest[i++] = MAGIC_CHAR;
dest[i++] = intToHexDigit((*src)>>4);
dest[i++] = intToHexDigit((*src)&0xf);
} else
{
dest[i++] = *src;
}
src ++;
if (!isalnum(*src))
{
dest[i++] = MAGIC_CHAR;
dest[i++] = intToHexDigit((*src)>>4);
dest[i++] = intToHexDigit((*src)&0xf);
}
else
{
dest[i++] = *src;
}
src ++;
}
dest[i] = '\0';
return dest;
}
// Convert ascii strings into ascii quoted-printable strings
AsciiString AsciiStringToQuotedPrintable(AsciiString original)
{
static char dest[1024];
const char *src = (const char *)original.str();
int i=0;
while ( src[0]!='\0' && i<1021 )
{
if (!isalnum(*src))
{
dest[i++] = MAGIC_CHAR;
dest[i++] = intToHexDigit((*src)>>4);
dest[i++] = intToHexDigit((*src)&0xf);
} else
{
dest[i++] = *src;
}
src ++;
}
dest[i] = '\0';
return dest;
}
// Convert ascii quoted-printable strings into unicode strings
UnicodeString QuotedPrintableToUnicodeString(AsciiString original)
{
static unsigned short dest[1024];
int i=0;
unsigned char *c = (unsigned char *)dest;
const unsigned char *src = (const unsigned char *)original.str();
while (*src && i<1023)
{
if (*src == MAGIC_CHAR)
{
if (src[1] == '\0')
{
// string ends with MAGIC_CHAR
break;
}
*c = hexDigitToInt(src[1]);
src++;
if (src[1] != '\0')
{
*c = *c<<4;
*c = *c | hexDigitToInt(src[1]);
src++;
}
}
else
{
*c = *src;
}
src++;
c++;
}
// Fixup odd-length strings
if ((c-(unsigned char *)dest)%2)
{
// OK
}
else
{
*c = '\0';
c++;
}
*c = 0;
UnicodeString out(dest);
return out;
}
// Convert ascii quoted-printable strings into ascii strings
AsciiString QuotedPrintableToAsciiString(AsciiString original)
{
static unsigned char dest[1024];
int i=0;
unsigned char *c = (unsigned char *)dest;
const unsigned char *src = (const unsigned char *)original.str();
while (*src && i<1023)
{
if (*src == MAGIC_CHAR)
{
if (src[1] == '\0')
{
// string ends with MAGIC_CHAR
break;
}
*c = hexDigitToInt(src[1]);
src++;
if (src[1] != '\0')
{
*c = *c<<4;
*c = *c | hexDigitToInt(src[1]);
src++;
}
}
else
{
*c = *src;
}
src++;
c++;
}
*c = 0;
return AsciiString((const char *)dest);
}

View file

@ -0,0 +1,508 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO
//
// File name: WSYS_RAMFile.cpp
//
// Created: 11/08/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
#include <sys/stat.h>
#include "Common/AsciiString.h"
#include "Common/FileSystem.h"
#include "Common/RAMFile.h"
#include "Common/PerfTimer.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//=================================================================
// RAMFile::RAMFile
//=================================================================
RAMFile::RAMFile()
: m_size(0),
m_data(NULL),
//Added By Sadullah Nader
//Initializtion(s) inserted
m_pos(0)
//
{
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//=================================================================
// RAMFile::~RAMFile
//=================================================================
RAMFile::~RAMFile()
{
if (m_data != NULL) {
delete [] m_data;
}
File::close();
}
//=================================================================
// RAMFile::open
//=================================================================
/**
* This function opens a file using the standard C open() call. Access flags
* are mapped to the appropriate open flags. Returns true if file was opened
* successfully.
*/
//=================================================================
//DECLARE_PERF_TIMER(RAMFile)
Bool RAMFile::open( const Char *filename, Int access )
{
//USE_PERF_TIMER(RAMFile)
File *file = TheFileSystem->openFile( filename, access );
if ( file == NULL )
{
return FALSE;
}
Bool result = open( file );
file->close();
return result;
}
//============================================================================
// RAMFile::open
//============================================================================
Bool RAMFile::open( File *file )
{
//USE_PERF_TIMER(RAMFile)
if ( file == NULL )
{
return NULL;
}
Int access = file->getAccess();
if ( !File::open( file->getName(), access ))
{
return FALSE;
}
// read whole file in to memory
m_size = file->size();
m_data = MSGNEW("RAMFILE") char [ m_size ]; // pool[]ify
if ( m_data == NULL )
{
return FALSE;
}
m_size = file->read( m_data, m_size );
if ( m_size < 0 )
{
delete [] m_data;
m_data = NULL;
return FALSE;
}
m_pos = 0;
return TRUE;
}
//============================================================================
// RAMFile::openFromArchive
//============================================================================
Bool RAMFile::openFromArchive(File *archiveFile, const AsciiString& filename, Int offset, Int size)
{
//USE_PERF_TIMER(RAMFile)
if (archiveFile == NULL) {
return FALSE;
}
if (File::open(filename.str(), File::READ | File::BINARY) == FALSE) {
return FALSE;
}
if (m_data != NULL) {
delete[] m_data;
m_data = NULL;
}
m_data = MSGNEW("RAMFILE") Char [size]; // pool[]ify
m_size = size;
if (archiveFile->seek(offset, File::START) != offset) {
return FALSE;
}
if (archiveFile->read(m_data, size) != size) {
return FALSE;
}
m_nameStr = filename;
return TRUE;
}
//=================================================================
// RAMFile::close
//=================================================================
/**
* Closes the current file if it is open.
* Must call RAMFile::close() for each successful RAMFile::open() call.
*/
//=================================================================
void RAMFile::close( void )
{
if ( m_data )
{
delete [] m_data;
m_data = NULL;
}
File::close();
}
//=================================================================
// RAMFile::read
//=================================================================
// if buffer is null, just advance the current position by 'bytes'
Int RAMFile::read( void *buffer, Int bytes )
{
if( m_data == NULL )
{
return -1;
}
Int bytesLeft = m_size - m_pos ;
if ( bytes > bytesLeft )
{
bytes = bytesLeft;
}
if (( bytes > 0 ) && ( buffer != NULL ))
{
memcpy ( buffer, &m_data[m_pos], bytes );
}
m_pos += bytes;
return bytes;
}
//=================================================================
// RAMFile::write
//=================================================================
Int RAMFile::write( const void *buffer, Int bytes )
{
return -1;
}
//=================================================================
// RAMFile::seek
//=================================================================
Int RAMFile::seek( Int pos, seekMode mode)
{
Int newPos;
switch( mode )
{
case START:
newPos = pos;
break;
case CURRENT:
newPos = m_pos + pos;
break;
case END:
DEBUG_ASSERTCRASH(pos <= 0, ("RAMFile::seek - position should be <= 0 for a seek starting from the end."));
newPos = m_size + pos;
break;
default:
// bad seek mode
return -1;
}
if ( newPos < 0 )
{
newPos = 0;
}
else if ( newPos > m_size )
{
newPos = m_size;
}
m_pos = newPos;
return m_pos;
}
//=================================================================
// RAMFile::scanInt
//=================================================================
Bool RAMFile::scanInt(Int &newInt)
{
newInt = 0;
AsciiString tempstr;
while ((m_pos < m_size) && ((m_data[m_pos] < '0') || (m_data[m_pos] > '9')) && (m_data[m_pos] != '-')) {
++m_pos;
}
if (m_pos >= m_size) {
m_pos = m_size;
return FALSE;
}
do {
tempstr.concat(m_data[m_pos]);
++m_pos;
} while ((m_pos < m_size) && ((m_data[m_pos] >= '0') && (m_data[m_pos] <= '9')));
// if (m_pos < m_size) {
// --m_pos;
// }
newInt = atoi(tempstr.str());
return TRUE;
}
//=================================================================
// RAMFile::scanInt
//=================================================================
Bool RAMFile::scanReal(Real &newReal)
{
newReal = 0.0;
AsciiString tempstr;
Bool sawDec = FALSE;
while ((m_pos < m_size) && ((m_data[m_pos] < '0') || (m_data[m_pos] > '9')) && (m_data[m_pos] != '-') && (m_data[m_pos] != '.')) {
++m_pos;
}
if (m_pos >= m_size) {
m_pos = m_size;
return FALSE;
}
do {
tempstr.concat(m_data[m_pos]);
if (m_data[m_pos] == '.') {
sawDec = TRUE;
}
++m_pos;
} while ((m_pos < m_size) && (((m_data[m_pos] >= '0') && (m_data[m_pos] <= '9')) || ((m_data[m_pos] == '.') && !sawDec)));
// if (m_pos < m_size) {
// --m_pos;
// }
newReal = atof(tempstr.str());
return TRUE;
}
//=================================================================
// RAMFile::scanString
//=================================================================
Bool RAMFile::scanString(AsciiString &newString)
{
newString.clear();
while ((m_pos < m_size) && isspace(m_data[m_pos])) {
++m_pos;
}
if (m_pos >= m_size) {
m_pos = m_size;
return FALSE;
}
do {
newString.concat(m_data[m_pos]);
++m_pos;
} while ((m_pos < m_size) && (!isspace(m_data[m_pos])));
return TRUE;
}
//=================================================================
// RAMFile::nextLine
//=================================================================
void RAMFile::nextLine(Char *buf, Int bufSize)
{
Int i = 0;
// seek to the next new-line character
while ((m_pos < m_size) && (m_data[m_pos] != '\n')) {
if ((buf != NULL) && (i < (bufSize-1))) {
buf[i] = m_data[m_pos];
++i;
}
++m_pos;
}
// we got to the new-line character, now go one past it.
if (m_pos < m_size) {
if ((buf != NULL) && (i < bufSize)) {
buf[i] = m_data[m_pos];
++i;
}
++m_pos;
}
if (buf != NULL) {
if (i < bufSize) {
buf[i] = 0;
} else {
buf[bufSize] = 0;
}
}
if (m_pos >= m_size) {
m_pos = m_size;
}
}
//=================================================================
// RAMFile::nextLine
//=================================================================
Bool RAMFile::copyDataToFile(File *localFile)
{
if (localFile == NULL) {
return FALSE;
}
if (localFile->write(m_data, m_size) == m_size) {
return TRUE;
}
return FALSE;
}
//=================================================================
//=================================================================
File* RAMFile::convertToRAMFile()
{
return this;
}
//=================================================================
// RAMFile::readEntireAndClose
//=================================================================
/**
Allocate a buffer large enough to hold entire file, read
the entire file into the buffer, then close the file.
the buffer is owned by the caller, who is responsible
for freeing is (via delete[]). This is a Good Thing to
use because it minimizes memory copies for BIG files.
*/
char* RAMFile::readEntireAndClose()
{
if (m_data == NULL)
{
DEBUG_CRASH(("m_data is NULL in RAMFile::readEntireAndClose -- should not happen!\n"));
return NEW char[1]; // just to avoid crashing...
}
char* tmp = m_data;
m_data = NULL; // will belong to our caller!
close();
return tmp;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,512 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: GameStateMap.cpp /////////////////////////////////////////////////////////////////////////
// Author: Colin Day, October 2002
// Desc: Chunk in the save game file that will hold a pristine version of the map file
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
#include "Common/GameState.h"
#include "Common/GameStateMap.h"
#include "Common/GlobalData.h"
#include "Common/Xfer.h"
#include "GameClient/GameClient.h"
#include "GameClient/MapUtil.h"
#include "GameLogic/GameLogic.h"
#include "GameNetwork/GameInfo.h"
// GLOBALS ////////////////////////////////////////////////////////////////////////////////////////
GameStateMap *TheGameStateMap = NULL;
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// METHODS ////////////////////////////////////////////////////////////////////////////////////////
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
GameStateMap::GameStateMap( void )
{
} // end GameStateMap
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
GameStateMap::~GameStateMap( void )
{
//
// clear the save directory of any temporary "scratch pad" maps that were extracted
// from any previously loaded save game files
//
clearScratchPadMaps();
} // end ~GameStateMap
// ------------------------------------------------------------------------------------------------
/** Embed the pristine map into the xfer stream */
// ------------------------------------------------------------------------------------------------
static void embedPristineMap( AsciiString map, Xfer *xfer )
{
// open the map file
File *file = TheFileSystem->openFile( map.str(), File::READ | File::BINARY );
if( file == NULL )
{
DEBUG_CRASH(( "embedPristineMap - Error opening source file '%s'\n", map.str() ));
throw SC_INVALID_DATA;
} // end if
// how big is the map file
Int fileSize = file->seek( 0, File::END );
// rewind to beginning of file
file->seek( 0, File::START );
// allocate buffer big enough to hold the entire map file
char *buffer = new char[ fileSize ];
if( buffer == NULL )
{
DEBUG_CRASH(( "embedPristineMap - Unable to allocate buffer for file '%s'\n", map.str() ));
throw SC_INVALID_DATA;
} // end if
// copy the file to the buffer
if( file->read( buffer, fileSize ) != fileSize )
{
DEBUG_CRASH(( "embeddPristineMap - Error reading from file '%s'\n", map.str() ));
throw SC_INVALID_DATA;
} // end if
// close the BIG file
file->close();
// write the contents to the save file
DEBUG_ASSERTCRASH( xfer->getXferMode() == XFER_SAVE, ("embedPristineMap - Unsupposed xfer mode\n") );
xfer->beginBlock();
xfer->xferUser( buffer, fileSize );
xfer->endBlock();
// delete the buffer
delete [] buffer;
} // end embedPristineMap
// ------------------------------------------------------------------------------------------------
/** Embed an "in use" map into the xfer stream. An "in use" map is one that has already
* been pulled out of a save game file and parked in a temporary file in the save directory */
// ------------------------------------------------------------------------------------------------
static void embedInUseMap( AsciiString map, Xfer *xfer )
{
FILE *fp = fopen( map.str(), "rb" );
// sanity
if( fp == NULL )
{
DEBUG_CRASH(( "embedInUseMap - Unable to open file '%s'\n", map.str() ));
throw SC_INVALID_DATA;
} // end if
// how big is the file
fseek( fp, 0, SEEK_END );
Int fileSize = ftell( fp );
// rewind file back to start
fseek( fp, 0, SEEK_SET );
// allocate a buffer big enough for the entire file
char *buffer = new char[ fileSize ];
if( buffer == NULL )
{
DEBUG_CRASH(( "embedInUseMap - Unable to allocate buffer for file '%s'\n", map.str() ));
throw SC_INVALID_DATA;
} // end if
// read the entire file
if( fread( buffer, 1, fileSize, fp ) != fileSize )
{
DEBUG_CRASH(( "embedInUseMap - Error reading from file '%s'\n", map.str() ));
throw SC_INVALID_DATA;
} // end if
// embed file into xfer stream
xfer->beginBlock();
xfer->xferUser( buffer, fileSize );
xfer->endBlock();
// close the file
fclose( fp );
// delete buffer
delete [] buffer;
} // embedInUseMap
// ------------------------------------------------------------------------------------------------
/** Extract the map from the xfer stream and save as a file with filename 'mapToSave' */
// ------------------------------------------------------------------------------------------------
static void extractAndSaveMap( AsciiString mapToSave, Xfer *xfer )
{
UnsignedInt dataSize;
// open handle to output file
FILE *fp = fopen( mapToSave.str(), "w+b" );
if( fp == NULL )
{
DEBUG_CRASH(( "extractAndSaveMap - Unable to open file '%s'\n", mapToSave.str() ));
throw SC_INVALID_DATA;
} // en
// read data size from file
dataSize = xfer->beginBlock();
// allocate buffer big enough for the entire map file
char *buffer = new char[ dataSize ];
if( buffer == NULL )
{
DEBUG_CRASH(( "extractAndSaveMap - Unable to allocate buffer for file '%s'\n", mapToSave.str() ));
throw SC_INVALID_DATA;
} // end if
// read map file
xfer->xferUser( buffer, dataSize );
// write contents of buffer to new file
if( fwrite( buffer, 1, dataSize, fp ) != dataSize )
{
DEBUG_CRASH(( "extractAndSaveMap - Error writing to file '%s'\n", mapToSave.str() ));
throw SC_INVALID_DATA;
} // end if
// close the new file
fclose( fp );
// end of data block
xfer->endBlock();
// delete the buffer
delete [] buffer;
} // end extractAndSaveMap
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version
* 2: Now storing the game mode from logic. Storing that here cause TheGameLogic->startNewGame
* needs to set up the player list based on it.
*/
// ------------------------------------------------------------------------------------------------
void GameStateMap::xfer( Xfer *xfer )
{
// version
const XferVersion currentVersion = 2;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// get save game info
SaveGameInfo *saveGameInfo = TheGameState->getSaveGameInfo();
//
// map filename, for purposes of saving we will always be saving a with a map filename
// that refers to map in the save directory so we must always save a filename into
// the file that is in the save directory
//
Bool firstSave = FALSE; // TRUE if we haven't yet saved a pristine load of a new map
if( xfer->getXferMode() == XFER_SAVE )
{
AsciiString mapLeafName = TheGameState->getMapLeafName(TheGlobalData->m_mapName);
// construct filename to map in the save directory
saveGameInfo->saveGameMapName = TheGameState->getFilePathInSaveDirectory(mapLeafName);
// write map name. For cross-machine compatibility, we always write
// it as just "Save\filename", not a full path.
{
AsciiString tmp = TheGameState->realMapPathToPortableMapPath(saveGameInfo->saveGameMapName);
xfer->xferAsciiString( &tmp );
}
//
// write pristine map name which is already in the member 'pristineMapName' from
// a previous load, or in the instance where we are saving for the first time
// and the global data map name refers to a pristine map we will copy it in there first
//
if (!TheGameState->isInSaveDirectory(TheGlobalData->m_mapName))
{
// copy the pristine name
saveGameInfo->pristineMapName = TheGlobalData->m_mapName;
//
// this is also an indication that we are saving for the first time a brand new
// map that has never been saved into this save file before (a save is also considered
// to be a first save as long as we are writing data to disk without having loaded
// this particluar map from the save file ... so if you load USA01 for the first
// time and save, that is a first save ... then, without quitting, if you save
// again that is *also* considered a first save). First save just determines
// whether the map file we embed in the save file is taken from the maps directory
// or from the temporary map extracted to the save directory from a load
//
firstSave = TRUE;
} // end if
// save the pristine name
// For cross-machine compatibility, we always write
// it as just "Save\filename", not a full path.
{
AsciiString tmp = TheGameState->realMapPathToPortableMapPath(saveGameInfo->pristineMapName);
xfer->xferAsciiString( &tmp );
}
if (currentVersion >= 2)
{
// save the game mode.
Int gameMode = TheGameLogic->getGameMode();
xfer->xferInt( &gameMode);
}
} // end if, save
else
{
// read the save game map name
AsciiString tmp;
xfer->xferAsciiString( &tmp );
saveGameInfo->saveGameMapName = TheGameState->portableMapPathToRealMapPath(tmp);
if (!TheGameState->isInSaveDirectory(saveGameInfo->saveGameMapName))
{
DEBUG_CRASH(("GameState::xfer - The map filename read from the file '%s' is not in the SAVE directory, but should be\n",
saveGameInfo->saveGameMapName.str()) );
throw SC_INVALID_DATA;
}
// set this map as the map to load in the global data
TheWritableGlobalData->m_mapName = saveGameInfo->saveGameMapName;
// read the pristine map filename
xfer->xferAsciiString( &saveGameInfo->pristineMapName );
saveGameInfo->pristineMapName = TheGameState->portableMapPathToRealMapPath(saveGameInfo->pristineMapName);
if (currentVersion >= 2)
{
// get the game mode.
Int gameMode;
xfer->xferInt(&gameMode);
TheGameLogic->setGameMode(gameMode);
}
} // end else, load
// map data
if( xfer->getXferMode() == XFER_SAVE )
{
//
// if this is a first save from a pristine map load, we need to copy the pristine
// map into the save game file
//
if( firstSave == TRUE )
{
embedPristineMap( saveGameInfo->pristineMapName, xfer );
} // end if, first save
else
{
//
// this is *NOT* a first save from a pristine map, just read the map file
// that was extracted from the save game file during the last load and embedd
// that into the save game file
//
embedInUseMap( saveGameInfo->saveGameMapName, xfer );
} // end else
} // end if, save
else
{
//
// take the embedded map file out of the save file, and save as its own .map file
// in the save directory temporarily
//
extractAndSaveMap( saveGameInfo->saveGameMapName, xfer );
} // end else
//
// it's important that early in the load process, we xfer the object ID counter
// in the game logic ... this is necessary because there are flows of code that
// create objects during the load of a save game that is *NOT FROM THE OBJECT BLOCK* of
// code, such as loading bridges (which creates the bridge and tower objects). We
// are fancy and "do the right thing" in those situations, but we don't want to run
// the risk of these objects being created and having overlapping IDs of anything
// that we will load from the save file
//
ObjectID highObjectID = TheGameLogic->getObjectIDCounter();
xfer->xferObjectID( &highObjectID );
TheGameLogic->setObjectIDCounter( highObjectID );
//
// it is also equally important to xfer the drawable id counter early in the load process
// because the act of creating objects also creates drawables, so when it comes time
// to load the block of drawables we want to make sure that newly created drawables
// at that time will never overlap IDs with any drawable created as part of an object
// which then had its ID transferred in from the save file
//
DrawableID highDrawableID = TheGameClient->getDrawableIDCounter();
xfer->xferDrawableID( &highDrawableID );
TheGameClient->setDrawableIDCounter( highDrawableID );
if (TheGameLogic->getGameMode()==GAME_SKIRMISH) {
if (TheSkirmishGameInfo==NULL) {
TheSkirmishGameInfo = NEW SkirmishGameInfo;
TheSkirmishGameInfo->init();
TheSkirmishGameInfo->clearSlotList();
TheSkirmishGameInfo->reset();
}
xfer->xferSnapshot(TheSkirmishGameInfo);
} else {
if (TheSkirmishGameInfo) {
delete TheSkirmishGameInfo;
TheSkirmishGameInfo = NULL;
}
}
//
// for loading, start a new game (flagged from a save game) ... this will load the map and all the
// things in the map file that don't don't change (terrain, triggers, teams, script
// definitions) etc
//
if( xfer->getXferMode() == XFER_LOAD ) {
TheGameLogic->startNewGame( TRUE );
}
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Delete any scratch pad maps in the save directory. Scratch pad maps are maps that
* were embedded in previously loaded save game files and temporarily written out as
* their own file so that those map files could be loaded as a part of the load game
* process */
// ------------------------------------------------------------------------------------------------
void GameStateMap::clearScratchPadMaps( void )
{
// remember the current directory
char currentDirectory[ _MAX_PATH ];
GetCurrentDirectory( _MAX_PATH, currentDirectory );
// switch into the save directory
SetCurrentDirectory( TheGameState->getSaveDirectory().str() );
// iterate all items in the directory
AsciiString fileToDelete;
WIN32_FIND_DATA item; // search item
HANDLE hFile = INVALID_HANDLE_VALUE; // handle for search resources
Bool done = FALSE;
Bool first = TRUE;
while( done == FALSE )
{
// first, clear flag for deleting file
fileToDelete.clear();
// if our first time through we need to start the search
if( first )
{
// start search
hFile = FindFirstFile( "*", &item );
if( hFile == INVALID_HANDLE_VALUE )
return;
// we are no longer on our first item
first = FALSE;
} // end if, first
// see if this is a file, and therefore a possible .map file
if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
// see if there is a ".map" at end of this filename
Char *c = strrchr( item.cFileName, '.' );
if( c && stricmp( c, ".map" ) == 0 )
fileToDelete.set( item.cFileName ); // we want to delete this one
} // end if
//
// find the next file before we delete this one, this is probably not necessary
// to strcuture things this way so that the find next occurs before the file
// delete, but it seems more correct to do so
//
if( FindNextFile( hFile, &item ) == 0 )
done = TRUE;
// delete file if set
if( fileToDelete.isEmpty() == FALSE )
DeleteFile( fileToDelete.str() );
} // end while
// close search resources
FindClose( hFile );
// restore our directory to the current directory
SetCurrentDirectory( currentDirectory );
} // end clearScratchPadMaps

View file

@ -0,0 +1,56 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Snapshot.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, February 2002
// Desc: The Snapshot object is the base class interface for data structures that will
// be considered during game saves, loads, and CRC checks.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameState.h"
#include "Common/Snapshot.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Snapshot::Snapshot( void )
{
} // end Snapshot
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Snapshot::~Snapshot( void )
{
//
// if we're loading, there are pathological cases where we could destroy snapshots while
// there is an entry for them in the post processing list ... need to clean this up
//
///@ todo, this might be needed in theory in the future, but iterating the post process
// list in the game state is expensive because it's HUGE!
//
// TheGameState->notifySnapshotDeleted();
} // end ~Snapshot

View file

@ -0,0 +1,634 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#if defined(_DEBUG) || defined(_INTERNAL) || defined(IG_DEBUG_STACKTRACE)
#pragma pack(push, 8)
#pragma comment(linker, "/defaultlib:Dbghelp.lib")
#include "Common/StackDump.h"
#include "Common/Debug.h"
//*****************************************************************************
// Prototypes
//*****************************************************************************
BOOL InitSymbolInfo(void);
void UninitSymbolInfo(void);
void MakeStackTrace(DWORD myeip,DWORD myesp,DWORD myebp, int skipFrames, void (*callback)(const char*));
void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* linenumber, unsigned int* address);
void WriteStackLine(void*address, void (*callback)(const char*));
//*****************************************************************************
// Mis-named globals :-)
//*****************************************************************************
static CONTEXT gsContext;
static Bool gsInit=FALSE;
BOOL (__stdcall *gsSymGetLineFromAddr)(
IN HANDLE hProcess,
IN DWORD dwAddr,
OUT PDWORD pdwDisplacement,
OUT PIMAGEHLP_LINE Line
);
//*****************************************************************************
//*****************************************************************************
void StackDumpDefaultHandler(const char*line)
{
DEBUG_LOG((line));
}
//*****************************************************************************
//*****************************************************************************
void StackDump(void (*callback)(const char*))
{
if (callback == NULL)
{
callback = StackDumpDefaultHandler;
}
InitSymbolInfo();
DWORD myeip,myesp,myebp;
_asm
{
MYEIP1:
mov eax, MYEIP1
mov dword ptr [myeip] , eax
mov eax, esp
mov dword ptr [myesp] , eax
mov eax, ebp
mov dword ptr [myebp] , eax
}
MakeStackTrace(myeip,myesp,myebp, 2, callback);
}
//*****************************************************************************
//*****************************************************************************
void StackDumpFromContext(DWORD eip,DWORD esp,DWORD ebp, void (*callback)(const char*))
{
if (callback == NULL)
{
callback = StackDumpDefaultHandler;
}
InitSymbolInfo();
MakeStackTrace(eip,esp,ebp, 0, callback);
}
//*****************************************************************************
//*****************************************************************************
BOOL InitSymbolInfo()
{
if (gsInit == TRUE)
return TRUE;
gsInit = TRUE;
atexit(UninitSymbolInfo);
// See if we have the line from address function
// We use GetProcAddress to stop link failures at dll loadup
HINSTANCE hInstDebugHlp = GetModuleHandle("dbghelp.dll");
gsSymGetLineFromAddr = (BOOL (__stdcall *)( IN HANDLE,IN DWORD,OUT PDWORD,OUT PIMAGEHLP_LINE))
GetProcAddress(hInstDebugHlp , "SymGetLineFromAddr");
char pathname[_MAX_PATH+1];
char drive[10];
char directory[_MAX_PATH+1];
HANDLE process;
::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST);
process = GetCurrentProcess();
//Get the apps name
::GetModuleFileName(NULL, pathname, _MAX_PATH);
// turn it into a search path
_splitpath(pathname, drive, directory, NULL, NULL);
sprintf(pathname, "%s:\\%s", drive, directory);
// append the current directory to build a search path for SymInit
::lstrcat(pathname, ";.;");
if(::SymInitialize(process, pathname, FALSE))
{
// regenerate the name of the app
::GetModuleFileName(NULL, pathname, _MAX_PATH);
if(::SymLoadModule(process, NULL, pathname, NULL, 0, 0))
{
//Load any other relevant modules (ie dlls) here
return TRUE;
}
::SymCleanup(process);
}
return(FALSE);
}
//*****************************************************************************
//*****************************************************************************
void UninitSymbolInfo(void)
{
if (gsInit == FALSE)
{
return;
}
gsInit = FALSE;
::SymCleanup(GetCurrentProcess());
}
//*****************************************************************************
//*****************************************************************************
void MakeStackTrace(DWORD myeip,DWORD myesp,DWORD myebp, int skipFrames, void (*callback)(const char*))
{
STACKFRAME stack_frame;
BOOL b_ret = TRUE;
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
memset(&gsContext, 0, sizeof(CONTEXT));
gsContext.ContextFlags = CONTEXT_FULL;
memset(&stack_frame, 0, sizeof(STACKFRAME));
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrPC.Offset = myeip;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = myesp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = myebp;
{
/*
if(GetThreadContext(thread, &gsContext))
{
memset(&stack_frame, 0, sizeof(STACKFRAME));
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrPC.Offset = gsContext.Eip;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = gsContext.Esp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = gsContext.Ebp;
*/
//{
callback("Call Stack\n**********\n");
// Skip some ?
unsigned int skip = skipFrames;
while (b_ret&&skip)
{
b_ret = StackWalk( IMAGE_FILE_MACHINE_I386,
process,
thread,
&stack_frame,
NULL, //&gsContext,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
skip--;
}
skip = 30;
while(b_ret&&skip)
{
b_ret = StackWalk( IMAGE_FILE_MACHINE_I386,
process,
thread,
&stack_frame,
NULL, //&gsContext,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
if (b_ret) WriteStackLine((void *) stack_frame.AddrPC.Offset, callback);
skip--;
}
}
}
//*****************************************************************************
//*****************************************************************************
void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* linenumber, unsigned int* address)
{
InitSymbolInfo();
if (name)
{
strcpy(name, "<Unknown>");
}
if (filename)
{
strcpy(filename, "<Unknown>");
}
if (linenumber)
{
*linenumber = 0xFFFFFFFF;
}
if (address)
{
*address = 0xFFFFFFFF;
}
ULONG displacement = 0;
HANDLE process = ::GetCurrentProcess();
char symbol_buffer[512 + sizeof(IMAGEHLP_SYMBOL)];
memset(symbol_buffer, 0, sizeof(symbol_buffer));
PIMAGEHLP_SYMBOL psymbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
psymbol->SizeOfStruct = sizeof(symbol_buffer);
psymbol->MaxNameLength = 512;
if (SymGetSymFromAddr(process, (DWORD) pointer, &displacement, psymbol))
{
if (name)
{
strcpy(name, psymbol->Name);
strcat(name, "();");
}
// Get line now
if (gsSymGetLineFromAddr)
{
// Unsupported for win95/98 at least with my current dbghelp.dll
IMAGEHLP_LINE line;
memset(&line,0,sizeof(line));
line.SizeOfStruct = sizeof(line);
if (gsSymGetLineFromAddr(process, (DWORD) pointer, &displacement, &line))
{
if (filename)
{
strcpy(filename, line.FileName);
}
if (linenumber)
{
*linenumber = (unsigned int)line.LineNumber;
}
if (address)
{
*address = (unsigned int)line.Address;
}
}
}
}
}
//*****************************************************************************
// Gets last x addresses from the stack
//*****************************************************************************
void FillStackAddresses(void**addresses, unsigned int count, unsigned int skip)
{
InitSymbolInfo();
STACKFRAME stack_frame;
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
memset(&gsContext, 0, sizeof(CONTEXT));
gsContext.ContextFlags = CONTEXT_FULL;
DWORD myeip,myesp,myebp;
_asm
{
MYEIP2:
mov eax, MYEIP2
mov dword ptr [myeip] , eax
mov eax, esp
mov dword ptr [myesp] , eax
mov eax, ebp
mov dword ptr [myebp] , eax
xor eax,eax
}
memset(&stack_frame, 0, sizeof(STACKFRAME));
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrPC.Offset = myeip;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = myesp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = myebp;
{
/*
if(GetThreadContext(thread, &gsContext))
{
memset(&stack_frame, 0, sizeof(STACKFRAME));
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrPC.Offset = gsContext.Eip;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = gsContext.Esp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = gsContext.Ebp;
*/
Bool stillgoing = TRUE;
// unsigned int cd = count;
// Skip some?
while (stillgoing&&skip)
{
stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386,
process,
thread,
&stack_frame,
NULL, //&gsContext,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL) != 0;
skip--;
}
while(stillgoing&&count)
{
stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386,
process,
thread,
&stack_frame,
NULL, //&gsContext,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL) != 0;
if (stillgoing)
{
*addresses = (void*)stack_frame.AddrPC.Offset;
addresses++;
count--;
}
}
// Fill remainder
while (count)
{
*addresses = NULL;
addresses++;
count--;
}
}
/*
else
{
memset(addresses,NULL,count*sizeof(void*));
}
*/
}
//*****************************************************************************
// Do full stack dump using an address array
//*****************************************************************************
void StackDumpFromAddresses(void**addresses, unsigned int count, void (*callback)(const char *))
{
if (callback == NULL)
{
callback = StackDumpDefaultHandler;
}
InitSymbolInfo();
while ((count--) && (*addresses!=NULL))
{
WriteStackLine(*addresses,callback);
addresses++;
}
}
AsciiString g_LastErrorDump;
//*****************************************************************************
//*****************************************************************************
void WriteStackLine(void*address, void (*callback)(const char*))
{
static char line[MAX_PATH];
static char function_name[512];
static char filename[MAX_PATH];
unsigned int linenumber;
unsigned int addr;
GetFunctionDetails(address, function_name, filename, &linenumber, &addr);
sprintf(line, " %s(%d) : %s 0x%08p", filename, linenumber, function_name, address);
if (g_LastErrorDump.isNotEmpty()) {
g_LastErrorDump.concat(line);
g_LastErrorDump.concat("\n");
}
callback(line);
callback("\n");
}
//*****************************************************************************
//*****************************************************************************
void DumpExceptionInfo( unsigned int u, EXCEPTION_POINTERS* e_info )
{
DEBUG_LOG(( "\n********** EXCEPTION DUMP ****************\n" ));
/*
** List of possible exceptions
*/
g_LastErrorDump.clear();
static const unsigned int _codes[] = {
EXCEPTION_ACCESS_VIOLATION,
EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
EXCEPTION_BREAKPOINT,
EXCEPTION_DATATYPE_MISALIGNMENT,
EXCEPTION_FLT_DENORMAL_OPERAND,
EXCEPTION_FLT_DIVIDE_BY_ZERO,
EXCEPTION_FLT_INEXACT_RESULT,
EXCEPTION_FLT_INVALID_OPERATION,
EXCEPTION_FLT_OVERFLOW,
EXCEPTION_FLT_STACK_CHECK,
EXCEPTION_FLT_UNDERFLOW,
EXCEPTION_ILLEGAL_INSTRUCTION,
EXCEPTION_IN_PAGE_ERROR,
EXCEPTION_INT_DIVIDE_BY_ZERO,
EXCEPTION_INT_OVERFLOW,
EXCEPTION_INVALID_DISPOSITION,
EXCEPTION_NONCONTINUABLE_EXCEPTION,
EXCEPTION_PRIV_INSTRUCTION,
EXCEPTION_SINGLE_STEP,
EXCEPTION_STACK_OVERFLOW,
0xffffffff
};
/*
** Information about each exception type.
*/
static char const * _code_txt[] = {
"Error code: EXCEPTION_ACCESS_VIOLATION\nDescription: The thread tried to read from or write to a virtual address for which it does not have the appropriate access.",
"Error code: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\nDescription: The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.",
"Error code: EXCEPTION_BREAKPOINT\nDescription: A breakpoint was encountered.",
"Error code: EXCEPTION_DATATYPE_MISALIGNMENT\nDescription: The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.",
"Error code: EXCEPTION_FLT_DENORMAL_OPERAND\nDescription: One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.",
"Error code: EXCEPTION_FLT_DIVIDE_BY_ZERO\nDescription: The thread tried to divide a floating-point value by a floating-point divisor of zero.",
"Error code: EXCEPTION_FLT_INEXACT_RESULT\nDescription: The result of a floating-point operation cannot be represented exactly as a decimal fraction.",
"Error code: EXCEPTION_FLT_INVALID_OPERATION\nDescription: Some strange unknown floating point operation was attempted.",
"Error code: EXCEPTION_FLT_OVERFLOW\nDescription: The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.",
"Error code: EXCEPTION_FLT_STACK_CHECK\nDescription: The stack overflowed or underflowed as the result of a floating-point operation.",
"Error code: EXCEPTION_FLT_UNDERFLOW\nDescription: The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.",
"Error code: EXCEPTION_ILLEGAL_INSTRUCTION\nDescription: The thread tried to execute an invalid instruction.",
"Error code: EXCEPTION_IN_PAGE_ERROR\nDescription: The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.",
"Error code: EXCEPTION_INT_DIVIDE_BY_ZERO\nDescription: The thread tried to divide an integer value by an integer divisor of zero.",
"Error code: EXCEPTION_INT_OVERFLOW\nDescription: The result of an integer operation caused a carry out of the most significant bit of the result.",
"Error code: EXCEPTION_INVALID_DISPOSITION\nDescription: An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.",
"Error code: EXCEPTION_NONCONTINUABLE_EXCEPTION\nDescription: The thread tried to continue execution after a noncontinuable exception occurred.",
"Error code: EXCEPTION_PRIV_INSTRUCTION\nDescription: The thread tried to execute an instruction whose operation is not allowed in the current machine mode.",
"Error code: EXCEPTION_SINGLE_STEP\nDescription: A trace trap or other single-instruction mechanism signaled that one instruction has been executed.",
"Error code: EXCEPTION_STACK_OVERFLOW\nDescription: The thread used up its stack.",
"Error code: ?????\nDescription: Unknown exception."
};
DEBUG_LOG( ("Dump exception info\n") );
CONTEXT *context = e_info->ContextRecord;
/*
** The following are set for access violation only
*/
int access_read_write=-1;
unsigned long access_address = 0;
AsciiString msg;
// DOUBLE_DEBUG does a DEBUG_LOG, and concats to g_LastErrorDump. jba.
#define DOUBLE_DEBUG(x) { msg.format x; g_LastErrorDump.concat(msg); DEBUG_LOG( x ); }
if ( e_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION )
{
DOUBLE_DEBUG (("Exception is access violation\n"));
access_read_write = e_info->ExceptionRecord->ExceptionInformation[0]; // 0=read, 1=write
access_address = e_info->ExceptionRecord->ExceptionInformation[1];
}
else
{
DOUBLE_DEBUG (("Exception code is %x\n", e_info->ExceptionRecord->ExceptionCode));
}
Int *winMainAddr = (Int *)WinMain;
DOUBLE_DEBUG(("WinMain at %x\n", winMainAddr));
/*
** Match the exception type with the error string and print it out
*/
for ( int i=0 ; _codes[i] != 0xffffffff ; i++ )
{
if ( _codes[i] == e_info->ExceptionRecord->ExceptionCode )
{
DEBUG_LOG ( ("Found exception description\n") );
break;
}
}
DOUBLE_DEBUG( ("%s\n", _code_txt[i]));
/** For access violations, print out the violation address and if it was read or write.
*/
if ( e_info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION )
{
if ( access_read_write )
{
DOUBLE_DEBUG( ("Access address:%08X was written to.\n", access_address));
}
else
{
DOUBLE_DEBUG( ("Access address:%08X was read from.\n", access_address));
}
}
DOUBLE_DEBUG (("\nStack Dump:\n"));
StackDumpFromContext(context->Eip, context->Esp, context->Ebp, NULL);
DOUBLE_DEBUG (("\nDetails:\n"));
DOUBLE_DEBUG (("Register dump...\n"));
/*
** Dump the registers.
*/
DOUBLE_DEBUG ( ( "Eip:%08X\tEsp:%08X\tEbp:%08X\n", context->Eip, context->Esp, context->Ebp));
DOUBLE_DEBUG ( ( "Eax:%08X\tEbx:%08X\tEcx:%08X\n", context->Eax, context->Ebx, context->Ecx));
DOUBLE_DEBUG ( ( "Edx:%08X\tEsi:%08X\tEdi:%08X\n", context->Edx, context->Esi, context->Edi));
DOUBLE_DEBUG ( ( "EFlags:%08X \n", context->EFlags));
DOUBLE_DEBUG ( ( "CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x\n", context->SegCs, context->SegSs, context->SegDs, context->SegEs, context->SegFs, context->SegGs));
/*
** Dump the bytes at EIP. This will make it easier to match the crash address with later versions of the game.
*/
char scrap[512];
DOUBLE_DEBUG ( ("EIP bytes dump...\n"));
wsprintf (scrap, "\nBytes at CS:EIP (%08X) : ", context->Eip);
unsigned char *eip_ptr = (unsigned char *) (context->Eip);
char bytestr[32];
for (int c = 0 ; c < 32 ; c++)
{
if (IsBadReadPtr(eip_ptr, 1))
{
lstrcat (scrap, "?? ");
}
else
{
sprintf (bytestr, "%02X ", *eip_ptr);
strcat (scrap, bytestr);
}
eip_ptr++;
}
strcat (scrap, "\n");
DOUBLE_DEBUG ( ( (scrap)));
DEBUG_LOG(( "********** END EXCEPTION DUMP ****************\n\n" ));
}
#pragma pack(pop)
#endif

View file

@ -0,0 +1,285 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: RTS
//
// Module: IO
//
// File name: StreamingArchiveFile.cpp
//
// Created: 12/06/02
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
#include <sys/stat.h>
#include "Common/AsciiString.h"
#include "Common/FileSystem.h"
#include "Common/StreamingArchiveFile.h"
#include "Common/PerfTimer.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//=================================================================
// StreamingArchiveFile::StreamingArchiveFile
//=================================================================
StreamingArchiveFile::StreamingArchiveFile()
: m_file(NULL),
m_startingPos(0),
m_size(0),
m_curPos(0)
{
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//=================================================================
// StreamingArchiveFile::~StreamingArchiveFile
//=================================================================
StreamingArchiveFile::~StreamingArchiveFile()
{
File::close();
}
//=================================================================
// StreamingArchiveFile::open
//=================================================================
/**
* This function opens a file using the standard C open() call. Access flags
* are mapped to the appropriate open flags. Returns true if file was opened
* successfully.
*/
//=================================================================
//DECLARE_PERF_TIMER(StreamingArchiveFile)
Bool StreamingArchiveFile::open( const Char *filename, Int access )
{
//USE_PERF_TIMER(StreamingArchiveFile)
File *file = TheFileSystem->openFile( filename, access );
if ( file == NULL )
{
return FALSE;
}
return (open( file ) != NULL);
}
//============================================================================
// StreamingArchiveFile::open
//============================================================================
Bool StreamingArchiveFile::open( File *file )
{
return TRUE;
}
//============================================================================
// StreamingArchiveFile::openFromArchive
//============================================================================
Bool StreamingArchiveFile::openFromArchive(File *archiveFile, const AsciiString& filename, Int offset, Int size)
{
//USE_PERF_TIMER(StreamingArchiveFile)
if (archiveFile == NULL) {
return FALSE;
}
if (File::open(filename.str(), File::READ | File::BINARY | File::STREAMING) == FALSE) {
return FALSE;
}
m_file = archiveFile;
m_startingPos = offset;
m_size = size;
m_curPos = 0;
if (m_file->seek(offset, File::START) != offset) {
return FALSE;
}
if (m_file->seek(size) != m_startingPos + size) {
return FALSE;
}
// We know this will succeed.
m_file->seek(offset, File::START);
m_nameStr = filename;
return TRUE;
}
//=================================================================
// StreamingArchiveFile::close
//=================================================================
/**
* Closes the current file if it is open.
* Must call StreamingArchiveFile::close() for each successful StreamingArchiveFile::open() call.
*/
//=================================================================
void StreamingArchiveFile::close( void )
{
File::close();
}
//=================================================================
// StreamingArchiveFile::read
//=================================================================
// if buffer is null, just advance the current position by 'bytes'
Int StreamingArchiveFile::read( void *buffer, Int bytes )
{
if (!m_file) {
return 0;
}
// There shouldn't be a way that this can fail, because we've already verified that the file
// contains at least this many bits.
m_file->seek(m_startingPos + m_curPos, File::START);
if (bytes + m_curPos > m_size)
bytes = m_size - m_curPos;
Int bytesRead = m_file->read(buffer, bytes);
m_curPos += bytesRead;
return bytesRead;
}
//=================================================================
// StreamingArchiveFile::write
//=================================================================
Int StreamingArchiveFile::write( const void *buffer, Int bytes )
{
DEBUG_CRASH(("Cannot write to streaming files.\n"));
return -1;
}
//=================================================================
// StreamingArchiveFile::seek
//=================================================================
Int StreamingArchiveFile::seek( Int pos, seekMode mode)
{
Int newPos;
switch( mode )
{
case START:
newPos = pos;
break;
case CURRENT:
newPos = m_curPos + pos;
break;
case END:
DEBUG_ASSERTCRASH(pos <= 0, ("StreamingArchiveFile::seek - position should be <= 0 for a seek starting from the end."));
newPos = m_size + pos;
break;
default:
// bad seek mode
return -1;
}
if ( newPos < 0 )
{
newPos = 0;
}
else if ( newPos > m_size )
{
newPos = m_size;
}
m_curPos = newPos;
return m_curPos;
}

View file

@ -0,0 +1,328 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: String
//
// File name: WSYS_String.cpp
//
// Created: 11/5/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "Common/string.h"
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// WSYS_String::WSYS_String
//============================================================================
WSYS_String::WSYS_String(const Char *string)
: m_data(NULL)
{
set(string);
}
//============================================================================
// WSYS_String::~WSYS_String
//============================================================================
WSYS_String::~WSYS_String()
{
delete [] m_data;
}
//============================================================================
// WSYS_String::operator==
//============================================================================
Bool WSYS_String::operator== (const char *rvalue) const
{
return strcmp( get(), rvalue) == 0;
}
//============================================================================
// WSYS_String::operator!=
//============================================================================
Bool WSYS_String::operator!= (const char *rvalue) const
{
return strcmp( get(), rvalue) != 0;
}
//============================================================================
// WSYS_String::operator=
//============================================================================
const WSYS_String& WSYS_String::operator= (const WSYS_String &string)
{
set( string.get());
return (*this);
}
//============================================================================
// WSYS_String::operator=
//============================================================================
const WSYS_String& WSYS_String::operator= (const Char *string)
{
set( string );
return (*this);
}
//============================================================================
// WSYS_String::operator+=
//============================================================================
const WSYS_String& WSYS_String::operator+= (const WSYS_String &string)
{
Char *buffer = MSGNEW("WSYS_String") Char [ length() + string.length() + 1 ]; // pool[]ify
if ( buffer != NULL )
{
strcpy( buffer, m_data );
strcat( buffer, string.get() );
delete [] m_data;
m_data = buffer;
}
return (*this);
}
//============================================================================
// WSYS_String::operator+=
//============================================================================
const WSYS_String& WSYS_String::operator+= (const Char *string)
{
if ( string != NULL )
{
Char *buffer = MSGNEW("WSYS_String") Char [ length() + strlen( string ) + 1 ];
if ( buffer != NULL )
{
strcpy( buffer, m_data );
strcat( buffer, string );
delete [] m_data;
m_data = buffer;
}
}
return (*this);
}
//============================================================================
// operator+ (const WSYS_String &string1, const WSYS_String &string2)
//============================================================================
WSYS_String operator+ (const WSYS_String &string1, const WSYS_String &string2)
{
WSYS_String temp(string1);
temp += string2;
return temp;
}
//============================================================================
// operator+ (const Char *string1, const WSYS_String &string2)
//============================================================================
WSYS_String operator+ (const Char *string1, const WSYS_String &string2)
{
WSYS_String temp(string1);
temp += string2;
return temp;
}
//============================================================================
// operator+ (const WSYS_String &string1, const Char *string2)
//============================================================================
WSYS_String operator+ (const WSYS_String &string1, const Char *string2)
{
WSYS_String temp(string1);
temp += string2;
return temp;
}
//============================================================================
// WSYS_String::length
//============================================================================
Int WSYS_String::length(void) const
{
return strlen( m_data );
}
//============================================================================
// WSYS_String::isEmpty
//============================================================================
Bool WSYS_String::isEmpty(void) const
{
return m_data[0] == 0;
}
//============================================================================
// WSYS_String::format
//============================================================================
Int _cdecl WSYS_String::format(const Char *format, ...)
{
Int result = 0;
char *buffer = MSGNEW("WSYS_String") char[100*1024];
if ( buffer )
{
va_list args;
va_start( args, format ); /* Initialize variable arguments. */
result = vsprintf ( buffer, format, args );
va_end( args );
set( buffer );
delete [] buffer;
}
else
{
set("");
}
return result;
}
//============================================================================
// WSYS_String::set
//============================================================================
void WSYS_String::set( const Char *string )
{
delete [] m_data;
if ( string == NULL )
{
string = "";
}
m_data = MSGNEW("WSYS_String") Char [ strlen(string) + 1];
strcpy ( m_data, string );
}
//============================================================================
// WSYS_String::makeUpperCase
//============================================================================
void WSYS_String::makeUpperCase( void )
{
Char *chr = m_data;
Char ch;
while( (ch = *chr) )
{
*chr++ = (Char) toupper( ch );
}
}
//============================================================================
// WSYS_String::makeLowerCase
//============================================================================
void WSYS_String::makeLowerCase( void )
{
Char *chr = m_data;
Char ch;
while( (ch = *chr) )
{
*chr++ = (Char) tolower( ch );
}
}

View file

@ -0,0 +1,234 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: SubsystemInterface.cpp
// ----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/SubsystemInterface.h"
#include "Common/Xfer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#ifdef DUMP_PERF_STATS
#include "GameLogic\GameLogic.h"
#include "Common/PerfTimer.h"
Real SubsystemInterface::s_msConsumed = 0;
#endif
//-----------------------------------------------------------------------------
SubsystemInterface::SubsystemInterface()
#ifdef DUMP_PERF_STATS
:m_curDrawTime(0),
m_startDrawTimeConsumed(0),
m_startTimeConsumed(0),
m_curUpdateTime(0)
#endif
{
if (TheSubsystemList) {
TheSubsystemList->addSubsystem(this);
}
}
SubsystemInterface::~SubsystemInterface()
{
if (TheSubsystemList) {
TheSubsystemList->removeSubsystem(this);
}
}
#ifdef DUMP_PERF_STATS
void SubsystemInterface::UPDATE(void)
{
__int64 startTime64;
__int64 endTime64,freq64;
GetPrecisionTimerTicksPerSec(&freq64);
GetPrecisionTimer(&startTime64);
m_startTimeConsumed = s_msConsumed;
update();
GetPrecisionTimer(&endTime64);
m_curUpdateTime = ((double)(endTime64-startTime64))/((double)(freq64));
Real subTime = s_msConsumed - m_startTimeConsumed;
if (m_name.isEmpty()) return;
if (m_curUpdateTime > 0.00001) {
//DEBUG_LOG(("Subsys %s total time %.2f, subTime %.2f, net time %.2f\n",
// m_name.str(), m_curUpdateTime*1000, subTime*1000, (m_curUpdateTime-subTime)*1000 ));
m_curUpdateTime -= subTime;
s_msConsumed += m_curUpdateTime;
} else {
m_curUpdateTime = 0;
}
}
void SubsystemInterface::DRAW(void)
{
__int64 startTime64;
__int64 endTime64,freq64;
GetPrecisionTimerTicksPerSec(&freq64);
GetPrecisionTimer(&startTime64);
m_startDrawTimeConsumed = s_msConsumed;
draw();
GetPrecisionTimer(&endTime64);
m_curDrawTime = ((double)(endTime64-startTime64))/((double)(freq64));
Real subTime = s_msConsumed - m_startDrawTimeConsumed;
if (m_name.isEmpty()) return;
if (m_curDrawTime > 0.00001) {
//DEBUG_LOG(("Subsys %s total time %.2f, subTime %.2f, net time %.2f\n",
// m_name.str(), m_curUpdateTime*1000, subTime*1000, (m_curUpdateTime-subTime)*1000 ));
m_curDrawTime -= subTime;
s_msConsumed += m_curDrawTime;
} else {
m_curDrawTime = 0;
}
}
#endif
//-----------------------------------------------------------------------------
SubsystemInterfaceList::SubsystemInterfaceList()
{
}
//-----------------------------------------------------------------------------
SubsystemInterfaceList::~SubsystemInterfaceList()
{
DEBUG_ASSERTCRASH(m_subsystems.empty(), ("not empty"));
shutdownAll();
}
//-----------------------------------------------------------------------------
void SubsystemInterfaceList::addSubsystem(SubsystemInterface* sys)
{
#ifdef DUMP_PERF_STATS
m_allSubsystems.push_back(sys);
#endif
}
//-----------------------------------------------------------------------------
void SubsystemInterfaceList::removeSubsystem(SubsystemInterface* sys)
{
#ifdef DUMP_PERF_STATS
for (SubsystemList::iterator it = m_allSubsystems.begin(); it != m_subsystems.end(); ++it)
{
if ( (*it) == sys) {
m_allSubsystems.erase(it);
break;
}
}
#endif
}
//-----------------------------------------------------------------------------
void SubsystemInterfaceList::initSubsystem(SubsystemInterface* sys, const char* path1, const char* path2, const char* dirpath, Xfer *pXfer, AsciiString name)
{
sys->setName(name);
sys->init();
INI ini;
if (path1)
ini.load(path1, INI_LOAD_OVERWRITE, pXfer );
if (path2)
ini.load(path2, INI_LOAD_OVERWRITE, pXfer );
if (dirpath)
ini.loadDirectory(dirpath, TRUE, INI_LOAD_OVERWRITE, pXfer );
m_subsystems.push_back(sys);
}
//-----------------------------------------------------------------------------
void SubsystemInterfaceList::postProcessLoadAll()
{
for (SubsystemList::iterator it = m_subsystems.begin(); it != m_subsystems.end(); ++it)
{
(*it)->postProcessLoad();
}
}
//-----------------------------------------------------------------------------
void SubsystemInterfaceList::resetAll()
{
// for (SubsystemList::iterator it = m_subsystems.begin(); it != m_subsystems.end(); ++it)
for (SubsystemList::reverse_iterator it = m_subsystems.rbegin(); it != m_subsystems.rend(); ++it)
{
(*it)->reset();
}
}
//-----------------------------------------------------------------------------
void SubsystemInterfaceList::shutdownAll()
{
// must go in reverse order!
for (SubsystemList::reverse_iterator it = m_subsystems.rbegin(); it != m_subsystems.rend(); ++it)
{
SubsystemInterface* sys = *it;
delete sys;
}
m_subsystems.clear();
}
#ifdef DUMP_PERF_STATS
//-----------------------------------------------------------------------------
AsciiString SubsystemInterfaceList::dumpTimesForAll()
{
AsciiString buffer;
buffer = "ALL SUBSYSTEMS:\n";
//buffer.format("\nSUBSYSTEMS: total time %.2f MS\n",
// SubsystemInterface::getTotalTime()*1000.0f);
Real misc = 0;
Real total = 0;
SubsystemInterface::clearTotalTime();
for (SubsystemList::reverse_iterator it = m_allSubsystems.rbegin(); it != m_allSubsystems.rend(); ++it)
{
SubsystemInterface* sys = *it;
total += sys->getUpdateTime();
if (sys->getUpdateTime()>0.00001f) {
AsciiString curLine;
curLine.format(" Time %02.2f MS update() %s \n", sys->getUpdateTime()*1000.0f, sys->getName().str());
buffer.concat(curLine);
} else {
misc += sys->getUpdateTime();
}
total += sys->getDrawTime();
if (sys->getDrawTime()>0.00001f) {
AsciiString curLine;
curLine.format(" Time %02.2f MS draw () %s \n", sys->getDrawTime()*1000.0f, sys->getName().str());
buffer.concat(curLine);
} else {
misc += sys->getDrawTime();
}
}
AsciiString tmp;
tmp.format("TOTAL %.2f MS, Misc time %.2f MS\n", total*1000.0f, misc*1000.0f);
buffer.concat(tmp);
return buffer;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,363 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: UnicodeString.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: UnicodeString.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: General-purpose string classes
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CriticalSection.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// -----------------------------------------------------
/*static*/ UnicodeString UnicodeString::TheEmptyString;
// -----------------------------------------------------
#ifdef _DEBUG
void UnicodeString::validate() const
{
if (!m_data) return;
DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
DEBUG_ASSERTCRASH(wcslen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long for storage"));
}
#endif
// -----------------------------------------------------
UnicodeString::UnicodeString(const UnicodeString& stringSrc) : m_data(stringSrc.m_data)
{
ScopedCriticalSection scopedCriticalSection(TheUnicodeStringCriticalSection);
if (m_data)
++m_data->m_refCount;
validate();
}
// -----------------------------------------------------
void UnicodeString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const WideChar* strToCopy, const WideChar* strToCat)
{
validate();
if (m_data &&
m_data->m_refCount == 1 &&
m_data->m_numCharsAllocated >= numCharsNeeded)
{
// no buffer manhandling is needed (it's already large enough, and unique to us)
if (strToCopy)
wcscpy(m_data->peek(), strToCopy);
if (strToCat)
wcscat(m_data->peek(), strToCat);
return;
}
int minBytes = sizeof(UnicodeStringData) + numCharsNeeded*sizeof(WideChar);
if (minBytes > MAX_LEN)
throw ERROR_OUT_OF_MEMORY;
int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
UnicodeStringData* newData = (UnicodeStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_UnicodeString::ensureUniqueBufferOfSize");
newData->m_refCount = 1;
newData->m_numCharsAllocated = (actualBytes - sizeof(UnicodeStringData))/sizeof(WideChar);
#if defined(_DEBUG) || defined(_INTERNAL)
newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
#endif
if (m_data && preserveData)
wcscpy(newData->peek(), m_data->peek());
else
newData->peek()[0] = 0;
// do these BEFORE releasing the old buffer, so that self-copies
// or self-cats will work correctly.
if (strToCopy)
wcscpy(newData->peek(), strToCopy);
if (strToCat)
wcscat(newData->peek(), strToCat);
releaseBuffer();
m_data = newData;
validate();
}
// -----------------------------------------------------
void UnicodeString::releaseBuffer()
{
ScopedCriticalSection scopedCriticalSection(TheUnicodeStringCriticalSection);
validate();
if (m_data)
{
if (--m_data->m_refCount == 0)
{
TheDynamicMemoryAllocator->freeBytes(m_data);
}
m_data = 0;
}
}
// -----------------------------------------------------
UnicodeString::UnicodeString(const WideChar* s) : m_data(0)
{
int len = wcslen(s);
if (len)
{
ensureUniqueBufferOfSize(len + 1, false, s, NULL);
}
validate();
}
// -----------------------------------------------------
void UnicodeString::set(const UnicodeString& stringSrc)
{
ScopedCriticalSection scopedCriticalSection(TheUnicodeStringCriticalSection);
validate();
if (&stringSrc != this)
{
releaseBuffer();
m_data = stringSrc.m_data;
if (m_data)
++m_data->m_refCount;
}
validate();
}
// -----------------------------------------------------
void UnicodeString::set(const WideChar* s)
{
validate();
if (!m_data || s != peek())
{
int len = s ? wcslen(s) : 0;
if (len)
{
ensureUniqueBufferOfSize(len + 1, false, s, NULL);
}
else
{
releaseBuffer();
}
}
validate();
}
// -----------------------------------------------------
WideChar* UnicodeString::getBufferForRead(Int len)
{
validate();
DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
validate();
return peek();
}
// -----------------------------------------------------
void UnicodeString::translate(const AsciiString& stringSrc)
{
validate();
/// @todo srj put in a real translation here; this will only work for 7-bit ascii
clear();
Int len = stringSrc.getLength();
for (Int i = 0; i < len; i++)
concat((WideChar)stringSrc.getCharAt(i));
validate();
}
// -----------------------------------------------------
void UnicodeString::concat(const WideChar* s)
{
validate();
int addlen = wcslen(s);
if (addlen == 0)
return; // my, that was easy
if (m_data)
{
ensureUniqueBufferOfSize(getLength() + addlen + 1, true, NULL, s);
}
else
{
set(s);
}
validate();
}
// -----------------------------------------------------
void UnicodeString::trim()
{
validate();
if (m_data)
{
const WideChar *c = peek();
// Strip leading white space from the string.
while (c && iswspace(*c))
{
c++;
}
if (c != peek())
{
set(c);
}
if (m_data) // another check, because the previous set() could erase m_data
{
// Clip trailing white space from the string.
int len = wcslen(peek());
for (int index = len-1; index >= 0; index--)
{
if (iswspace(getCharAt(index)))
{
removeLastChar();
}
else
{
break;
}
}
}
}
validate();
}
// -----------------------------------------------------
void UnicodeString::removeLastChar()
{
validate();
if (m_data)
{
int len = wcslen(peek());
if (len > 0)
{
ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
peek()[len - 1] = 0;
}
}
validate();
}
// -----------------------------------------------------
void UnicodeString::format(UnicodeString format, ...)
{
validate();
va_list args;
va_start(args, format);
format_va(format, args);
va_end(args);
validate();
}
// -----------------------------------------------------
void UnicodeString::format(const WideChar* format, ...)
{
validate();
va_list args;
va_start(args, format);
format_va(format, args);
va_end(args);
validate();
}
// -----------------------------------------------------
void UnicodeString::format_va(const UnicodeString& format, va_list args)
{
validate();
WideChar buf[MAX_FORMAT_BUF_LEN];
if (_vsnwprintf(buf, sizeof(buf)/sizeof(WideChar)-1, format.str(), args) < 0)
throw ERROR_OUT_OF_MEMORY;
set(buf);
validate();
}
// -----------------------------------------------------
void UnicodeString::format_va(const WideChar* format, va_list args)
{
validate();
WideChar buf[MAX_FORMAT_BUF_LEN];
if (_vsnwprintf(buf, sizeof(buf)/sizeof(WideChar)-1, format, args) < 0)
throw ERROR_OUT_OF_MEMORY;
set(buf);
validate();
}
//-----------------------------------------------------------------------------
Bool UnicodeString::nextToken(UnicodeString* tok, UnicodeString delimiters)
{
if (this->isEmpty() || tok == this)
return false;
if (delimiters.isEmpty())
delimiters = UnicodeString(L" \t\n\r");
Int offset;
offset = wcsspn(peek(), delimiters.str());
WideChar* start = peek() + offset;
offset = wcscspn(start, delimiters.str());
WideChar* end = start + offset;
if (end > start)
{
Int len = end - start;
WideChar* tmp = tok->getBufferForRead(len + 1);
memcpy(tmp, start, len*2);
tmp[len] = 0;
this->set(end);
return true;
}
else
{
this->clear();
tok->clear();
return false;
}
}

View file

@ -0,0 +1,488 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Upgrade.cpp //////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, March 2002
// Desc: Upgrade system for players
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_UPGRADE_TYPE_NAMES
#define DEFINE_VETERANCY_NAMES
#include "Common/Upgrade.h"
#include "Common/Player.h"
#include "Common/Xfer.h"
#include "GameClient/InGameUI.h"
#include "GameClient/Image.h"
// PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
class UpgradeCenter *TheUpgradeCenter = NULL;
///////////////////////////////////////////////////////////////////////////////////////////////////
// UPGRADE ////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Upgrade::Upgrade( const UpgradeTemplate *upgradeTemplate )
{
m_template = upgradeTemplate;
m_status = UPGRADE_STATUS_INVALID;
m_next = NULL;
m_prev = NULL;
} // end Upgrade
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Upgrade::~Upgrade( void )
{
} // end ~Upgrade
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void Upgrade::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Upgrade::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// status
xfer->xferUser( &m_status, sizeof( UpgradeStatusType ) );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void Upgrade::loadPostProcess( void )
{
} // end loadPostProcess
///////////////////////////////////////////////////////////////////////////////////////////////////
// UPGRADE TEMPLATE ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
const FieldParse UpgradeTemplate::m_upgradeFieldParseTable[] =
{
{ "DisplayName", INI::parseAsciiString, NULL, offsetof( UpgradeTemplate, m_displayNameLabel ) },
{ "Type", INI::parseIndexList, UpgradeTypeNames, offsetof( UpgradeTemplate, m_type ) },
{ "BuildTime", INI::parseReal, NULL, offsetof( UpgradeTemplate, m_buildTime ) },
{ "BuildCost", INI::parseInt, NULL, offsetof( UpgradeTemplate, m_cost ) },
{ "ButtonImage", INI::parseAsciiString, NULL, offsetof( UpgradeTemplate, m_buttonImageName ) },
{ "ResearchSound", INI::parseAudioEventRTS, NULL, offsetof( UpgradeTemplate, m_researchSound ) },
{ "UnitSpecificSound", INI::parseAudioEventRTS, NULL, offsetof( UpgradeTemplate, m_unitSpecificSound ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
UpgradeTemplate::UpgradeTemplate( void )
{
//Added By Sadullah Nader
//Initialization(s) inserted
m_cost = 0;
//
m_type = UPGRADE_TYPE_PLAYER;
m_nameKey = NAMEKEY_INVALID;
m_buildTime = 0.0f;
m_upgradeMask = 0;
m_next = NULL;
m_prev = NULL;
m_buttonImage = NULL;
} // end UpgradeTemplate
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
UpgradeTemplate::~UpgradeTemplate( void )
{
} // end ~UpgradeTemplate
//-------------------------------------------------------------------------------------------------
/** Calculate the time it takes (in logic frames) for a player to build this UpgradeTemplate */
//-------------------------------------------------------------------------------------------------
Int UpgradeTemplate::calcTimeToBuild( Player *player ) const
{
#if defined(_DEBUG) || defined(_INTERNAL)
if( player->buildsInstantly() )
{
return 1;
}
#endif
///@todo modify this by power state of player
return m_buildTime * LOGICFRAMES_PER_SECOND;
} // end calcTimeToBuild
//-------------------------------------------------------------------------------------------------
/** Calculate the cost takes this player to build this upgrade */
//-------------------------------------------------------------------------------------------------
Int UpgradeTemplate::calcCostToBuild( Player *player ) const
{
///@todo modify this by any player handicaps
return m_cost;
} // end calcCostToBuild
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
static AsciiString getVetUpgradeName(VeterancyLevel v)
{
AsciiString tmp;
tmp = "Upgrade_Veterancy_";
tmp.concat(TheVeterancyNames[v]);
return tmp;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void UpgradeTemplate::friend_makeVeterancyUpgrade(VeterancyLevel v)
{
m_type = UPGRADE_TYPE_OBJECT; // veterancy "upgrades" are always per-object, not per-player
m_name = getVetUpgradeName(v);
m_nameKey = TheNameKeyGenerator->nameToKey( m_name );
m_displayNameLabel.clear(); // should never be displayed
m_buildTime = 0.0f;
m_cost = 0.0f;
// leave this alone.
//m_upgradeMask = ???;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void UpgradeTemplate::cacheButtonImage()
{
if( m_buttonImageName.isNotEmpty() )
{
m_buttonImage = TheMappedImageCollection->findImageByName( m_buttonImageName );
DEBUG_ASSERTCRASH( m_buttonImage, ("UpgradeTemplate: %s is looking for button image %s but can't find it. Skipping...", m_name.str(), m_buttonImageName.str() ) );
m_buttonImageName.clear(); // we're done with this, so nuke it
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// UPGRADE CENTER /////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
UpgradeCenter::UpgradeCenter( void )
{
m_upgradeList = NULL;
m_nextTemplateMaskBit = 0;
buttonImagesCached = FALSE;
} // end UpgradeCenter
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
UpgradeCenter::~UpgradeCenter( void )
{
// delete all the upgrades loaded from the INI database
UpgradeTemplate *next;
while( m_upgradeList )
{
// get next
next = m_upgradeList->friend_getNext();
// delete head of list
m_upgradeList->deleteInstance();
// set head to next element
m_upgradeList = next;
} // end while
} // end ~UpgradeCenter
//-------------------------------------------------------------------------------------------------
/** Upgrade center initialization */
//-------------------------------------------------------------------------------------------------
void UpgradeCenter::init( void )
{
UpgradeTemplate* up;
// name will be overridden by friend_makeVeterancyUpgrade
// no, there ISN'T an upgrade for this one...
//up = newUpgrade("");
//up->friend_makeVeterancyUpgrade(LEVEL_REGULAR);
up = newUpgrade("");
up->friend_makeVeterancyUpgrade(LEVEL_VETERAN);
up = newUpgrade("");
up->friend_makeVeterancyUpgrade(LEVEL_ELITE);
up = newUpgrade("");
up->friend_makeVeterancyUpgrade(LEVEL_HEROIC);
}
//-------------------------------------------------------------------------------------------------
/** Upgrade center system reset */
//-------------------------------------------------------------------------------------------------
void UpgradeCenter::reset( void )
{
if( TheMappedImageCollection && !buttonImagesCached )
{
UpgradeTemplate *upgrade;
for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() )
{
upgrade->cacheButtonImage();
}
buttonImagesCached = TRUE;
}
}
//-------------------------------------------------------------------------------------------------
/** Find upgrade matching name key */
//-------------------------------------------------------------------------------------------------
const UpgradeTemplate *UpgradeCenter::findVeterancyUpgrade( VeterancyLevel level ) const
{
AsciiString tmp = getVetUpgradeName(level);
return findUpgrade(tmp);
}
//-------------------------------------------------------------------------------------------------
/** Find upgrade matching name key */
//-------------------------------------------------------------------------------------------------
UpgradeTemplate *UpgradeCenter::findNonConstUpgradeByKey( NameKeyType key )
{
UpgradeTemplate *upgrade;
// search list
for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() )
if( upgrade->getUpgradeNameKey() == key )
return upgrade;
// item not found
return NULL;
}
// ------------------------------------------------------------------------------------------------
/** Return the first upgrade template */
// ------------------------------------------------------------------------------------------------
UpgradeTemplate *UpgradeCenter::firstUpgradeTemplate( void )
{
return m_upgradeList;
} // end firstUpgradeTemplate
//-------------------------------------------------------------------------------------------------
/** Find upgrade matching name key */
//-------------------------------------------------------------------------------------------------
const UpgradeTemplate *UpgradeCenter::findUpgradeByKey( NameKeyType key ) const
{
const UpgradeTemplate *upgrade;
// search list
for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() )
if( upgrade->getUpgradeNameKey() == key )
return upgrade;
// item not found
return NULL;
}
//-------------------------------------------------------------------------------------------------
/** Find upgrade matching name */
//-------------------------------------------------------------------------------------------------
const UpgradeTemplate *UpgradeCenter::findUpgrade( const AsciiString& name ) const
{
return findUpgradeByKey( TheNameKeyGenerator->nameToKey( name ) );
} // end findUpgrade
//-------------------------------------------------------------------------------------------------
/** Allocate a new upgrade template */
//-------------------------------------------------------------------------------------------------
UpgradeTemplate *UpgradeCenter::newUpgrade( const AsciiString& name )
{
UpgradeTemplate *newUpgrade = newInstance(UpgradeTemplate);
// copy data from the default upgrade
const UpgradeTemplate *defaultUpgrade = findUpgrade( "DefaultUpgrade" );
if( defaultUpgrade )
*newUpgrade = *defaultUpgrade;
// assign name and starting data
newUpgrade->setUpgradeName( name );
newUpgrade->setUpgradeNameKey( TheNameKeyGenerator->nameToKey( name ) );
// Make a unique bitmask for this new template by keeping track of what bits have been assigned
// damn MSFT! proper ANSI syntax for a proper 64-bit constant is "1LL", but MSVC doesn't recognize it
Int64 newMask = 1i64 << m_nextTemplateMaskBit;
m_nextTemplateMaskBit++;
DEBUG_ASSERTCRASH( m_nextTemplateMaskBit < 64, ("Can't have over 64 types of Upgrades and have a Bitfield function.") );
newUpgrade->friend_setUpgradeMask( newMask );
// link upgrade
linkUpgrade( newUpgrade );
// return new upgrade
return newUpgrade;
} // end newUnlinkedUpgrade
//-------------------------------------------------------------------------------------------------
/** Link an upgrade to our list */
//-------------------------------------------------------------------------------------------------
void UpgradeCenter::linkUpgrade( UpgradeTemplate *upgrade )
{
// sanity
if( upgrade == NULL )
return;
// link
upgrade->friend_setPrev( NULL );
upgrade->friend_setNext( m_upgradeList );
if( m_upgradeList )
m_upgradeList->friend_setPrev( upgrade );
m_upgradeList = upgrade;
} // end linkUpgrade
//-------------------------------------------------------------------------------------------------
/** Unlink an upgrade from our list */
//-------------------------------------------------------------------------------------------------
void UpgradeCenter::unlinkUpgrade( UpgradeTemplate *upgrade )
{
// sanity
if( upgrade == NULL )
return;
if( upgrade->friend_getNext() )
upgrade->friend_getNext()->friend_setPrev( upgrade->friend_getPrev() );
if( upgrade->friend_getPrev() )
upgrade->friend_getPrev()->friend_setNext( upgrade->friend_getNext() );
else
m_upgradeList = upgrade->friend_getNext();
} // end unlinkUpgrade
//-------------------------------------------------------------------------------------------------
/** does this player have all the necessary things to make this upgrade */
//-------------------------------------------------------------------------------------------------
Bool UpgradeCenter::canAffordUpgrade( Player *player, const UpgradeTemplate *upgradeTemplate, Bool displayReason ) const
{
// sanity
if( player == NULL || upgradeTemplate == NULL )
return FALSE;
// money check
Money *money = player->getMoney();
if( money->countMoney() < upgradeTemplate->calcCostToBuild( player ) )
{
//Post reason why we can't make upgrade!
if( displayReason )
{
TheInGameUI->message( "GUI:NotEnoughMoneyToUpgrade" );
}
return FALSE;
}
/// @todo maybe have prereq checks for upgrades???
return TRUE; // all is well
} // end canAffordUpgrade
//-------------------------------------------------------------------------------------------------
/** generate a list of upgrade names for WorldBuilder */
//-------------------------------------------------------------------------------------------------
std::vector<AsciiString> UpgradeCenter::getUpgradeNames( void ) const
{
std::vector<AsciiString> upgradeNames;
for( UpgradeTemplate *upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() )
upgradeNames.push_back(upgrade->getUpgradeName());
return upgradeNames;
} // end getUpgradeNames
//-------------------------------------------------------------------------------------------------
/** Parse an upgrade definition */
//-------------------------------------------------------------------------------------------------
void UpgradeCenter::parseUpgradeDefinition( INI *ini )
{
// read the name
const char* c = ini->getNextToken();
AsciiString name = c;
// find existing item if present
UpgradeTemplate* upgrade = TheUpgradeCenter->findNonConstUpgradeByKey( NAMEKEY(name) );
if( upgrade == NULL )
{
// allocate a new item
upgrade = TheUpgradeCenter->newUpgrade( name );
} // end if
// sanity
DEBUG_ASSERTCRASH( upgrade, ("parseUpgradeDefinition: Unable to allocate upgrade '%s'\n", name.str()) );
// parse the ini definition
ini->initFromINI( upgrade, upgrade->getFieldParse() );
}

View file

@ -0,0 +1,797 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Xfer.cpp /////////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, February 2002
// Desc: The Xfer system is capable of setting up operations to work with blocks of data
// from other subsystems. It can work things such as file reading, file writing,
// CRC computations etc
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Upgrade.h"
#include "Common/GameState.h"
#include "Common/Xfer.h"
#include "Common/BitFlagsIO.h"
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Xfer::Xfer( void )
{
m_options = XO_NONE;
m_xferMode = XFER_INVALID;
} // end Xfer
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Xfer::~Xfer( void )
{
} // end ~Xfer
// ------------------------------------------------------------------------------------------------
/** Open */
// ------------------------------------------------------------------------------------------------
void Xfer::open( AsciiString identifier )
{
// save identifier
m_identifier = identifier;
} // end open
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferByte( Byte *byteData )
{
xferImplementation( byteData, sizeof( Byte ) );
} // end xferByte
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferVersion( XferVersion *versionData, XferVersion currentVersion )
{
xferImplementation( versionData, sizeof( XferVersion ) );
// sanity, after the xfer, version data is never allowed to be higher than the current version
if( *versionData > currentVersion )
{
DEBUG_CRASH(( "XferVersion - Unknown version '%d' should be no higher than '%d'\n",
*versionData, currentVersion ));
throw XFER_INVALID_VERSION;
} // end if
} // end xferVersion
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferUnsignedByte( UnsignedByte *unsignedByteData )
{
xferImplementation( unsignedByteData, sizeof( UnsignedByte ) );
} // end xferUnsignedByte
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferBool( Bool *boolData )
{
xferImplementation( boolData, sizeof( Bool ) );
} // end xferBool
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferInt( Int *intData )
{
xferImplementation( intData, sizeof( Int ) );
} // end xferInt
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferInt64( Int64 *int64Data )
{
xferImplementation( int64Data, sizeof( Int64 ) );
} // end xferInt64
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferUnsignedInt( UnsignedInt *unsignedIntData )
{
xferImplementation( unsignedIntData, sizeof( UnsignedInt ) );
} // end xferUnsignedInt
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferShort( Short *shortData )
{
xferImplementation( shortData, sizeof( Short ) );
} // end xferShort
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferUnsignedShort( UnsignedShort *unsignedShortData )
{
xferImplementation( unsignedShortData, sizeof( UnsignedShort ) );
} // end xferUnsignedShort
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferReal( Real *realData )
{
xferImplementation( realData, sizeof( Real ) );
} // end xferReal
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferMapName( AsciiString *mapNameData )
{
if (getXferMode() == XFER_SAVE)
{
AsciiString tmp = TheGameState->realMapPathToPortableMapPath(*mapNameData);
xferAsciiString(&tmp);
}
else if (getXferMode() == XFER_LOAD)
{
xferAsciiString(mapNameData);
*mapNameData = TheGameState->portableMapPathToRealMapPath(*mapNameData);
}
} // end xferAsciiString
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferAsciiString( AsciiString *asciiStringData )
{
xferImplementation( (void *)asciiStringData->str(), sizeof( Byte ) * asciiStringData->getLength() );
} // end xferAsciiString
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferMarkerLabel( AsciiString asciiStringData )
{
} // end xferMarkerLabel
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferUnicodeString( UnicodeString *unicodeStringData )
{
xferImplementation( (void *)unicodeStringData->str(), sizeof( WideChar ) * unicodeStringData->getLength() );
} // end xferUnicodeString
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferCoord3D( Coord3D *coord3D )
{
xferReal( &coord3D->x );
xferReal( &coord3D->y );
xferReal( &coord3D->z );
} // end xferCoord3D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferICoord3D( ICoord3D *iCoord3D )
{
xferInt( &iCoord3D->x );
xferInt( &iCoord3D->y );
xferInt( &iCoord3D->z );
} // end xferICoor3D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferRegion3D( Region3D *region3D )
{
xferCoord3D( &region3D->lo );
xferCoord3D( &region3D->hi );
} // end xferRegion3D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferIRegion3D( IRegion3D *iRegion3D )
{
xferICoord3D( &iRegion3D->lo );
xferICoord3D( &iRegion3D->hi );
} // end xferIRegion3D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferCoord2D( Coord2D *coord2D )
{
xferReal( &coord2D->x );
xferReal( &coord2D->y );
} // end xferCoord2D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferICoord2D( ICoord2D *iCoord2D )
{
xferInt( &iCoord2D->x );
xferInt( &iCoord2D->y );
} // end xferICoord2D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferRegion2D( Region2D *region2D )
{
xferCoord2D( &region2D->lo );
xferCoord2D( &region2D->hi );
} // end xferRegion2D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferIRegion2D( IRegion2D *iRegion2D )
{
xferICoord2D( &iRegion2D->lo );
xferICoord2D( &iRegion2D->hi );
} // end xferIRegion2D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferRealRange( RealRange *realRange )
{
xferReal( &realRange->lo );
xferReal( &realRange->hi );
} // end xferRealRange
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferColor( Color *color )
{
xferImplementation( color, sizeof( Color ) );
} // end xferColor
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferRGBColor( RGBColor *rgbColor )
{
xferReal( &rgbColor->red );
xferReal( &rgbColor->green );
xferReal( &rgbColor->blue );
} // end xferRGBColor
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferRGBAColorReal( RGBAColorReal *rgbaColorReal )
{
xferReal( &rgbaColorReal->red );
xferReal( &rgbaColorReal->green );
xferReal( &rgbaColorReal->blue );
xferReal( &rgbaColorReal->alpha );
} // end xferRGBAColorReal
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferRGBAColorInt( RGBAColorInt *rgbaColorInt )
{
xferUnsignedInt( &rgbaColorInt->red );
xferUnsignedInt( &rgbaColorInt->green );
xferUnsignedInt( &rgbaColorInt->blue );
xferUnsignedInt( &rgbaColorInt->alpha );
} // end xferRGBAColorInt
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferObjectID( ObjectID *objectID )
{
xferImplementation( objectID, sizeof( ObjectID ) );
} // end xferObjeftID
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferDrawableID( DrawableID *drawableID )
{
xferImplementation( drawableID, sizeof( DrawableID ) );
} // end xferDrawableID
// ------------------------------------------------------------------------------------------------
/** STL Object ID list (cause it's a common data structure we use a lot)
* Version Info;
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Xfer::xferSTLObjectIDList( std::list< ObjectID > *objectIDListData )
{
//
// the fact that this is a list and a little higher level than a simple data type
// is reason enough to have every one of these versioned
//
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
// xfer the count of the list
UnsignedShort listCount = objectIDListData->size();
xferUnsignedShort( &listCount );
// xfer list data
ObjectID objectID;
if( getXferMode() == XFER_SAVE || getXferMode() == XFER_CRC )
{
// save all ids
std::list< ObjectID >::const_iterator it;
for( it = objectIDListData->begin(); it != objectIDListData->end(); ++it )
{
objectID = *it;
xferObjectID( &objectID );
} // end for
} // end if, save
else if( getXferMode() == XFER_LOAD )
{
// sanity, the list should be empty before we transfer more data into it
if( objectIDListData->size() != 0 )
{
DEBUG_CRASH(( "Xfer::xferSTLObjectIDList - object list should be empty before loading\n" ));
throw XFER_LIST_NOT_EMPTY;
} // end if
// read all ids
for( UnsignedShort i = 0; i < listCount; ++i )
{
xferObjectID( &objectID );
objectIDListData->push_back( objectID );
} // end for, i
} // end else if
else
{
DEBUG_CRASH(( "xferSTLObjectIDList - Unknown xfer mode '%d'\n", getXferMode() ));
throw XFER_MODE_UNKNOWN;
} // end else
} // end xferSTLObjectIDList
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferSTLIntList( std::list< Int > *intListData )
{
// sanity
if( intListData == NULL )
return;
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
// xfer the count of the list
UnsignedShort listCount = intListData->size();
xferUnsignedShort( &listCount );
// xfer list data
Int intData;
if( getXferMode() == XFER_SAVE || getXferMode() == XFER_CRC )
{
// save all ids
std::list< Int >::const_iterator it;
for( it = intListData->begin(); it != intListData->end(); ++it )
{
intData = *it;
xferInt( &intData );
} // end for
} // end if, save
else if( getXferMode() == XFER_LOAD )
{
// sanity, the list should be empty before we transfer more data into it
if( intListData->size() != 0 )
{
DEBUG_CRASH(( "Xfer::xferSTLIntList - int list should be empty before loading\n" ));
throw XFER_LIST_NOT_EMPTY;
} // end if
// read all ids
for( UnsignedShort i = 0; i < listCount; ++i )
{
xferInt( &intData );
intListData->push_back( intData );
} // end for, i
} // end else if
else
{
DEBUG_CRASH(( "xferSTLIntList - Unknown xfer mode '%d'\n", getXferMode() ));
throw XFER_MODE_UNKNOWN;
} // end else
} // end xferSTLIntList
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferScienceType( ScienceType *science )
{
// sanity
DEBUG_ASSERTCRASH( science != NULL, ("xferScienceType - Invalid parameters\n") );
AsciiString scienceName;
if( getXferMode() == XFER_SAVE )
{
// translate to string
scienceName = TheScienceStore->getInternalNameForScience( *science );
// write the string
xferAsciiString( &scienceName );
} // end if, save
else if( getXferMode() == XFER_LOAD )
{
xferAsciiString( &scienceName );
// translate to science
*science = TheScienceStore->getScienceFromInternalName( scienceName );
if( *science == SCIENCE_INVALID )
{
DEBUG_CRASH(( "xferScienceType - Unknown science '%s'\n", scienceName.str() ));
throw XFER_UNKNOWN_STRING;
} // end if
} // end else if, load
else if( getXferMode() == XFER_CRC )
{
xferImplementation( science, sizeof( *science ) );
} // end else if, crc
else
{
DEBUG_CRASH(( "xferScienceVec - Unknown xfer mode '%d'\n", getXferMode() ));
throw XFER_MODE_UNKNOWN;
} // end else
} // end xferScienceType
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferScienceVec( ScienceVec *scienceVec )
{
// sanity
DEBUG_ASSERTCRASH( scienceVec != NULL, ("xferScienceVec - Invalid parameters\n") );
// this deserves a version number
const XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
// count of vector
UnsignedShort count = scienceVec->size();
xferUnsignedShort( &count );
if( getXferMode() == XFER_SAVE )
{
for( ScienceVec::const_iterator it = scienceVec->begin(); it != scienceVec->end(); ++it )
{
ScienceType science = *it;
xferScienceType(&science);
}
}
else if( getXferMode() == XFER_LOAD )
{
// vector should be empty at this point
if( scienceVec->empty() == FALSE )
{
DEBUG_CRASH(( "xferScienceVec - vector is not empty, but should be\n" ));
throw XFER_LIST_NOT_EMPTY;
}
for( UnsignedShort i = 0; i < count; ++i )
{
ScienceType science;
xferScienceType(&science);
scienceVec->push_back( science );
}
}
else if( getXferMode() == XFER_CRC )
{
for( ScienceVec::const_iterator it = scienceVec->begin(); it != scienceVec->end(); ++it )
{
ScienceType science = *it;
xferImplementation( &science, sizeof( ScienceType ) );
} // end for, it
} // end else if, crc
else
{
DEBUG_CRASH(( "xferScienceVec - Unknown xfer mode '%d'\n", getXferMode() ));
throw XFER_MODE_UNKNOWN;
} // end else
} // end xferScienceVec
// ------------------------------------------------------------------------------------------------
/** kind of type, for load/save it is xfered as a string so we can reorder the
* kindofs if we like
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Xfer::xferKindOf( KindOfType *kindOfData )
{
// this deserves a version number
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
// check which type of xfer we're doing
if( getXferMode() == XFER_SAVE )
{
// save as an ascii string
AsciiString kindOfName = KindOfMaskType::getNameFromSingleBit(*kindOfData);
xferAsciiString( &kindOfName );
} // end if, save
else if( getXferMode() == XFER_LOAD )
{
// read ascii string from file
AsciiString kindOfName;
xferAsciiString( &kindOfName );
// turn kind of name into an enum value
Int bit = KindOfMaskType::getSingleBitFromName(kindOfName.str());
if (bit != -1)
*kindOfData = (KindOfType)bit;
} // end else if, load
else if( getXferMode() == XFER_CRC )
{
// just call the xfer implementation on the data values
xferImplementation( kindOfData, sizeof( KindOfType ) );
} // end else if, crc
else
{
DEBUG_CRASH(( "xferKindOf - Unknown xfer mode '%d'\n", getXferMode() ));
throw XFER_MODE_UNKNOWN;
} // end else
} // end xferKindOf
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferUpgradeMask( Int64 *upgradeMaskData )
{
// this deserves a version number
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
// check which type of xfer we're doing
if( getXferMode() == XFER_SAVE )
{
AsciiString upgradeName;
// count how many bits are set in the mask
UnsignedShort count = 0;
UpgradeTemplate *upgradeTemplate;
for( upgradeTemplate = TheUpgradeCenter->firstUpgradeTemplate();
upgradeTemplate;
upgradeTemplate = upgradeTemplate->friend_getNext() )
{
// if the mask of this upgrade is set, it counts
if( BitTest( *upgradeMaskData, upgradeTemplate->getUpgradeMask() ) )
count++;
} // end for, upgradeTemplate
// write the count
xferUnsignedShort( &count );
// write out the upgrades as strings
for( upgradeTemplate = TheUpgradeCenter->firstUpgradeTemplate();
upgradeTemplate;
upgradeTemplate = upgradeTemplate->friend_getNext() )
{
// if the mask of this upgrade is set, it counts
if( BitTest( *upgradeMaskData, upgradeTemplate->getUpgradeMask() ) )
{
upgradeName = upgradeTemplate->getUpgradeName();
xferAsciiString( &upgradeName );
} // end if
} // end for, upgradeTemplate
} // end if, save
else if( getXferMode() == XFER_LOAD )
{
AsciiString upgradeName;
const UpgradeTemplate *upgradeTemplate;
// how many strings are we going to read from the file
UnsignedShort count;
xferUnsignedShort( &count );
// zero the mask data
*upgradeMaskData = 0;
// read all the strings and set the mask vaules
for( UnsignedShort i = 0; i < count; ++i )
{
// read the string
xferAsciiString( &upgradeName );
// find this upgrade template
upgradeTemplate = TheUpgradeCenter->findUpgrade( upgradeName );
if( upgradeTemplate == NULL )
{
DEBUG_CRASH(( "Xfer::xferUpgradeMask - Unknown upgrade '%s'\n", upgradeName.str() ));
throw XFER_UNKNOWN_STRING;
} // end if
// set the mask data
BitSet( *upgradeMaskData, upgradeTemplate->getUpgradeMask() );
} // end for i
} // end else if, load
else if( getXferMode() == XFER_CRC )
{
// just xfer implementation the data itself
xferImplementation( upgradeMaskData, sizeof( Int64 ) );
} // end else if, crc
else
{
DEBUG_CRASH(( "xferUpgradeMask - Unknown xfer mode '%d'\n", getXferMode() ));
throw XFER_MODE_UNKNOWN;
} // end else
} // end xferUpgradeMask
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferUser( void *data, Int dataSize )
{
xferImplementation( data, dataSize );
} // end xferUser
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Xfer::xferMatrix3D( Matrix3D* mtx )
{
// this deserves a version number
const XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
Vector4& tmp0 = (*mtx)[0];
Vector4& tmp1 = (*mtx)[1];
Vector4& tmp2 = (*mtx)[2];
xferReal(&tmp0.X);
xferReal(&tmp0.Y);
xferReal(&tmp0.Z);
xferReal(&tmp0.W);
xferReal(&tmp1.X);
xferReal(&tmp1.Y);
xferReal(&tmp1.Z);
xferReal(&tmp1.W);
xferReal(&tmp2.X);
xferReal(&tmp2.Y);
xferReal(&tmp2.Z);
xferReal(&tmp2.W);
}

View file

@ -0,0 +1,357 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: XferCRC.cpp //////////////////////////////////////////////////////////////////////////////
// Author: Matt Campbell, February 2002
// Desc: Xfer CRC implementation
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/XferCRC.h"
#include "Common/XferDeepCRC.h"
#include "Common/CRC.h"
#include "Common/Snapshot.h"
#include "winsock2.h" // for htonl
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferCRC::XferCRC( void )
{
m_xferMode = XFER_CRC;
//Added By Sadullah Nader
//Initialization(s) inserted
m_crc = 0;
//
} // end XferCRC
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferCRC::~XferCRC( void )
{
} // end ~XferCRC
//-------------------------------------------------------------------------------------------------
/** Open file 'identifier' for writing */
//-------------------------------------------------------------------------------------------------
void XferCRC::open( AsciiString identifier )
{
// call base class
Xfer::open( identifier );
// initialize CRC to brand new one at zero
m_crc = 0;
} // end open
//-------------------------------------------------------------------------------------------------
/** Close our current file */
//-------------------------------------------------------------------------------------------------
void XferCRC::close( void )
{
} // end close
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Int XferCRC::beginBlock( void )
{
return 0;
} // end beginBlock
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void XferCRC::endBlock( void )
{
} // end endBlock
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void XferCRC::addCRC( UnsignedInt val )
{
int hibit;
val = htonl(val);
if (m_crc & 0x80000000)
{
hibit = 1;
}
else
{
hibit = 0;
}
m_crc <<= 1;
m_crc += val;
m_crc += hibit;
} // end addCRC
// ------------------------------------------------------------------------------------------------
/** Entry point for xfering a snapshot */
// ------------------------------------------------------------------------------------------------
void XferCRC::xferSnapshot( Snapshot *snapshot )
{
if( snapshot == NULL )
{
return;
} // end if
// run the crc function of the snapshot
snapshot->crc( this );
} // end xferSnapshot
//-------------------------------------------------------------------------------------------------
/** Perform a single CRC operation on the data passed in */
//-------------------------------------------------------------------------------------------------
void XferCRC::xferImplementation( void *data, Int dataSize )
{
if (!data || dataSize < 1)
{
return;
}
const UnsignedInt *uintPtr = (const UnsignedInt *) (data);
for (Int i=0 ; i<dataSize/4 ; i++)
{
addCRC (*uintPtr++);
}
int leftover = dataSize & 3;
if (leftover)
{
UnsignedInt val = 0;
const unsigned char *c = (const unsigned char *)uintPtr;
for (Int i=0; i<leftover; i++)
{
val += (c[i] << (i*8));
}
val = htonl(val);
addCRC (val);
}
} // end xferImplementation
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void XferCRC::skip( Int dataSize )
{
} // end skip
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
UnsignedInt XferCRC::getCRC( void )
{
return htonl(m_crc);
} // end skip
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferDeepCRC::XferDeepCRC( void )
{
m_xferMode = XFER_SAVE;
m_fileFP = NULL;
} // end XferCRC
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferDeepCRC::~XferDeepCRC( void )
{
// warn the user if a file was left open
if( m_fileFP != NULL )
{
DEBUG_CRASH(( "Warning: Xfer file '%s' was left open\n", m_identifier.str() ));
close();
} // end if
} // end ~XferCRC
//-------------------------------------------------------------------------------------------------
/** Open file 'identifier' for writing */
//-------------------------------------------------------------------------------------------------
void XferDeepCRC::open( AsciiString identifier )
{
m_xferMode = XFER_SAVE;
// sanity, check to see if we're already open
if( m_fileFP != NULL )
{
DEBUG_CRASH(( "Cannot open file '%s' cause we've already got '%s' open\n",
identifier.str(), m_identifier.str() ));
throw XFER_FILE_ALREADY_OPEN;
} // end if
// call base class
Xfer::open( identifier );
// open the file
m_fileFP = fopen( identifier.str(), "w+b" );
if( m_fileFP == NULL )
{
DEBUG_CRASH(( "File '%s' not found\n", identifier.str() ));
throw XFER_FILE_NOT_FOUND;
} // end if
// initialize CRC to brand new one at zero
m_crc = 0;
} // end open
//-------------------------------------------------------------------------------------------------
/** Close our current file */
//-------------------------------------------------------------------------------------------------
void XferDeepCRC::close( void )
{
// sanity, if we don't have an open file we can do nothing
if( m_fileFP == NULL )
{
DEBUG_CRASH(( "Xfer close called, but no file was open\n" ));
throw XFER_FILE_NOT_OPEN;
} // end if
// close the file
fclose( m_fileFP );
m_fileFP = NULL;
// erase the filename
m_identifier.clear();
} // end close
//-------------------------------------------------------------------------------------------------
/** Perform a single CRC operation on the data passed in */
//-------------------------------------------------------------------------------------------------
void XferDeepCRC::xferImplementation( void *data, Int dataSize )
{
if (!data || dataSize < 1)
{
return;
}
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferSave - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// write data to file
if( fwrite( data, dataSize, 1, m_fileFP ) != 1 )
{
DEBUG_CRASH(( "XferSave - Error writing to file '%s'\n", m_identifier.str() ));
throw XFER_WRITE_ERROR;
} // end if
XferCRC::xferImplementation( data, dataSize );
} // end xferImplementation
// ------------------------------------------------------------------------------------------------
/** Save ascii string */
// ------------------------------------------------------------------------------------------------
void XferDeepCRC::xferMarkerLabel( AsciiString asciiStringData )
{
} // end xferAsciiString
// ------------------------------------------------------------------------------------------------
/** Save ascii string */
// ------------------------------------------------------------------------------------------------
void XferDeepCRC::xferAsciiString( AsciiString *asciiStringData )
{
// sanity
if( asciiStringData->getLength() > 16385 )
{
DEBUG_CRASH(( "XferSave cannot save this ascii string because it's too long. Change the size of the length header (but be sure to preserve save file compatability\n" ));
throw XFER_STRING_ERROR;
} // end if
// save length of string to follow
UnsignedShort len = asciiStringData->getLength();
xferUnsignedShort( &len );
// save string data
if( len > 0 )
xferUser( (void *)asciiStringData->str(), sizeof( Byte ) * len );
} // end xferAsciiString
// ------------------------------------------------------------------------------------------------
/** Save unicodee string */
// ------------------------------------------------------------------------------------------------
void XferDeepCRC::xferUnicodeString( UnicodeString *unicodeStringData )
{
// sanity
if( unicodeStringData->getLength() > 255 )
{
DEBUG_CRASH(( "XferSave cannot save this unicode string because it's too long. Change the size of the length header (but be sure to preserve save file compatability\n" ));
throw XFER_STRING_ERROR;
} // end if
// save length of string to follow
Byte len = unicodeStringData->getLength();
xferByte( &len );
// save string data
if( len > 0 )
xferUser( (void *)unicodeStringData->str(), sizeof( WideChar ) * len );
} // end xferUnicodeString

View file

@ -0,0 +1,260 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: XferLoad.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, February 2002
// Desc: Xfer implemenation for loading from disk
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/Debug.h"
#include "Common/GameState.h"
#include "Common/Snapshot.h"
#include "Common/XferLoad.h"
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferLoad::XferLoad( void )
{
m_xferMode = XFER_LOAD;
m_fileFP = NULL;
} // end XferLoad
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferLoad::~XferLoad( void )
{
// warn the user if a file was left open
if( m_fileFP != NULL )
{
DEBUG_CRASH(( "Warning: Xfer file '%s' was left open\n", m_identifier.str() ));
close();
} // end if
} // end ~XferLoad
//-------------------------------------------------------------------------------------------------
/** Open file 'identifier' for reading */
//-------------------------------------------------------------------------------------------------
void XferLoad::open( AsciiString identifier )
{
// sanity, check to see if we're already open
if( m_fileFP != NULL )
{
DEBUG_CRASH(( "Cannot open file '%s' cause we've already got '%s' open\n",
identifier.str(), m_identifier.str() ));
throw XFER_FILE_ALREADY_OPEN;
} // end if
// call base class
Xfer::open( identifier );
// open the file
m_fileFP = fopen( identifier.str(), "rb" );
if( m_fileFP == NULL )
{
DEBUG_CRASH(( "File '%s' not found\n", identifier.str() ));
throw XFER_FILE_NOT_FOUND;
} // end if
} // end open
//-------------------------------------------------------------------------------------------------
/** Close our current file */
//-------------------------------------------------------------------------------------------------
void XferLoad::close( void )
{
// sanity, if we don't have an open file we can do nothing
if( m_fileFP == NULL )
{
DEBUG_CRASH(( "Xfer close called, but no file was open\n" ));
throw XFER_FILE_NOT_OPEN;
} // end if
// close the file
fclose( m_fileFP );
m_fileFP = NULL;
// erase the filename
m_identifier.clear();
} // end close
//-------------------------------------------------------------------------------------------------
/** Read a block size descriptor from the file at the current position */
//-------------------------------------------------------------------------------------------------
Int XferLoad::beginBlock( void )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("Xfer begin block - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// read block size
XferBlockSize blockSize;
if( fread( &blockSize, sizeof( XferBlockSize ), 1, m_fileFP ) != 1 )
{
DEBUG_CRASH(( "Xfer - Error reading block size for '%s'\n", m_identifier.str() ));
return 0;
} // end if
// return the block size
return blockSize;
} // end beginBlock
// ------------------------------------------------------------------------------------------------
/** End block ... this does nothing when reading */
// ------------------------------------------------------------------------------------------------
void XferLoad::endBlock( void )
{
} // end endBlock
//-------------------------------------------------------------------------------------------------
/** Skip forward 'dataSize' bytes in the file */
//-------------------------------------------------------------------------------------------------
void XferLoad::skip( Int dataSize )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferLoad::skip - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// sanity
DEBUG_ASSERTCRASH( dataSize >=0, ("XferLoad::skip - dataSize '%d' must be greater than 0\n",
dataSize) );
// skip datasize in the file from the current position
if( fseek( m_fileFP, dataSize, SEEK_CUR ) != 0 )
throw XFER_SKIP_ERROR;
} // end skip
// ------------------------------------------------------------------------------------------------
/** Entry point for xfering a snapshot */
// ------------------------------------------------------------------------------------------------
void XferLoad::xferSnapshot( Snapshot *snapshot )
{
if( snapshot == NULL )
{
DEBUG_CRASH(( "XferLoad::xferSnapshot - Invalid parameters\n" ));
throw XFER_INVALID_PARAMETERS;
} // end if
// run the xfer function of the snapshot
snapshot->xfer( this );
// add this snapshot to the game state for later post processing if not restricted
if( BitTest( getOptions(), XO_NO_POST_PROCESSING ) == FALSE )
TheGameState->addPostProcessSnapshot( snapshot );
} // end xferSnapshot
// ------------------------------------------------------------------------------------------------
/** Read string from file and store in ascii string */
// ------------------------------------------------------------------------------------------------
void XferLoad::xferAsciiString( AsciiString *asciiStringData )
{
// read bytes of string length to follow
UnsignedByte len;
xferUnsignedByte( &len );
// read all the string data
const Int MAX_XFER_LOAD_STRING_BUFFER = 1024;
static Char buffer[ MAX_XFER_LOAD_STRING_BUFFER ];
if( len > 0 )
xferUser( buffer, sizeof( Byte ) * len );
buffer[ len ] = 0; // terminate
// save into ascii string
asciiStringData->set( buffer );
} // end xferAsciiString
// ------------------------------------------------------------------------------------------------
/** Read string from file and store in unicode string */
// ------------------------------------------------------------------------------------------------
void XferLoad::xferUnicodeString( UnicodeString *unicodeStringData )
{
// read bytes of string length to follow
UnsignedByte len;
xferUnsignedByte( &len );
// read all the string data
const Int MAX_XFER_LOAD_STRING_BUFFER = 1024;
static WideChar buffer[ MAX_XFER_LOAD_STRING_BUFFER ];
if( len > 0 )
xferUser( buffer, sizeof( WideChar ) * len );
buffer[ len ] = 0; // terminate
// save into unicode string
unicodeStringData->set( buffer );
} // end xferUnicodeString
//-------------------------------------------------------------------------------------------------
/** Perform the read operation */
//-------------------------------------------------------------------------------------------------
void XferLoad::xferImplementation( void *data, Int dataSize )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferLoad - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// read data from file
if( fread( data, dataSize, 1, m_fileFP ) != 1 )
{
DEBUG_CRASH(( "XferLoad - Error reading from file '%s'\n", m_identifier.str() ));
throw XFER_READ_ERROR;
} // end if
} // end xferImplementation

View file

@ -0,0 +1,358 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: XferSave.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, February 2002
// Desc: Xfer disk write implementation
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/XferSave.h"
#include "Common/Snapshot.h"
#include "Common/GameMemory.h"
// PRIVATE TYPES //////////////////////////////////////////////////////////////////////////////////
class XferBlockData : public MemoryPoolObject
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(XferBlockData, "XferBlockData")
public:
XferFilePos filePos; ///< the file position of this block
XferBlockData *next; ///< next block on the stack
};
EMPTY_DTOR(XferBlockData)
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHDOS /////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferSave::XferSave( void )
{
m_xferMode = XFER_SAVE;
m_fileFP = NULL;
m_blockStack = NULL;
} // end XferSave
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
XferSave::~XferSave( void )
{
// warn the user if a file was left open
if( m_fileFP != NULL )
{
DEBUG_CRASH(( "Warning: Xfer file '%s' was left open\n", m_identifier.str() ));
close();
} // end if
//
// the block stack should be empty, if it's not that means we started blocks but never
// called enough matching end blocks
//
if( m_blockStack != NULL )
{
// tell the user there is an error
DEBUG_CRASH(( "Warning: XferSave::~XferSave - m_blockStack was not NULL!\n" ));
// delete the block stack
XferBlockData *next;
while( m_blockStack )
{
next = m_blockStack->next;
m_blockStack->deleteInstance();
m_blockStack = next;
} // end while
} // end if
} // end ~XferSave
//-------------------------------------------------------------------------------------------------
/** Open file 'identifier' for writing */
//-------------------------------------------------------------------------------------------------
void XferSave::open( AsciiString identifier )
{
// sanity, check to see if we're already open
if( m_fileFP != NULL )
{
DEBUG_CRASH(( "Cannot open file '%s' cause we've already got '%s' open\n",
identifier.str(), m_identifier.str() ));
throw XFER_FILE_ALREADY_OPEN;
} // end if
// call base class
Xfer::open( identifier );
// open the file
m_fileFP = fopen( identifier.str(), "w+b" );
if( m_fileFP == NULL )
{
DEBUG_CRASH(( "File '%s' not found\n", identifier.str() ));
throw XFER_FILE_NOT_FOUND;
} // end if
} // end open
//-------------------------------------------------------------------------------------------------
/** Close our current file */
//-------------------------------------------------------------------------------------------------
void XferSave::close( void )
{
// sanity, if we don't have an open file we can do nothing
if( m_fileFP == NULL )
{
DEBUG_CRASH(( "Xfer close called, but no file was open\n" ));
throw XFER_FILE_NOT_OPEN;
} // end if
// close the file
fclose( m_fileFP );
m_fileFP = NULL;
// erase the filename
m_identifier.clear();
} // end close
//-------------------------------------------------------------------------------------------------
/** Write a placeholder at the current location in the file and store this location
* internally. The next endBlock that is called will back up to the most recently stored
* beginBlock and write the difference in file bytes from the endBlock call to the
* location of this beginBlock. The current file position will then return to the location
* at which endBlock was called */
//-------------------------------------------------------------------------------------------------
Int XferSave::beginBlock( void )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("Xfer begin block - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// get the current file position so we can back up here for the next end block call
XferFilePos filePos = ftell( m_fileFP );
// write a placeholder
XferBlockSize blockSize = 0;
if( fwrite( &blockSize, sizeof( XferBlockSize ), 1, m_fileFP ) != 1 )
{
DEBUG_CRASH(( "XferSave::beginBlock - Error writing block size in '%s'\n",
m_identifier.str() ));
return XFER_WRITE_ERROR;
} // end if
// save this block position on the top of the "stack"
XferBlockData *top = newInstance(XferBlockData);
// impossible -- exception will be thrown (srj)
// if( top == NULL )
// {
//
// DEBUG_CRASH(( "XferSave - Begin block, out of memory - can't save block stack data\n" ));
// return XFER_OUT_OF_MEMORY;
//
// } // end if
top->filePos = filePos;
top->next = m_blockStack;
m_blockStack = top;
return XFER_OK;
} // end beginBlock
//-------------------------------------------------------------------------------------------------
/** Do the tail end as described in beginBlock above. Back up to the last begin block,
* write the file difference from current position to the last begin position, and put
* current file position back to where it was */
//-------------------------------------------------------------------------------------------------
void XferSave::endBlock( void )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("Xfer end block - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// sanity, make sure we have a block started
if( m_blockStack == NULL )
{
DEBUG_CRASH(( "Xfer end block called, but no matching begin block was found\n" ));
throw XFER_BEGIN_END_MISMATCH;
} // end if
// save our current file position
XferFilePos currentFilePos = ftell( m_fileFP );
// pop the block descriptor off the top of the block stack
XferBlockData *top = m_blockStack;
m_blockStack = m_blockStack->next;
// rewind the file to the block position
fseek( m_fileFP, top->filePos, SEEK_SET );
// write the size in bytes between the block position and what is our current file position
XferBlockSize blockSize = currentFilePos - top->filePos - sizeof( XferBlockSize );
if( fwrite( &blockSize, sizeof( XferBlockSize ), 1, m_fileFP ) != 1 )
{
DEBUG_CRASH(( "Error writing block size to file '%s'\n", m_identifier.str() ));
throw XFER_WRITE_ERROR;
} // end if
// place the file pointer back to the current position
fseek( m_fileFP, currentFilePos, SEEK_SET );
// delete the block data as it's all used up now
top->deleteInstance();
} // end endBlock
//-------------------------------------------------------------------------------------------------
/** Skip forward 'dataSize' bytes in the file */
//-------------------------------------------------------------------------------------------------
void XferSave::skip( Int dataSize )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferSave - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// skip forward dataSize bytes
fseek( m_fileFP, dataSize, SEEK_CUR );
} // end skip
// ------------------------------------------------------------------------------------------------
/** Entry point for xfering a snapshot */
// ------------------------------------------------------------------------------------------------
void XferSave::xferSnapshot( Snapshot *snapshot )
{
if( snapshot == NULL )
{
DEBUG_CRASH(( "XferSave::xferSnapshot - Invalid parameters\n" ));
throw XFER_INVALID_PARAMETERS;
} // end if
// run the xfer function of the snapshot
snapshot->xfer( this );
} // end xferSnapshot
// ------------------------------------------------------------------------------------------------
/** Save ascii string */
// ------------------------------------------------------------------------------------------------
void XferSave::xferAsciiString( AsciiString *asciiStringData )
{
// sanity
if( asciiStringData->getLength() > 255 )
{
DEBUG_CRASH(( "XferSave cannot save this unicode string because it's too long. Change the size of the length header (but be sure to preserve save file compatability\n" ));
throw XFER_STRING_ERROR;
} // end if
// save length of string to follow
UnsignedByte len = asciiStringData->getLength();
xferUnsignedByte( &len );
// save string data
if( len > 0 )
xferUser( (void *)asciiStringData->str(), sizeof( Byte ) * len );
} // end xferAsciiString
// ------------------------------------------------------------------------------------------------
/** Save unicodee string */
// ------------------------------------------------------------------------------------------------
void XferSave::xferUnicodeString( UnicodeString *unicodeStringData )
{
// sanity
if( unicodeStringData->getLength() > 255 )
{
DEBUG_CRASH(( "XferSave cannot save this unicode string because it's too long. Change the size of the length header (but be sure to preserve save file compatability\n" ));
throw XFER_STRING_ERROR;
} // end if
// save length of string to follow
UnsignedByte len = unicodeStringData->getLength();
xferUnsignedByte( &len );
// save string data
if( len > 0 )
xferUser( (void *)unicodeStringData->str(), sizeof( WideChar ) * len );
} // end xferUnicodeString
//-------------------------------------------------------------------------------------------------
/** Perform the write operation */
//-------------------------------------------------------------------------------------------------
void XferSave::xferImplementation( void *data, Int dataSize )
{
// sanity
DEBUG_ASSERTCRASH( m_fileFP != NULL, ("XferSave - file pointer for '%s' is NULL\n",
m_identifier.str()) );
// write data to file
if( fwrite( data, dataSize, 1, m_fileFP ) != 1 )
{
DEBUG_CRASH(( "XferSave - Error writing to file '%s'\n", m_identifier.str() ));
throw XFER_WRITE_ERROR;
} // end if
} // end xferImplementation

View file

@ -0,0 +1,106 @@
/*
** 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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Encrypt.cpp //////////////////////////////////////////////////////
// Ancient Westwood Online password encryption (obfuscation?) code
// Author: Anonymous
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include <stdio.h>
#include <string.h>
#include "Common/encrypt.h"
#define MAX_CHARS 65
static char Base_String[MAX_CHARS] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
static char Return_Buffer[MAX_ENCRYPTED_STRING + 1];
static char Temp_Buffer [MAX_ENCRYPTED_STRING + 1];
/*******************************************************/
/* This function is a simple one-way encryption that */
/* is portable across many platforms */
/* */
/* The 'String' to encrypt MUST be up to 8 chars long */
/* and should be no shorter than 4 characters. */
/* It can contain letters and numbers and '.' and '/' */
/*******************************************************/
/* String is the original string to encrypt */
/* Seed is the string to encrypt */
//char *encrypt(char *String, char *Seed)
const char *EncryptString(const char *String)
{
/* We need a 56 bit key, so use two 32 bit values */
/* and we'll strip off the high order 8 bits */
//unsigned long Random_Seed_Value_high = 0; /* 32 bit seed value */
//unsigned long Random_Seed_Value_low = 0; /* 32 bit seed value */
//unsigned long Temp_high = 0; /* 32 bit storage value */
//unsigned long Temp_low = 0; /* 32 bit storage value */
unsigned int UpCnt = 0, DnCnt = 0, Cnt = 0;
unsigned int Length = strlen(String);
/* Ok, here is the algorithm: */
/* */
if (Length > MAX_ENCRYPTED_STRING)
Length = MAX_ENCRYPTED_STRING;
for (UpCnt = 0, DnCnt = Length; UpCnt < Length; UpCnt++, DnCnt--)
if (String[UpCnt] & 0x01)
Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) &
String[DnCnt];
else
Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) ^
String[DnCnt];
for (Cnt = 0; Cnt < MAX_ENCRYPTED_STRING; Cnt++)
Return_Buffer[Cnt] = Base_String[Temp_Buffer[Cnt] & 0x3F];
Return_Buffer[Cnt] = NULL;
return (Return_Buffer);
}
#ifdef UNIT_TEST
void main(void)
{
char Input_String[9];
char *new_string;
while (1)
{
printf ("Enter a string to encrypt:");
gets(Input_String);
printf("\nString enterred was: %s", Input_String);
new_string = encrypt(Input_String, "ab");
printf("\nEncrypted string is: %s", new_string);
}
}
#endif

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/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// Registry.cpp
// Simple interface for storing/retreiving registry values
// Author: Matthew D. Campbell, December 2001
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Registry.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
Bool getStringFromRegistry(HKEY root, AsciiString path, AsciiString key, AsciiString& val)
{
HKEY handle;
unsigned char buffer[256];
unsigned long size = 256;
unsigned long type;
int returnValue;
if ((returnValue = RegOpenKeyEx( root, path.str(), 0, KEY_READ, &handle )) == ERROR_SUCCESS)
{
returnValue = RegQueryValueEx(handle, key.str(), NULL, &type, (unsigned char *) &buffer, &size);
RegCloseKey( handle );
}
if (returnValue == ERROR_SUCCESS)
{
val = (char *)buffer;
return TRUE;
}
return FALSE;
}
Bool getUnsignedIntFromRegistry(HKEY root, AsciiString path, AsciiString key, UnsignedInt& val)
{
HKEY handle;
unsigned char buffer[4];
unsigned long size = 4;
unsigned long type;
int returnValue;
if ((returnValue = RegOpenKeyEx( root, path.str(), 0, KEY_READ, &handle )) == ERROR_SUCCESS)
{
returnValue = RegQueryValueEx(handle, key.str(), NULL, &type, (unsigned char *) &buffer, &size);
RegCloseKey( handle );
}
if (returnValue == ERROR_SUCCESS)
{
val = *(UnsignedInt *)buffer;
return TRUE;
}
return FALSE;
}
Bool setStringInRegistry( HKEY root, AsciiString path, AsciiString key, AsciiString val)
{
HKEY handle;
unsigned long type;
unsigned long returnValue;
int size;
if ((returnValue = RegCreateKeyEx( root, path.str(), 0, "REG_NONE", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &handle, NULL )) == ERROR_SUCCESS)
{
type = REG_SZ;
size = val.getLength()+1;
returnValue = RegSetValueEx(handle, key.str(), 0, type, (unsigned char *)val.str(), size);
RegCloseKey( handle );
}
return (returnValue == ERROR_SUCCESS);
}
Bool setUnsignedIntInRegistry( HKEY root, AsciiString path, AsciiString key, UnsignedInt val)
{
HKEY handle;
unsigned long type;
unsigned long returnValue;
int size;
if ((returnValue = RegCreateKeyEx( root, path.str(), 0, "REG_NONE", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &handle, NULL )) == ERROR_SUCCESS)
{
type = REG_DWORD;
size = 4;
returnValue = RegSetValueEx(handle, key.str(), 0, type, (unsigned char *)&val, size);
RegCloseKey( handle );
}
return (returnValue == ERROR_SUCCESS);
}
Bool GetStringFromRegistry(AsciiString path, AsciiString key, AsciiString& val)
{
AsciiString fullPath = "SOFTWARE\\Electronic Arts\\EA Games\\Generals";
fullPath.concat(path);
DEBUG_LOG(("GetStringFromRegistry - looking in %s for key %s\n", fullPath.str(), key.str()));
if (getStringFromRegistry(HKEY_LOCAL_MACHINE, fullPath.str(), key.str(), val))
{
return TRUE;
}
return getStringFromRegistry(HKEY_CURRENT_USER, fullPath.str(), key.str(), val);
}
Bool GetUnsignedIntFromRegistry(AsciiString path, AsciiString key, UnsignedInt& val)
{
AsciiString fullPath = "SOFTWARE\\Electronic Arts\\EA Games\\Generals";
fullPath.concat(path);
DEBUG_LOG(("GetUnsignedIntFromRegistry - looking in %s for key %s\n", fullPath.str(), key.str()));
if (getUnsignedIntFromRegistry(HKEY_LOCAL_MACHINE, fullPath.str(), key.str(), val))
{
return TRUE;
}
return getUnsignedIntFromRegistry(HKEY_CURRENT_USER, fullPath.str(), key.str(), val);
}
AsciiString GetRegistryLanguage(void)
{
static Bool cached = FALSE;
static AsciiString val = "english";
if (cached) {
return val;
} else {
cached = TRUE;
}
GetStringFromRegistry("", "Language", val);
return val;
}
AsciiString GetRegistryGameName(void)
{
AsciiString val = "GeneralsMPTest";
GetStringFromRegistry("", "SKU", val);
return val;
}
UnsignedInt GetRegistryVersion(void)
{
UnsignedInt val = 65536;
GetUnsignedIntFromRegistry("", "Version", val);
return val;
}
UnsignedInt GetRegistryMapPackVersion(void)
{
UnsignedInt val = 65536;
GetUnsignedIntFromRegistry("", "MapPackVersion", val);
return val;
}