/* ** 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 . */ /*********************************************************************************************** *** 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 #include #include "systimer.h" #include /* ** 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 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::Add_Entry(s1); SentinelTwo = DataSafeClass::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 ; iEntryCount != 0) { found = true; break; } } if (!found) { for (int i=0 ; i= 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 ; iEntryType == 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 ; iEntryCount > 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 ; iEntryCount > 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 ; iHandleIDUsage[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 ; iEntryCount) { 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 }