/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (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"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
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
// ------------------------------------------------------------------------------------------------
void Xfer::xferSTLObjectIDVector( std::vector *objectIDVectorData )
{
//
// 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 vector
UnsignedShort listCount = objectIDVectorData->size();
xferUnsignedShort( &listCount );
// xfer vector data
ObjectID objectID;
if( getXferMode() == XFER_SAVE || getXferMode() == XFER_CRC )
{
// save all ids
std::vector< ObjectID >::const_iterator it;
for( it = objectIDVectorData->begin(); it != objectIDVectorData->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( objectIDVectorData->size() != 0 )
{
DEBUG_CRASH(( "Xfer::xferSTLObjectIDList - object vector 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 );
objectIDVectorData->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
}
// ------------------------------------------------------------------------------------------------
/** 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(( "xferScienceType - 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 )
{
// Not worth an assert, since things can give you Sciences on creation. Just handle it and load.
scienceVec->clear();
// Homework for today. Write 2000 words reconciling "Your code must never crash" with "Intentionally putting crashes in the code". Fucktard.
// 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( UpgradeMaskType *upgradeMaskData )
{
// this deserves a version number
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xferVersion( &version, currentVersion );
//Kris: The Upgrade system has been converted from Int64 to BitFlags. However because the
//names of upgrades are saved to preserve order reassignments (inserting a new upgrade in
//the INI file will skew the bit values), we must continue saving the names of the upgrades
//in order to recalculate the actual bit value of said upgrade.
//---------------------------------------------------------------------------------------------
//NOTE: The xfer code didn't have to change with the bitset upgrades, because either way, we're
//converting data <-> Ascii, so the minor syntax works with the before and after code!
// 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( upgradeMaskData->testForAll( 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( upgradeMaskData->testForAll( 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->clear();
// 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
upgradeMaskData->set( upgradeTemplate->getUpgradeMask() );
} // end for i
} // end else if, load
else if( getXferMode() == XFER_CRC )
{
// just xfer implementation the data itself
xferImplementation( upgradeMaskData, sizeof( UpgradeMaskType ) );
} // 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);
}