/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/******************************************************************************
*
* FILE
*     $Archive: /Commando/Code/WWOnline/GameResPacket.cpp $
*
* DESCRIPTION
*
* PROGRAMMER
*     $Author: Denzil_l $
*
* VERSION INFO
*     $Revision: 4 $
*     $Modtime: 12/11/01 6:23p $
*
******************************************************************************/

#include "GameResPacket.h"
#include <assert.h>
#include <string.h>
#include <winsock.h>

namespace WWOnline {

/**************************************************************************
 * GameResPacket::~GameResPacket -- destroys a packet class be freeing list   *
 *                                                                        *
 * INPUT:      none                                                       *
 *                                                                        *
 * OUTPUT:     none                                                       *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/24/1996 PWG : Created.                                            *
 *========================================================================*/
GameResPacket::~GameResPacket(void)
	{
	GameResField* current;
	GameResField* next;

	// Loop through the entire field list and delete each entry.
	for (current = mHead; current; current = next)
		{
		next = current->mNext;
		delete current;
		}
	}


/**************************************************************************
 * GameResPacket::ADD_FIELD -- Adds a GameResField entry to head of packet li *
 *                                                                        *
 * INPUT:      GameResField * - a properly constructed field class entry.   *
 *                                                                        *
 * OUTPUT:     none                                                       *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/24/1996 PWG : Created.                                            *
 *========================================================================*/
void GameResPacket::Add_Field(GameResField *field)
	{
	field->mNext = mHead;
	mHead = field;
	}


/**************************************************************************
 * GameResPacket::GameResPacket -- Creates a Packet object from a COMMS packe *
 *                                                                        *
 * INPUT:                                                                 *
 *                                                                        *
 * OUTPUT:                                                                *
 *                                                                        *
 * WARNINGS:                                                              *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/22/1996 PWG : Created.                                            *
 *========================================================================*/
GameResPacket::GameResPacket(unsigned char* curbuf)
	{
	// Pull the size and packet ID out of the linear packet stream.
	mSize = ntohl(*(unsigned long*)curbuf);
	curbuf += sizeof(mSize);

	mID = ntohs(*(unsigned short*)curbuf);
	curbuf += sizeof(mID);
	
	mReserved = ntohs(*(unsigned short*)curbuf);
	curbuf += sizeof(mReserved);

	mHead = NULL;

	// Calculate the remaining size so that we can loop through the
	// packets and extract them.
	unsigned long remaining_size = (mSize - (sizeof(mSize) + sizeof(mID) + sizeof(mReserved)));

	// Loop through the linear packet until we run out of room and
	// create a field for each.
	while (remaining_size > 0)
		{
		GameResField* field = new GameResField;

		// Copy the adjusted header into the buffer and then advance the buffer
		memcpy(field, curbuf, GAMERESFIELD_HEADER_SIZE);
		curbuf += GAMERESFIELD_HEADER_SIZE;
		remaining_size -= GAMERESFIELD_HEADER_SIZE;

		// Copy the data into the buffer
		unsigned short size = ntohs(field->mSize);
		field->mData = new unsigned char[size];
		memcpy(field->mData, curbuf, size);
		curbuf += size;
		remaining_size -= size;

		// Make sure we allow for the pad bytes.
		int pad = (4 - (ntohs(field->mSize) & 3)) & 3;
		curbuf += pad;
		remaining_size -= pad;

		// Convert the field back to the host format
		field->Net_To_Host();

		// Finally add the field to the field list in the packet
		// structure.
		Add_Field(field);
		}
	}


/**************************************************************************
 * CREATE_COMMS_PACKET -- Walks field list creating a packet              *
 *                                                                        *
 * INPUT:      short - the id of the packet so the server can identify it *
 *               unsigned short & - the size of the packet returned here  *
 *                                                                        *
 * OUTPUT:     void * pointer to the linear packet data                   *
 *                                                                        *
 * WARNINGS:    This routine allocates memory that the user is responsible*
 *              for freeing.                                              *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/22/1996 PWG : Created.                                            *
 *========================================================================*/
unsigned char* GameResPacket::Create_Comms_Packet(unsigned long& size, char* sig_name, unsigned long& sig_offset)
	{
	GameResField* current;
	sig_offset = 0;

	// Size starts at 8 because that is the size of the packet header. (size[4] + id[2] + reserved[2])
	size = (sizeof(mSize) + sizeof(mID) + sizeof(mReserved));

	// Take a quick spin through and calculate the size of the packet we
	// are building.
	for (current = mHead; current; current = current->mNext)
		{
		size += (unsigned long)GAMERESFIELD_HEADER_SIZE;			// add in packet header size
		size += current->mSize;				// add in data size
		size += (4 - (current->mSize & 3)) & 3;		// add in pad value to dword align next packet
		}

	// Now that we know the size allocate a buffer big enough to hold the
	// packet.
	unsigned char* bufferStart = new unsigned char[size];
	unsigned char* curbuf = bufferStart;

	// write the size into the packet header
	*(unsigned long*)curbuf = htonl(size);
	curbuf += sizeof(unsigned long);

	*(unsigned short*)curbuf = htons(mID);
	curbuf += sizeof(unsigned short);

	*(unsigned short*)curbuf = htons(mReserved);
	curbuf += sizeof(unsigned short);

	// Ok now that the actual header information has been written we need to write out
	// field information.
	for (current = mHead; current; current = current->mNext)
		{
		#ifdef _DEBUG
		current->DebugDump();
		#endif

		unsigned short fieldSize = current->mSize;

		// Temporarily convert the packet to net format (this saves alot of
		// effort, and seems safe...)
		current->Host_To_Net();

		// Copy the adjusted header into the buffer and then advance the buffer
		memcpy(curbuf, current, GAMERESFIELD_HEADER_SIZE);
		curbuf += GAMERESFIELD_HEADER_SIZE;

		// If this is the sig data then make a note of the offset into the packet.
		if (sig_name && strncmp(sig_name, current->mID, 4) == 0)
			{
			// If sig_offset isn't zero then that would indicate that there were multiple sig entries in the packet.
			assert(sig_offset == 0);
			sig_offset = (curbuf - bufferStart);
			}

		// Copy the data into the buffer and then advance the buffer
		memcpy(curbuf, current->mData, fieldSize);
		curbuf += fieldSize;

		// Finally take care of any pad bytes by setting them to 0
		int pad = ((4 - (fieldSize & 3)) & 3);

		// If there is any pad left over, make sure you memset it
		// to zeros, so it looks like a pad.
		if (pad)
			{
			memset(curbuf, 0, pad);
			curbuf += pad;
			}

		current->Net_To_Host();
		}

	return bufferStart;
	}


/**************************************************************************
 * GameResPacket::FIND_FIELD -- Finds a field if it exists in the packets   *
 *                                                                        *
 * INPUT:      char *  - the id of the field we are looking for.          *
 *                                                                        *
 * OUTPUT:     GameResField * pointer to the field class                    *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
GameResField *GameResPacket::Find_Field(char *id)
	{
	for (GameResField *current = mHead; current; current = current->mNext)
		{
		if (strncmp(id, current->mID, 4) == 0)
			return current;
		}

	return NULL;
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:      char *   - the id of the field that holds the data.        *
 *             char &   - the reference to store the data into            *
 *                                                                        *
 * OUTPUT:     true if the field was found, false if it was not.          *
 *                                                                        *
 * WARNINGS:   The data reference is not changed if the field is not      *
 *               found.                                                   *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, char &data)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		data = *((char*)field->mData);
		}

	return((field) ? true : false);
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:      char *   - the id of the field that holds the data.        *
 *               unsigned char &   - the reference to store the data into *
 *                                                                        *
 * OUTPUT:      true if the field was found, false if it was not.         *
 *                                                                        *
 * WARNINGS:   The data reference is not changed if the field is not      *
 *               found.                                                   *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, unsigned char &data)
{
	GameResField *field = Find_Field(id);

	if (field)
		{
		data = *((unsigned char *)field->mData);
		}

	return((field) ? true : false);
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:      char *   - the id of the field that holds the data.        *
 *               short &   - the reference to store the data into         *
 *                                                                        *
 * OUTPUT:      true if the field was found, false if it was not.         *
 *                                                                        *
 * WARNINGS:   The data reference is not changed if the field is not      *
 *               found.                                                   *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, short &data)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		data = *((short *)field->mData);
		}

	return((field) ? true : false);
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:      char *   - the id of the field that holds the data.        *
 *               unsigned short &   - the reference to store the data into*
 *                                                                        *
 * OUTPUT:      true if the field was found, false if it was not.         *
 *                                                                        *
 * WARNINGS:   The data reference is not changed if the field is not      *
 *               found.                                                   *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, unsigned short &data)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		data = *((unsigned short *)field->mData);
		}

	return((field) ? true : false);
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:      char *   - the id of the field that holds the data.        *
 *               long &   - the reference to store the data into          *
 *                                                                        *
 * OUTPUT:      true if the field was found, false if it was not.         *
 *                                                                        *
 * WARNINGS:   The data reference is not changed if the field is not      *
 *               found.                                                   *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, long &data)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		data = *((long *)field->mData);
		}

	return((field) ? true : false);
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data as a string          *
 *                                                                        *
 * INPUT:      char *   - the id of the field that holds the data.        *
 *               char *   - the string to store the data into             *
 *                                                                        *
 * OUTPUT:      true if the field was found, false if it was not.         *
 *                                                                        *
 * WARNINGS:   The string is not changed if the field is not found.  It   *
 *               is assumed that the string variabled specified by the    *
 *               pointer is large enough to hold the data.                *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, char *data)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		strcpy(data, (char *)field->mData);
		}

	return((field) ? true : false);
	}



/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:    char *   - the id of the field that holds the data           *
 *           unsigned long &   - the reference to store the data into     *
 *                                                                        *
 * OUTPUT:   true if the field was found, false if it was not.            *
 *                                                                        *
 * WARNINGS: The data reference is not changed if the field is not        *
 *           found.                                                       *
 *                                                                        *
 * HISTORY:                                                               *
 *   04/23/1996 PWG : Created.                                            *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, unsigned long &data)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		data = *((unsigned long *)field->mData);
		}

	return((field) ? true : false);
	}


/**************************************************************************
 * GET_FIELD -- Find specified name and returns data                      *
 *                                                                        *
 * INPUT:   char *   - the id of the field that holds the data.           *
 *          void * - the reference to store the data into                 *
 *           int    - the length of the buffer passed in                  *
 *                                                                        *
 * OUTPUT:  true if the field was found, false if it was not.             *
 *                                                                        *
 * WARNINGS:  The data reference is not changed if the field is not       *
 *            found.                                                      *
 *                                                                        *
 * HISTORY:                                                               *
 *   6/4/96 4:46PM ST : Created                                           *
 *========================================================================*/
bool GameResPacket::Get_Field(char *id, void *data, int &length)
	{
	GameResField *field = Find_Field(id);

	if (field)
		{
		memcpy(data, field->mData, min((int)field->mSize, length));
		length = (int) field->mSize;
		}

	return((field) ? true : false);
	}

} // namespace WWOnline
