/*
**	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/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Command & Conquer                                            *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Commando/datasafe.cpp                        $*
 *                                                                                             *
 *                      $Author:: Steve_t                                                     $*
 *                                                                                             *
 *                     $Modtime:: 10/10/02 2:38p                                              $*
 *                                                                                             *
 *                    $Revision:: 22                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "always.h"
#include "crandom.h"
#include "datasafe.h"
#include "vector.h"

#include <malloc.h>
#include <windows.h>
#include "systimer.h"
#include <stdio.h>

/*
** Renegade specific includes. For reporting tampering.
*/
#include "cstextobj.h"
#include "cnetwork.h"
#include "player.h"
#include "playermanager.h"
#include "devoptions.h"
#include "string_ids.h"
#include "translatedb.h"


/*
** Unreferenced local variable.
*/
#ifndef WWDEBUG
#pragma warning(disable : 4101)
#endif

/*
** warning C4073: initializers put in library initialization area
*/
//#pragma warning(disable : 4073)

/*
** Put this data in the lib init area so it gets initialised first.
*/
//#pragma init_seg(lib)

/*
** Static data. See class definition for details.
**
** I need to make this stuff static so that I can templatize the derived class and have all expansions use the same data.
*/
unsigned long GenericDataSafeClass::SimpleKey;
unsigned long GenericDataSafeClass::HandleKey;
unsigned long GenericDataSafeClass::Checksum;
unsigned long GenericDataSafeClass::ShuffleDelay;
unsigned long GenericDataSafeClass::SecurityCheckDelay;
DataSafeHandleClass GenericDataSafeClass::SentinelOne = 0;
int GenericDataSafeClass::NumLists = 0;
DataSafeEntryListClass *GenericDataSafeClass::Safe[MAX_DATASAFE_LISTS];
DataSafeEntryTypeClass GenericDataSafeClass::TypeList[MAX_DATASAFE_TYPES];
int GenericDataSafeClass::TypeListCount = 0;
DataSafeHandleClass GenericDataSafeClass::SentinelTwo = 0;
int GenericDataSafeClass::CRCErrors = 0;
#ifdef THREAD_SAFE_DATA_SAFE
HANDLE GenericDataSafeClass::SafeMutex;
#else //THREAD_SAFE_DATA_SAFE
unsigned int GenericDataSafeClass::PreferredThread = GetCurrentThreadId();
#endif //THREAD_SAFE_DATA_SAFE

#ifdef WWDEBUG
unsigned long GenericDataSafeClass::LastDump = TIMEGETTIME();
int GenericDataSafeClass::NumSwaps = 0;
int GenericDataSafeClass::NumFetches = 0;
int GenericDataSafeClass::SlopCount = 0;
int GenericDataSafeClass::NumSecurityChecks = 0;
int GenericDataSafeClass::NumShuffles = 0;
#endif	//WWDEBUG

static DynamicVectorClass<int> random_list_contenders;

/*
** Buffer to return a reference to in error cases.
*/
char ErrorVal[1024] = {0,0,0,0};

/*
** Instances of data safes. One for each type of data we will be storing in the safe.
**
** For each type, we have to declare more than just the class, we have to declare static data too. This is taken care of by
** the DECLARE_DATA_SAFE macro.
**
** All data safe type MUST be declared here so that they are constructed after the ::TypeList and ::TypeListCount statics above.
**
** A data safe of type 'int' must always be declared first.
**
*/
typedef unsigned int DATASAFE_UNSIGNED_INT;
DECLARE_DATA_SAFE(int);
DECLARE_DATA_SAFE(DATASAFE_UNSIGNED_INT);
DECLARE_DATA_SAFE(float);
DECLARE_DATA_SAFE(double);

/*
** Define this to set the keys to a fixed value to aid debugging.
*/
//#define FIXED_KEY


/***********************************************************************************************
 * GenericDataSafeClass::GenericDataSafeClass -- Class constructor.                            *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/3/2001 2:58PM ST : Created                                                              *
 *=============================================================================================*/
GenericDataSafeClass::GenericDataSafeClass(void)
{
	/*
	** This constructor could be called lots of times as new types are added to the data safe. Make sure we don't get an
	** unexpected reset if we have already added types.
	*/
	if (TypeListCount == 0) {

#ifdef THREAD_SAFE_DATA_SAFE
		SafeMutex = CreateMutex(NULL, false, NULL);
#else
		PreferredThread = GetCurrentThreadId();
#endif //THREAD_SAFE_DATA_SAFE

#ifdef FIXED_KEY
		SimpleKey = 0x80000000;
		HandleKey = 0x40000000;
#else	//FIXED_KEY
		SimpleKey = 0x55555555 ^ TIMEGETTIME() ^ FreeRandom.Get_Int(0, 0xffffffff);
		HandleKey = 0xaaaaaaaa ^ (TIMEGETTIME()*2) ^ FreeRandom.Get_Int(0, 0xffffffff);
#endif	//FIXED_KEY
		NumLists = 0;
		Checksum = ~SimpleKey;
		ShuffleDelay = TIMEGETTIME();
		SecurityCheckDelay = TIMEGETTIME();
		memset(&Safe[0], 0, sizeof(Safe));

		Reset();

	} else {
		if (TypeListCount == 1 && SentinelOne == 0 && SentinelTwo == 0) {

			/*
			** Add sentinels if we already know about the 'int' type.
			*/
			int s1 = SENTINEL_ONE;
			int s2 = SENTINEL_TWO;
			SentinelOne = DataSafeClass<int>::Add_Entry(s1);
			SentinelTwo = DataSafeClass<int>::Add_Entry(s2);
		}
	}
}



/***********************************************************************************************
 * GenericDataSafeClass::Reset_Timers -- Handle timer reset                                    *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   1/31/2002 12:12PM ST : Created                                                            *
 *=============================================================================================*/
void GenericDataSafeClass::Reset_Timers(void)
{
	ShuffleDelay = TIMEGETTIME();
	SecurityCheckDelay = TIMEGETTIME();
#ifdef WWDEBUG
	LastDump = TIMEGETTIME();
#endif //WWDEBUG
}



/***********************************************************************************************
 * GenericDataSafeClass::Reset -- Reset the safe stats.                                        *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/30/2001 11:38AM ST : Created                                                            *
 *=============================================================================================*/
void GenericDataSafeClass::Reset(void)
{
	CRCErrors = 0;

#ifdef WWDEBUG
	LastDump = TIMEGETTIME();
	NumSwaps = 0;
	NumFetches = 0;
	SlopCount = 0;
	NumSecurityChecks = 0;
	NumShuffles = 0;
#endif	//WWDEBUG

	if (NumLists) {
		Shuffle();
		Security_Check();
	}
}



/***********************************************************************************************
 * GenericDataSafeClass::Shutdown -- Free any remaining memory if we are finished with the safe*
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/6/2001 1:47PM ST : Created                                                              *
 *=============================================================================================*/
void GenericDataSafeClass::Shutdown(void)
{
	/*
	** If there is nothing left in the data safe - not even slop - then free the list memory.
	*/
	bool found = false;
	for (int i=0 ; i<NumLists ; i++) {
		ds_assert(Safe[i] != NULL);

		if (Safe[i] != NULL && Safe[i]->EntryCount != 0) {
			found = true;
			break;
		}
	}

	if (!found) {
		for (int i=0 ; i<NumLists ; i++) {
			delete Safe[i];
		}
		NumLists = 0;

#ifdef THREAD_SAFE_DATA_SAFE
		CloseHandle(SafeMutex);
#endif //THREAD_SAFE_DATA_SAFE
	}
}




/***********************************************************************************************
 * GenericDataSafeClass::Get_Entry -- Find a raw entry in the data safe                        *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Handle                                                                            *
 *                                                                                             *
 * OUTPUT:   Pointer to raw entry (including header) or NULL if not found                      *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/13/2001 9:43PM ST : Created                                                             *
 *=============================================================================================*/
DataSafeEntryClass *GenericDataSafeClass::Get_Entry(DataSafeHandleClass handle)
{
	int list;

#ifdef	WWDEBUG
	NumFetches++;
#endif	//WWDEBUG

	/*
	** Check safe integrity.
	*/
	Security_Check();

	/*
	** The handles we give out are encrypted so we need to decrypt temporarily to extract the list number.
	*/
	DataSafeHandleClass new_handle = handle ^ HandleKey;
	list = new_handle.Handle.Part.List;

	/*
	** Apply the current data key to the handle for matching purposes.
	*/
	DataSafeHandleClass match_handle = new_handle ^ SimpleKey;

	ds_assert(list >= 0);
	ds_assert(list < NumLists);
	ds_assert(Safe[list] != NULL);

	/*
	** Get the head of the list.
	*/
	DataSafeEntryClass *entry_ptr = Safe[list]->SafeList;

	/*
	** Search through until we find a matching handle. Check to make sure we don't get stuck in an infinite loop.
	*/
	int times = 0;
	while (entry_ptr) {
		if (entry_ptr->Handle == match_handle) {
			return(entry_ptr);
		}
		entry_ptr = entry_ptr->Next;
		times++;
		ds_assert(times <= MAX_ENTRIES_PER_LIST);
		if (times > MAX_ENTRIES_PER_LIST) {
			entry_ptr = NULL;
			break;
		}
	}

	/*
	** Falling through to here means no match found and we return NULL as an error.
	*/
	WWDEBUG_SAY(("WARNING: Data Safe: No item found for handle %08x\n", (int)handle));
	ds_assert(entry_ptr != NULL);
	return(NULL);
}




/***********************************************************************************************
 * GenericDataSafeClass::Get_Entry -- Find a raw entry in the data safe                        *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Handle                                                                            *
 *                                                                                             *
 * OUTPUT:   Type of object that handle represents                                             *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/17/2001 12:44PM ST : Created                                                            *
 *=============================================================================================*/
int GenericDataSafeClass::Get_Entry_Type(DataSafeHandleClass handle)
{
	int list;

	/*
	** Check safe integrity.
	*/
	Security_Check();

	/*
	** The handles we give out are encrypted so we need to decrypt temporarily to extract the list number.
	*/
	DataSafeHandleClass new_handle = handle ^ HandleKey;
	list = new_handle.Handle.Part.List;

	ds_assert(list >= 0);
	ds_assert(list < NumLists);
	ds_assert(Safe[list] != NULL);

	/*
	** Return the type of data stored in this list.
	*/
	return(Safe[list]->EntryType);
}


/***********************************************************************************************
 * GenericDataSafeClass::Get_Entry_By_Index -- Find the nth entry in the given data safe list  *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    List number                                                                       *
 *           Entry number                                                                      *
 *                                                                                             *
 * OUTPUT:   Pointer to entry or NULL if not found                                             *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/13/2001 9:43PM ST : Created                                                             *
 *=============================================================================================*/
DataSafeEntryClass *GenericDataSafeClass::Get_Entry_By_Index(int list, int index)
{
#ifdef	WWDEBUG
	NumFetches++;
#endif	//WWDEBUG

	/*
	** Locals.
	*/
	int count = 0;

	/*
	** Input ds_asserts.
	*/
	ds_assert(list >= 0);
	ds_assert(list < NumLists);
	ds_assert(Safe[list] != NULL);

	/*
	** Get the head of the list.
	*/
	DataSafeEntryClass *entry_ptr = Safe[list]->SafeList;

	/*
	** Iterate through the list, counting entries, until we find the requested entry number.
	*/
	while (entry_ptr) {
		if (count == index) {
			return(entry_ptr);
		}
		entry_ptr = entry_ptr->Next;
		count++;
		ds_assert(count < MAX_ENTRIES_PER_LIST);
	}

	/*
	** Falling through to here means no match found and we return NULL as an error.
	*/
	WWDEBUG_SAY(("WARNING: Data Safe: No item found for list %d, index %d\n", list, index));
	ds_assert(entry_ptr != NULL);
	return(NULL);
}






/***********************************************************************************************
 * GenericDataSafeClass::Mem_Copy_Encrypt -- Copy and encrypt data in one pass.                *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to data dest                                                                  *
 *           Ptr to data source                                                                *
 *           Length of data in bytes                                                           *
 *           Should we update the checksum                                                     *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/19/2001 9:29PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Mem_Copy_Encrypt(void *dest, void *src, int size, bool do_checksum)
{
	ds_assert((size % 4) == 0);
	unsigned long temp;
	unsigned long *s = (unsigned long *) src;
	unsigned long *d = (unsigned long *) dest;

	if (do_checksum) {
		for (int i = 0 ; i < (size / 4) ; i++) {
			temp = *s++;
			temp = temp ^ SimpleKey;
			Checksum = Checksum ^ temp;
			*d++ = temp;
		}
	} else {
		for (int i = 0 ; i < (size / 4) ; i++) {
			*d++ = *s++ ^ SimpleKey;
		}
	}
}




/***********************************************************************************************
 * GenericDataSafeClass::Mem_Copy_Decrypt -- Copy and decrypt data in one pass.                *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to data dest                                                                  *
 *           Ptr to data source                                                                *
 *           Length of data in bytes                                                           *
 *           Should we update the checksum                                                     *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/19/2001 9:29PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Mem_Copy_Decrypt(void *dest, void *src, int size, bool do_checksum)
{
	ds_assert((size % 4) == 0);
	unsigned long temp;
	unsigned long *s = (unsigned long *) src;
	unsigned long *d = (unsigned long *) dest;

	if (do_checksum) {
		for (int i = 0 ; i < (size / 4) ; i++) {
			temp = *s++;
			Checksum = Checksum ^ temp;
			temp = temp ^ SimpleKey;
			*d++ = temp;
		}
	} else {
		for (int i = 0 ; i < (size / 4) ; i++) {
			*d++ = *s++ ^ SimpleKey;
		}
	}
}


/***********************************************************************************************
 * GenericDataSafeClass::Encrypt -- Encrypt data.                                              *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to data to encrypt                                                            *
 *           Length of data in bytes                                                           *
 *           Key to use                                                                        *
 *           Should we update the checksum                                                     *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/19/2001 9:29PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Encrypt(void *data, int size, unsigned long key, bool do_checksum)
{
	ds_assert((size % 4) == 0);
	unsigned long *data_ptr = (unsigned long*)data;

	if (do_checksum) {
		for (int i = 0 ; i < (size / 4) ; i++) {
			*data_ptr ^= key;
			Checksum ^= *data_ptr++;
		}
	} else {
		for (int i = 0 ; i < (size / 4) ; i++) {
			*data_ptr ^= key;
		}
	}
}


/***********************************************************************************************
 * GenericDataSafeClass::Decrypt -- Decrypt data.                                              *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to data to decrypt                                                            *
 *           Length of data in bytes                                                           *
 *           Key to use                                                                        *
 *           Should we update the checksum                                                     *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/19/2001 9:29PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Decrypt(void *data, int size, unsigned long key, bool do_checksum)
{
	ds_assert((size % 4) == 0);
	unsigned long *data_ptr = (unsigned long*)data;

	if (do_checksum) {
		for (int i = 0 ; i < (size / 4) ; i++) {
			Checksum ^= *data_ptr;
			*data_ptr++ ^= key;
		}
	} else {
		for (int i = 0 ; i < (size / 4) ; i++) {
			*data_ptr++ ^= key;
		}
	}
}





/***********************************************************************************************
 * GenericDataSafeClass::Get_Random_List_For_Insertion -- Get a random list to insert an item  *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Item type                                                                         *
 *                                                                                             *
 * OUTPUT:   List index                                                                        *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/25/2001 12:07PM ST : Created                                                            *
 *=============================================================================================*/
int GenericDataSafeClass::Get_Random_List_For_Insertion(int type)
{
	ThreadLockClass locker;

	random_list_contenders.Reset_Active();

	/*
	** Build a list of lists that contain the appropriate size data and have room for extra entries.
	*/
	for (int i=0 ; i<NumLists ; i++) {
		ds_assert(Safe[i] != NULL);
		if (Safe[i] != NULL && (Safe[i]->EntryType == type || Safe[i]->EntryType == -1) && Safe[i]->EntryCount < MAX_ENTRIES_PER_LIST) {
			random_list_contenders.Add(i);
		}
	}

	/*
	** If no lists are available then we need to create one.
	*/
	if (random_list_contenders.Count() == 0) {
		int safe_index = Create_Safe_List(type);
		ds_assert(safe_index != -1);
		ds_assert(Safe[safe_index] != NULL);
		return(safe_index);
	}

	/*
	** Choose a random list from the possible contenders.
	*/
	int pick = 0;
	if (random_list_contenders.Count() > 1) {
		pick = FreeRandom.Get_Int(0, random_list_contenders.Count()-1);
	}
	ds_assert(pick >= 0 && pick < random_list_contenders.Count());

	/*
	** If the list we picked is currently empty then we might need to do some fixup before we can use it.
	*/
	int list_index = random_list_contenders[pick];
	ds_assert(Safe[list_index] != NULL);
	if (Safe[list_index]->EntryType == -1 || Safe[list_index]->SafeList == NULL) {
		Safe[list_index]->EntryType = type;
		Safe[list_index]->SlopCount = 0;
	}

	return(list_index);
}



/***********************************************************************************************
 * GenericDataSafeClass::Create_Safe_List -- Create a single data safe list of specified type  *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Type of list to create                                                            *
 *                                                                                             *
 * OUTPUT:   Index of list. -1 if unable to create                                             *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/25/2001 12:27PM ST : Created                                                            *
 *=============================================================================================*/
int GenericDataSafeClass::Create_Safe_List(int type)
{
	/*
	** Create a new list entry.
	*/
	DataSafeEntryListClass *safe_list = new DataSafeEntryListClass;
	ds_assert(safe_list->EntryCount == 0);
	ds_assert(safe_list->SafeList == NULL);
	safe_list->EntryType = type;
	safe_list->SlopCount = 0;

	/*
	** Add it to the list of data safes.
	*/
	ds_assert(NumLists < MAX_DATASAFE_LISTS);
	if (NumLists < MAX_DATASAFE_LISTS) {
		Safe[NumLists++] = safe_list;
		return(NumLists-1);
	}

	return(-1);
}



/***********************************************************************************************
 * GenericDataSafeClass::Random_Insertion -- Insert an entry at a random position in the list  *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to data entry                                                                 *
 *           Index of list to add to                                                           *
 *           Type of data (size)                                                               *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/25/2001 12:34PM ST : Created                                                            *
 *=============================================================================================*/
void GenericDataSafeClass::Random_Insertion(DataSafeEntryClass *entry_ptr, int list, int type, bool is_slop)
{
	ds_assert(Safe[list] != NULL);
	ds_assert(Safe[list]->EntryCount < MAX_ENTRIES_PER_LIST);
	ds_assert(Safe[list]->EntryType == type);

	/*
	** If this is the first entry to be added to the list then there is no possibility of random insertion. Just stick it
	** at the beginning.
	*/
	if (Safe[list]->EntryCount == 0) {
		entry_ptr->Next = entry_ptr;
		entry_ptr->Prev = entry_ptr;
		Safe[list]->SafeList = entry_ptr;
		Safe[list]->EntryCount++;
	} else {

		/*
		** Pick a random insertion point.
		*/
		int pick = 0;
		if (Safe[list]->EntryCount > 1) {
			pick = FreeRandom.Get_Int(0, Safe[list]->EntryCount-1);
		}

		/*
		** Find the item to insert ahead of.
		*/
		DataSafeEntryClass *walker = Safe[list]->SafeList;
		int skip = pick;
		while (skip) {
			walker = walker->Next;
			skip--;
		}

		/*
		** Fix up the list pointers to insert.
		*/
		entry_ptr->Prev = walker->Prev;
		walker->Prev->Next = entry_ptr;
		entry_ptr->Next = walker;
		walker->Prev = entry_ptr;

		if (pick == 0) {
			Safe[list]->SafeList = entry_ptr;
		}

		Safe[list]->EntryCount++;
	}
	Safe[list]->SlopCount += (is_slop ? 1 : 0);
}




/***********************************************************************************************
 * GenericDataSafeClass::Swap_Entries -- Swap two entries in memory (must be same type/size).  *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to first entry                                                                *
 *           Ptr to second entry                                                               *
 *           Entry types (must be same type)                                                   *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: Entries must be same size and in the same list. If entries are moved to a         *
 *           different list then the owners handle will no longer work                         *
 *                                                                                             *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/25/2001 1:02PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Swap_Entries(DataSafeEntryClass *first, DataSafeEntryClass *second, int type)
{
	ds_assert(first != NULL);
	ds_assert(second != NULL);
	ds_assert(type >= 0);

#ifdef	WWDEBUG
	NumSwaps++;
#endif	//WWDEBUG

	/*
	** Get pointers to the actual data rider part of the entry.
	*/
	void *first_data = ((char*)first) + sizeof(*first);
	void *second_data = ((char*)second) + sizeof(*second);

	/*
	** Swap the objects in memory. Just swap the data and the handle so that the next and prev pointers are still good
	*/
	if (first && second && first != second) {

		int size = Get_Type_Size(type);	// + sizeof(DataSafeEntryClass);

		/*
		** Optimise for small data types.
		*/
		if (size == 4) {

			/*
			** Convert to long pointers to make it easy to read.
			*/
			long *p1 = (long*) first_data;
			long *p2 = (long*) second_data;

			long temp = *p1;
			*p1 = *p2;
			*p2 = temp;

		} else {

			/*
			** Use a temp buffer on the stack to swap through.
			*/
			void *temp_buffer = _alloca(size);
			memcpy(temp_buffer, first_data, size);
			memcpy(first_data, second_data, size);
			memcpy(second_data, temp_buffer, size);
		}

		/*
		** Swap the handles.
		*/
		DataSafeHandleClass temp_handle = first->Handle;
		bool is_slop = first->IsSlop;

		first->Handle = second->Handle;
		first->IsSlop = second->IsSlop;

		second->Handle = temp_handle;
		second->IsSlop = is_slop;
	}
}








/***********************************************************************************************
 * GenericDataSafeClass::Remove_From_List -- Remove entry from the linked list and fix pointers*
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    List number                                                                       *
 *           Pointer to entry in list                                                          *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/29/2001 10:54AM ST : Created                                                            *
 *=============================================================================================*/
void GenericDataSafeClass::Remove_From_List(int list, DataSafeEntryClass *entry_ptr)
{
	ds_assert(list >= 0);
	ds_assert(list < NumLists);
	ds_assert(Safe[list] != NULL);

	/*
	** We found the entry for this handle. We need to fix up the list pointers and delete the memory.
	*/
	if (Safe[list] && Safe[list]->EntryCount) {

		int data_size = Get_Type_Size(Safe[list]->EntryType);

		if (Safe[list]->EntryCount > 1) {
			entry_ptr->Prev->Next = entry_ptr->Next;
			entry_ptr->Next->Prev = entry_ptr->Prev;

			/*
			** If this entry was at the head of the list then we need to adjust the head pointer.
			*/
			if (Safe[list]->SafeList == entry_ptr) {
				Safe[list]->SafeList = entry_ptr->Next;
			}
		} else {
			Safe[list]->SafeList = NULL;
			Safe[list]->EntryType = -1;
		}

		Safe[list]->EntryCount--;
		if (entry_ptr->IsSlop) {
			Safe[list]->SlopCount--;
		}

		/*
		** Muss up the memory so the data value isn't hanging around.
		*/
		memset(entry_ptr, FreeRandom.Get_Int(0, 255), sizeof(*entry_ptr) + data_size);
	}
}



/***********************************************************************************************
 * GenericDataSafeClass::Shuffle -- Move stuff around in the safe, maybe change the key        *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Forced - set if we should shuffle now - even if it's not time yet                 *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: Sloooooooow                                                                       *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   6/25/2001 9:12PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Shuffle(bool forced)
{
	ThreadLockClass locker;

#ifndef FIXED_KEY

	/*
	** Only check the time every n calls.
	*/
	static unsigned long _calls = 0;
	_calls++;
	if (_calls < DATASAFE_TIME_CHECK_CALLS) {
		return;
	}
	_calls = 0;

	/*
	** Locals.
	*/
	unsigned long new_key;
	unsigned long mod_key;
	int i,j;

	/*
	** We should only do this once in a while
	*/
	unsigned long time = TIMEGETTIME();

	if (forced || time < ShuffleDelay || (time | ShuffleDelay) == 0 || (time - ShuffleDelay) > SHUFFLE_TIME) {
		//WWDEBUG_SAY(("Data Safe: Performing data shuffle and re-key\n"));

#ifdef WWDEBUG
		NumShuffles++;
#endif	//WWDEBUG

		/*
		** Calculate a new key. Lifed from old datasafe code.
		*/
		/*
		** Change keys
		** Given: encrypted item a, old key b, new key c
		** To re-key, the normal algorithm is (a ^ b) ^ c
		** To hide what we're doing, we're going to do
		** this:  a ^ ( b ^ c )
		*/

		/*
		** Clear keys.
		*/
		mod_key ^= mod_key;
		new_key ^= new_key;

		if (SimpleKey == 0) {
			/*
			** Do some error stuff.
			*/
		}

		/*
		** XOR in old key.
		*/
		mod_key ^= SimpleKey;

		/*
		** Generate a new key.
		*/
		new_key = TIMEGETTIME();
		new_key ^= FreeRandom.Get_Int(0, 0xffffffff);

		/*
		** Reset the checksum. Can't keep a running checksum if we re-key
		*/
		Checksum = ~new_key;

		/*
		** Xor in the new key
		*/
		mod_key ^= new_key;

		/*
		** Now, mod_key contains the "modifier key" used to rekey items. We will use it after we have swapped some stuff around.
		*/
		for (i=0 ; i<NumLists ; i++) {
			ds_assert(Safe[i] != NULL);
			if (Safe[i] != NULL && Safe[i]->EntryCount > 1) {
				for (j=0 ; j < max(Safe[i]->EntryCount / 3, 1) ; j++) {

					/*
					** This code has been replaced by the simplified version below to avoid a compiler bug in release mode.
					*/
					//DataSafeEntryClass *first = Get_Entry_By_Index(i, FreeRandom.Get_Int(0, Safe[i]->EntryCount - 1));
					//DataSafeEntryClass *second = Get_Entry_By_Index(i, FreeRandom.Get_Int(0, Safe[i]->EntryCount - 1));

					int first_index = FreeRandom.Get_Int(0, Safe[i]->EntryCount - 1);
					int second_index = FreeRandom.Get_Int(0, Safe[i]->EntryCount - 1);
					DataSafeEntryClass *first = Get_Entry_By_Index(i, first_index);
					DataSafeEntryClass *second = Get_Entry_By_Index(i, second_index);

					if (first && second && first != second) {
						Swap_Entries(first, second, Safe[i]->EntryType);
					}
				}
			}
		}

		/*
		** Clear the old key.
		*/
		SimpleKey ^= SimpleKey;

		/*
		** Now, as if that wasn't slow enough, we have to go through and re-key *everything*.
		*/

		/*
		** Loop through all safe lists.
		*/
		for (i=0 ; i<NumLists ; i++) {
			ds_assert(Safe[i] != NULL);
			if (Safe[i] != NULL && Safe[i]->EntryCount > 0) {

				/*
				** Get the pointer to the first entry in the list.
				*/
				DataSafeEntryClass *entry_ptr = Safe[i]->SafeList;

				/*
				** Loop through all entries in this list.
				*/
				for (j=0 ; j < Safe[i]->EntryCount ; j++) {

					/*
					** Re-key the entry.
					*/
					entry_ptr->Handle = entry_ptr->Handle ^ mod_key;
					Checksum ^= entry_ptr->Handle;

					void *data_ptr = ((char *)entry_ptr) + sizeof(*entry_ptr);
					Encrypt(data_ptr, Get_Type_Size(Safe[i]->EntryType), mod_key, true);

					/*
					** Next entry.
					*/
					entry_ptr = entry_ptr->Next;
				}
			}
		}

		/*
		** Set the key.
		*/
		SimpleKey ^= new_key;

		/*
		** Reset the delay.
		*/
		ShuffleDelay = TIMEGETTIME();

		/*
		** Check safe integrity.
		*/
		Security_Check();
	}
#endif	//FIXED_KEY
}



/***********************************************************************************************
 * GenericDataSafeClass::Get_Handle_ID -- Get the next available handle ID in the given list.  *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    List number                                                                       *
 *                                                                                             *
 * OUTPUT:   Handle ID                                                                         *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/5/2001 8:56PM ST : Created                                                              *
 *=============================================================================================*/
int GenericDataSafeClass::Get_Handle_ID(int list)
{
	/*
	** Asserts.
	*/
	ds_assert(Safe[list] != NULL);

	/*
	** If the list isn't allocated then we are out of luck.
	*/
	if (Safe[list] != NULL) {

		/*
		** Go through the handle id usage table looking for a free spot.
		*/
		for (int i=0 ; i<MAX_ENTRIES_PER_LIST ; i++) {
			if (Safe[list]->HandleIDUsage[i] == 0) {
				Safe[list]->HandleIDUsage[i] = 1;
				return(i);
			}
		}
	}

	/*
	** Error. No free space or list not allocated.
	*/
	return(0);
}




/***********************************************************************************************
 * GenericDataSafeClass::Free_Handle_ID -- Return a handle ID to the available pool            *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Safe list number                                                                  *
 *           Handle ID                                                                         *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/5/2001 8:59PM ST : Created                                                              *
 *=============================================================================================*/
void GenericDataSafeClass::Free_Handle_ID(int list, int id)
{
	/*
	** Asserts.
	*/
	ds_assert(Safe[list] != NULL);
	ds_assert(id >= 0 && id < MAX_ENTRIES_PER_LIST);

	/*
	** If the list isn't allocated then we are out of luck.
	*/
	if (Safe[list] != NULL) {

		/*
		** It's easy, we just zero out the array element of the ID we no longer need.
		*/
		Safe[list]->HandleIDUsage[id] = 0;
	}
}


/***********************************************************************************************
 * GenericDataSafeClass::Say_Security_Fault -- Security fault display action                   *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   8/8/2001 2:25PM ST : Created                                                              *
 *=============================================================================================*/
void GenericDataSafeClass::Say_Security_Fault(void)
{
	WideStringClass text(TRANSLATION(IDS_DATASAFE_DETECTED_TAMPERING), true);
	if (cNetwork::I_Am_Server()) {
		text += L"Host";
		cScTextObj *event_obj = new cScTextObj;
		event_obj->Init(text, TEXT_MESSAGE_PUBLIC, false, HOST_TEXT_SENDER, -1);
	} else {
		cCsTextObj *message = new cCsTextObj;
		cPlayer *me = cPlayerManager::Find_Player(cNetwork::Get_My_Id());
		if (me) {
			text += me->Get_Name();
		}
		message->Init(text, TEXT_MESSAGE_PUBLIC, cNetwork::Get_My_Id(), -1);
	}
}


#ifdef THREAD_SAFE_DATA_SAFE

/***********************************************************************************************
 * GenericDataSafeClass::Lock -- Get the safe mutex.                                           *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/6/2001 3:02PM ST : Created                                                              *
 *=============================================================================================*/
inline void GenericDataSafeClass::Lock(void)
{
	int deadlock = WaitForSingleObject(GenericDataSafeClass::SafeMutex, 10 * 1000);
	if (deadlock == WAIT_TIMEOUT) {
		WWDEBUG_SAY(("ERROR: Data Safe: Timeout waiting for data safe mutex\n"));
		ds_assert(deadlock != WAIT_TIMEOUT);
	}
}


/***********************************************************************************************
 * GenericDataSafeClass::Unlock -- Release the safe mutex.                                     *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/6/2001 3:03PM ST : Created                                                              *
 *=============================================================================================*/
inline void GenericDataSafeClass::Unlock(void)
{
	ReleaseMutex(GenericDataSafeClass::SafeMutex);
}

#endif //THREAD_SAFE_DATA_SAFE



/***********************************************************************************************
 * GenericDataSafeClass::Print_Safe_Stats_To_Debug_Output -- Print stats with OutputDebugString*
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/16/2001 2:28PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Print_Safe_Stats_To_Debug_Output(void)
{
#ifdef WWDEBUG
	if (cDevOptions::LogDataSafe.Is_True()) {
		char temp[8192];
		Dump_Safe_Stats(temp, 8192);
		WWDEBUG_SAY((temp));
	}
#endif	//WWDEBUG
}


/***********************************************************************************************
 * GenericDataSafeClass::Dump_Safe_Stats - Dump safe stats into a buffer                       *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Ptr to buffer to receive stats                                                    *
 *           Size of buffer                                                                    *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   7/16/2001 2:12PM ST : Created                                                             *
 *=============================================================================================*/
void GenericDataSafeClass::Dump_Safe_Stats(char *dump_buffer, int buffer_size)
{
#ifndef	WWDEBUG
	dump_buffer = dump_buffer;
	buffer_size = buffer_size;
#else		//WWDEBUG

#define UPDATE_PTR									\
 	chars += strlen(dump_ptr);					\
	dump_ptr = dump_buffer + chars;			\
	ds_assert(buffer_size > chars);				\


	/*
	** Asserts.
	*/
	ds_assert(dump_buffer != NULL);
	ds_assert(buffer_size >= 1024);
	ds_assert(!IsBadWritePtr(dump_buffer, buffer_size));

	/*
	** Make a copy of the buffer ptr.
	*/
	int chars = 0;
	char *dump_ptr = dump_buffer;

	/*
	** Precalculate some stats.
	*/
	unsigned long time = TIMEGETTIME() - LastDump;
	LastDump = TIMEGETTIME();
	time = time / 1000;
	int fetches_per_second = 0;
	int swaps_per_second = 0;
	if (time) {
		fetches_per_second = NumFetches / time;
		swaps_per_second = NumSwaps / time;
	}
	NumFetches = 0;
	NumSwaps = 0;

	/*
	** Dump basic datasafe stats into the buffer.
	*/
	sprintf(dump_ptr, "Data Safe Stats\n\n");
	UPDATE_PTR;

	sprintf(dump_ptr, "  Lists              : %d\n", NumLists);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Slop Entries       : %d\n", SlopCount);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Safe Key           : %08X\n", SimpleKey);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Handle Key         : %08X\n", HandleKey);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Checksum           : %08X\n", Checksum);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Known Types        : %d\n", TypeListCount);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Security Checks    : %d\n", NumSecurityChecks);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Shuffles           : %d\n", NumShuffles);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Fetches per second : %d\n", fetches_per_second);
	UPDATE_PTR;
	sprintf(dump_ptr, "  Swaps per second   : %d\n", swaps_per_second);
	UPDATE_PTR;
	sprintf(dump_ptr, "  CRC Errors         : %d\n", CRCErrors);
	UPDATE_PTR;

	/*
	** Count the number of items currently stored in the data safe.
	*/
	int count = 0;
	unsigned long bytes = 0;
	for (int i=0 ; i<NumLists ; i++) {
		ds_assert(Safe[i] != NULL);
		if (Safe[i] != NULL) {
			if (Safe[i]->EntryCount) {
				count += Safe[i]->EntryCount;
				bytes += (sizeof(DataSafeEntryClass) + Get_Type_Size(Safe[i]->EntryType)) * Safe[i]->EntryCount;
			}
		}
	}

	/*
	** Print out the usage stats.
	*/
	sprintf(dump_ptr, "\n  Safe contains %d objects consuming %d bytes\n", count, bytes);
	UPDATE_PTR;

	/*
	** Print out the percentage of safe capacity in use.
	*/
	unsigned long percent = (count * 100) / (MAX_DATASAFE_LISTS * MAX_ENTRIES_PER_LIST);
	sprintf(dump_ptr, "\n  Safe is %d percent full\n\n", percent);
	UPDATE_PTR;

#endif	//WWDEBUG
}