mirror of
https://github.com/electronicarts/CnC_Generals_Zero_Hour.git
synced 2025-12-16 23:51:41 -05:00
Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
235
Generals/Code/GameEngine/Source/Common/System/ArchiveFile.cpp
Normal file
235
Generals/Code/GameEngine/Source/Common/System/ArchiveFile.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
483
Generals/Code/GameEngine/Source/Common/System/AsciiString.cpp
Normal file
483
Generals/Code/GameEngine/Source/Common/System/AsciiString.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
1579
Generals/Code/GameEngine/Source/Common/System/BuildAssistant.cpp
Normal file
1579
Generals/Code/GameEngine/Source/Common/System/BuildAssistant.cpp
Normal file
File diff suppressed because it is too large
Load Diff
297
Generals/Code/GameEngine/Source/Common/System/CDManager.cpp
Normal file
297
Generals/Code/GameEngine/Source/Common/System/CDManager.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
204
Generals/Code/GameEngine/Source/Common/System/Compression.cpp
Normal file
204
Generals/Code/GameEngine/Source/Common/System/Compression.cpp
Normal 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
|
||||
233
Generals/Code/GameEngine/Source/Common/System/CopyProtection.cpp
Normal file
233
Generals/Code/GameEngine/Source/Common/System/CopyProtection.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
964
Generals/Code/GameEngine/Source/Common/System/DataChunk.cpp
Normal file
964
Generals/Code/GameEngine/Source/Common/System/DataChunk.cpp
Normal 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;
|
||||
}
|
||||
779
Generals/Code/GameEngine/Source/Common/System/Debug.cpp
Normal file
779
Generals/Code/GameEngine/Source/Common/System/Debug.cpp
Normal 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, <20>berhitzte Hardware und Hardware, die den Mindestanforderungen des Spiels nicht entspricht, ausgel<65>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);
|
||||
}
|
||||
135
Generals/Code/GameEngine/Source/Common/System/Directory.cpp
Normal file
135
Generals/Code/GameEngine/Source/Common/System/Directory.cpp
Normal 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
|
||||
@@ -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 );
|
||||
}
|
||||
259
Generals/Code/GameEngine/Source/Common/System/File.cpp
Normal file
259
Generals/Code/GameEngine/Source/Common/System/File.cpp
Normal 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());
|
||||
}
|
||||
324
Generals/Code/GameEngine/Source/Common/System/FileSystem.cpp
Normal file
324
Generals/Code/GameEngine/Source/Common/System/FileSystem.cpp
Normal 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 );
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
66
Generals/Code/GameEngine/Source/Common/System/GameCommon.cpp
Normal file
66
Generals/Code/GameEngine/Source/Common/System/GameCommon.cpp
Normal 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;
|
||||
}
|
||||
|
||||
3594
Generals/Code/GameEngine/Source/Common/System/GameMemory.cpp
Normal file
3594
Generals/Code/GameEngine/Source/Common/System/GameMemory.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
Generals/Code/GameEngine/Source/Common/System/GameType.cpp
Normal file
46
Generals/Code/GameEngine/Source/Common/System/GameType.cpp
Normal 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
|
||||
};
|
||||
558
Generals/Code/GameEngine/Source/Common/System/Geometry.cpp
Normal file
558
Generals/Code/GameEngine/Source/Common/System/Geometry.cpp
Normal 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
|
||||
137
Generals/Code/GameEngine/Source/Common/System/KindOf.cpp
Normal file
137
Generals/Code/GameEngine/Source/Common/System/KindOf.cpp
Normal 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
|
||||
|
||||
451
Generals/Code/GameEngine/Source/Common/System/List.cpp
Normal file
451
Generals/Code/GameEngine/Source/Common/System/List.cpp
Normal 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;
|
||||
}
|
||||
627
Generals/Code/GameEngine/Source/Common/System/LocalFile.cpp
Normal file
627
Generals/Code/GameEngine/Source/Common/System/LocalFile.cpp
Normal 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;
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
754
Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp
Normal file
754
Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
82
Generals/Code/GameEngine/Source/Common/System/QuickTrig.cpp
Normal file
82
Generals/Code/GameEngine/Source/Common/System/QuickTrig.cpp
Normal 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
508
Generals/Code/GameEngine/Source/Common/System/RAMFile.cpp
Normal file
508
Generals/Code/GameEngine/Source/Common/System/RAMFile.cpp
Normal 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;
|
||||
}
|
||||
1546
Generals/Code/GameEngine/Source/Common/System/Radar.cpp
Normal file
1546
Generals/Code/GameEngine/Source/Common/System/Radar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1666
Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Normal file
1666
Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
56
Generals/Code/GameEngine/Source/Common/System/Snapshot.cpp
Normal file
56
Generals/Code/GameEngine/Source/Common/System/Snapshot.cpp
Normal 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
|
||||
634
Generals/Code/GameEngine/Source/Common/System/StackDump.cpp
Normal file
634
Generals/Code/GameEngine/Source/Common/System/StackDump.cpp
Normal 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
328
Generals/Code/GameEngine/Source/Common/System/String.cpp
Normal file
328
Generals/Code/GameEngine/Source/Common/System/String.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
1753
Generals/Code/GameEngine/Source/Common/System/Trig.cpp
Normal file
1753
Generals/Code/GameEngine/Source/Common/System/Trig.cpp
Normal file
File diff suppressed because it is too large
Load Diff
363
Generals/Code/GameEngine/Source/Common/System/UnicodeString.cpp
Normal file
363
Generals/Code/GameEngine/Source/Common/System/UnicodeString.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
488
Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp
Normal file
488
Generals/Code/GameEngine/Source/Common/System/Upgrade.cpp
Normal 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() );
|
||||
|
||||
}
|
||||
|
||||
797
Generals/Code/GameEngine/Source/Common/System/Xfer.cpp
Normal file
797
Generals/Code/GameEngine/Source/Common/System/Xfer.cpp
Normal 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( ®ion3D->lo );
|
||||
xferCoord3D( ®ion3D->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( ®ion2D->lo );
|
||||
xferCoord2D( ®ion2D->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);
|
||||
}
|
||||
|
||||
|
||||
357
Generals/Code/GameEngine/Source/Common/System/XferCRC.cpp
Normal file
357
Generals/Code/GameEngine/Source/Common/System/XferCRC.cpp
Normal 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
|
||||
260
Generals/Code/GameEngine/Source/Common/System/XferLoad.cpp
Normal file
260
Generals/Code/GameEngine/Source/Common/System/XferLoad.cpp
Normal 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
|
||||
|
||||
358
Generals/Code/GameEngine/Source/Common/System/XferSave.cpp
Normal file
358
Generals/Code/GameEngine/Source/Common/System/XferSave.cpp
Normal 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
|
||||
106
Generals/Code/GameEngine/Source/Common/System/encrypt.cpp
Normal file
106
Generals/Code/GameEngine/Source/Common/System/encrypt.cpp
Normal 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
|
||||
|
||||
182
Generals/Code/GameEngine/Source/Common/System/registry.cpp
Normal file
182
Generals/Code/GameEngine/Source/Common/System/registry.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user