/* ** 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 : WWSaveLoad * * * * $Archive:: /Commando/Code/wwsaveload/definitionmgr.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 3/29/02 4:13p $* * * * $Revision:: 36 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "definitionmgr.h" #include "definition.h" #include "definitionfactory.h" #include "definitionfactorymgr.h" #include "definitionclassids.h" #include "chunkio.h" #include "persistfactory.h" #include "wwdebug.h" #include "wwmemlog.h" #include "twiddler.h" #include #include "wwprofile.h" ////////////////////////////////////////////////////////////////////////////////// // Global instance ////////////////////////////////////////////////////////////////////////////////// DefinitionMgrClass _TheDefinitionMgr; ////////////////////////////////////////////////////////////////////////////////// // Constants ////////////////////////////////////////////////////////////////////////////////// static const int DEFINTION_LIST_GROW_SIZE = 1000; static const uint32 IDRANGE_PER_CLASS = 10000; enum { CHUNKID_VARIABLES = 0x00000100, CHUNKID_OBJECTS, CHUNKID_OBJECT }; enum { VARID_NEXTDEFID = 0x01 }; ////////////////////////////////////////////////////////////////////////////////// // Static member initialization ////////////////////////////////////////////////////////////////////////////////// DefinitionClass ** DefinitionMgrClass::_SortedDefinitionArray = NULL; int DefinitionMgrClass::_DefinitionCount = 0; int DefinitionMgrClass::_MaxDefinitionCount = 0; HashTemplateClass*>* DefinitionMgrClass::DefinitionHash; ////////////////////////////////////////////////////////////////////////////////// // // DefinitionMgrClass // ////////////////////////////////////////////////////////////////////////////////// DefinitionMgrClass::DefinitionMgrClass (void) { return ; } ////////////////////////////////////////////////////////////////////////////////// // // ~DefinitionMgrClass // ////////////////////////////////////////////////////////////////////////////////// DefinitionMgrClass::~DefinitionMgrClass (void) { Free_Definitions (); return ; } ////////////////////////////////////////////////////////////////////////////////// // // Find_Definition // ////////////////////////////////////////////////////////////////////////////////// DefinitionClass * DefinitionMgrClass::Find_Definition (uint32 id, bool twiddle) { DefinitionClass *definition = NULL; int lower_index = 0; int upper_index = _DefinitionCount - 1; int index = upper_index / 2; bool keep_going = (_DefinitionCount > 0); // // Binary search the list until we've found the definition // while (keep_going) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; WWASSERT (curr_def != NULL); // // Is this the definition we are looking for? // if (curr_def->Get_ID () == id) { definition = _SortedDefinitionArray[index]; keep_going = false; } else if (upper_index <= lower_index + 1) { // // When the window get's too small, our divide by two won't catch // both entries, so just go ahead and do them both now. // keep_going = false; if (_SortedDefinitionArray[lower_index]->Get_ID () == id) { definition = _SortedDefinitionArray[lower_index]; } else if (_SortedDefinitionArray[upper_index]->Get_ID () == id) { definition = _SortedDefinitionArray[upper_index]; } } else { // // Cut our 'window' in half // if (id > curr_def->Get_ID ()) { lower_index = index; index += (upper_index - index) / 2; } else { upper_index = index; index -= (index - lower_index) / 2; } } } // // Should we twiddle this definition? (Twiddling refers to our randomizing // framework for definitions) // if ( twiddle && definition != NULL && definition->Get_Class_ID () == CLASSID_TWIDDLERS) { definition = ((TwiddlerClass *)definition)->Twiddle (); } return definition; } ////////////////////////////////////////////////////////////////////////////////// // // Find_Named_Definition // ////////////////////////////////////////////////////////////////////////////////// DefinitionClass * DefinitionMgrClass::Find_Named_Definition (const char *name, bool twiddle) { DefinitionClass *definition = NULL; // // Loop through all the definitions and see if we can // find the one with the requested name // for (int index = 0; index < _DefinitionCount; index ++) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; // // Is this the definition we were looking for? // if (curr_def != NULL && ::stricmp (curr_def->Get_Name (), name) == 0) { definition = curr_def; break; } } // // Should we twiddle this definition? (Twiddling refers to our randomizing // framework for definitions) // if ( twiddle && definition != NULL && definition->Get_Class_ID () == CLASSID_TWIDDLERS) { definition = ((TwiddlerClass *)definition)->Twiddle (); } return definition; } ////////////////////////////////////////////////////////////////////////////////// // // Find_Typed_Definition // ////////////////////////////////////////////////////////////////////////////////// DefinitionClass * DefinitionMgrClass::Find_Typed_Definition (const char *name, uint32 class_id, bool twiddle) { // // Sanity check // if (DefinitionHash == NULL) { WWDEBUG_SAY (("DefinitionMgrClass::Find_Typed_Definition () failed due to a NULL DefinitionHash. %s\n", name)); return NULL; } DefinitionClass *definition = NULL; // Check the hash table first. The hash table is built as we need the definitions, so if definition is not // in the table, it will be added there. // // TSS null deref on this sucker 08/03/01 // WWASSERT(DefinitionHash != NULL); StringClass lower_case_name(name,true); _strlwr(lower_case_name.Peek_Buffer()); DynamicVectorClass* defs = DefinitionHash->Get(lower_case_name); if (defs) { for (int i=0;iLength();++i) { DefinitionClass* curr_def=(*defs)[i]; WWASSERT(curr_def); uint32 curr_class_id = curr_def->Get_Class_ID (); if ( (curr_class_id == class_id) || (::SuperClassID_From_ClassID (curr_class_id) == class_id) || (twiddle && (curr_def->Get_Class_ID () == CLASSID_TWIDDLERS))) { definition = curr_def; break; } } } // // Loop through all the definitions and see if we can // find the one with the requested name // if (!definition) { for (int index = 0; index < _DefinitionCount; index ++) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; if (curr_def != NULL) { // // Is this the correct class of definition? // uint32 curr_class_id = curr_def->Get_Class_ID (); if ( (curr_class_id == class_id) || (::SuperClassID_From_ClassID (curr_class_id) == class_id) || (twiddle && (curr_def->Get_Class_ID () == CLASSID_TWIDDLERS))) { // // Is this the definition we were looking for? // if (::stricmp (curr_def->Get_Name (), name) == 0) { definition = curr_def; // Add the definition to the hash table, so that it can be quickly accessed the next time it is needed. if (!defs) { defs=new DynamicVectorClass; DefinitionHash->Insert(lower_case_name,defs); } defs->Add(definition); break; } } } } } // // Should we twiddle this definition? (Twiddling refers to our randomizing // framework for definitions) // if ( twiddle && definition != NULL && definition->Get_Class_ID () == CLASSID_TWIDDLERS) { definition = ((TwiddlerClass *)definition)->Twiddle (); } return definition; } ////////////////////////////////////////////////////////////////////////////////// // // List_Available_Definitions // ////////////////////////////////////////////////////////////////////////////////// void DefinitionMgrClass::List_Available_Definitions (void) { // // Loop through all the definitions and print the definition name // WWDEBUG_SAY(("Available definitions:\n")); for (int index = 0; index < _DefinitionCount; index ++) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; if (curr_def != NULL) { WWDEBUG_SAY((" >%s<\n", curr_def->Get_Name ())); } } return ; } ////////////////////////////////////////////////////////////////////////////////// // // List_Available_Definitions // ////////////////////////////////////////////////////////////////////////////////// void DefinitionMgrClass::List_Available_Definitions (int superclass_id) { // // Loop through all the definitions and print the definition name // WWDEBUG_SAY(("Available superclass definitions for 0x%8X:\n", superclass_id)); DefinitionClass *definition = NULL; for ( definition = Get_First (superclass_id, DefinitionMgrClass::ID_SUPERCLASS); definition != NULL; definition = Get_Next (definition, superclass_id, DefinitionMgrClass::ID_SUPERCLASS)) { WWDEBUG_SAY((" >%s<\n", definition->Get_Name ())); } return ; } ////////////////////////////////////////////////////////////////////////////////// // // Get_First // ////////////////////////////////////////////////////////////////////////////////// DefinitionClass * DefinitionMgrClass::Get_First (uint32 id, ID_TYPE type) { DefinitionClass *definition = NULL; // // Loop through all the definitions and find the first // one that belongs to the requested class // for ( int index = 0; (definition == NULL) && (index < _DefinitionCount); index ++) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; if (curr_def != NULL) { // // Is this the definition we were looking for? // if ( (type == ID_SUPERCLASS) && (::SuperClassID_From_ClassID (curr_def->Get_Class_ID ()) == id)) { definition = curr_def; } else if ( (type == ID_CLASS) && (curr_def->Get_Class_ID () == id)) { definition = curr_def; } } } return definition; } ////////////////////////////////////////////////////////////////////////////////// // // Get_Next // ////////////////////////////////////////////////////////////////////////////////// DefinitionClass * DefinitionMgrClass::Get_Next ( DefinitionClass * curr_def, uint32 id, ID_TYPE type ) { DefinitionClass *definition = NULL; // // Loop through all the definitions and find the first // one that belongs to the requested class // for ( int index = curr_def->m_DefinitionMgrLink + 1; (definition == NULL) && (index < _DefinitionCount); index ++) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; if (curr_def != NULL) { // // Is this the definition we were looking for? // if ( (type == ID_SUPERCLASS) && (::SuperClassID_From_ClassID (curr_def->Get_Class_ID ()) == id)) { definition = curr_def; } else if ( (type == ID_CLASS) && (curr_def->Get_Class_ID () == id)) { definition = curr_def; } } } return definition; } //////////////////////////////////////////////////////////////////////////// // // Get_Next // //////////////////////////////////////////////////////////////////////////// DefinitionClass * DefinitionMgrClass::Get_Next (DefinitionClass *curr_def) { WWASSERT (curr_def != NULL); DefinitionClass *definition = NULL; int index = curr_def->m_DefinitionMgrLink + 1; if (index < _DefinitionCount) { definition = _SortedDefinitionArray[index]; } return definition; } //////////////////////////////////////////////////////////////////////////// // // Free_Definitions // //////////////////////////////////////////////////////////////////////////// void DefinitionMgrClass::Free_Definitions (void) { // Clear the hash table if (DefinitionHash) { HashTemplateIterator*> ite(*DefinitionHash); for (ite.First();!ite.Is_Done();ite.Next()) { DynamicVectorClass* defs=ite.Peek_Value(); // delete ite.Peek_Value(); delete defs; } DefinitionHash->Remove_All(); delete DefinitionHash; DefinitionHash=NULL; } // // Free each of the definition objects // for (int index = 0; index < _DefinitionCount; index ++) { DefinitionClass *definition = _SortedDefinitionArray[index]; if (definition != NULL) { delete definition; } } // // Free the definition array // if (_SortedDefinitionArray != NULL) { delete [] _SortedDefinitionArray; } _SortedDefinitionArray = NULL; _MaxDefinitionCount = 0; _DefinitionCount = 0; return ; } //////////////////////////////////////////////////////////////////////////// // // Prepare_Definition_Array // //////////////////////////////////////////////////////////////////////////// void DefinitionMgrClass::Prepare_Definition_Array (void) { if (_DefinitionCount + 1 > _MaxDefinitionCount) { // // Allocate a new, bigger array // int new_size = _MaxDefinitionCount + DEFINTION_LIST_GROW_SIZE; DefinitionClass **new_array = new DefinitionClass *[new_size]; // // Copy the entries from the old array to the new array // ::memcpy (new_array, _SortedDefinitionArray, _DefinitionCount * sizeof (DefinitionClass *)); // // Free the old array and start using the new array // if (_SortedDefinitionArray != NULL) { delete [] _SortedDefinitionArray; } _SortedDefinitionArray = new_array; _MaxDefinitionCount = new_size; } if (!DefinitionHash) DefinitionHash=new HashTemplateClass*>; return ; } //////////////////////////////////////////////////////////////////////////// // // Register_Definition // //////////////////////////////////////////////////////////////////////////// void DefinitionMgrClass::Register_Definition (DefinitionClass *definition) { WWASSERT (definition != NULL); if (definition != NULL && definition->m_DefinitionMgrLink == -1 && definition->Get_ID () != 0) { // // Make sure the definition array is large enough // Prepare_Definition_Array (); // // Calculate where in the list we should insert this definition // uint32 id = definition->Get_ID (); int lower_index = 0; int upper_index = _DefinitionCount - 1; int index = upper_index / 2; int insert_index = _DefinitionCount; bool keep_going = (_DefinitionCount > 0); bool is_valid = true; while (keep_going) { DefinitionClass *curr_def = _SortedDefinitionArray[index]; WWASSERT (curr_def != NULL); // // Check to make sure we aren't trying to register a definition // that has the same ID as a definition that is already in the list. // if (curr_def->Get_ID () == id) { insert_index = index; keep_going = false; is_valid = false; } else { // // Cut our 'window' in half // if (id > curr_def->Get_ID ()) { lower_index = index; index += (upper_index - index) / 2; } else { upper_index = index; index -= (index - lower_index) / 2; } // // If we've narrowed down the window to 2 entries, then quick check // the different possibilities. // if (upper_index <= lower_index + 1) { if (_SortedDefinitionArray[upper_index]->Get_ID () <= id) { insert_index = upper_index + 1; } else if (_SortedDefinitionArray[lower_index]->Get_ID () <= id) { insert_index = upper_index; } else { insert_index = lower_index; } keep_going = false; } } } //WWASSERT (is_valid); if (is_valid) { // // Re-index all the definitions that got bumped one cell due to this insertion. // for (index = _DefinitionCount - 1; index >= insert_index; index --) { _SortedDefinitionArray[index + 1] = _SortedDefinitionArray[index]; _SortedDefinitionArray[index + 1]->m_DefinitionMgrLink = index + 1; } // // Insert this definition into the list // definition->m_DefinitionMgrLink = insert_index; _SortedDefinitionArray[insert_index] = definition; _DefinitionCount ++; } } return ; } //////////////////////////////////////////////////////////////////////////// // // Unregister_Definition // //////////////////////////////////////////////////////////////////////////// void DefinitionMgrClass::Unregister_Definition (DefinitionClass *definition) { WWASSERT (definition != 0); //WWASSERT (definition->m_DefinitionMgrLink >= 0 && definition->m_DefinitionMgrLink < _DefinitionCount); if (definition != NULL && definition->m_DefinitionMgrLink != -1) { // // Re-index the definitions that come after this definition in the list // for (int index = definition->m_DefinitionMgrLink; index < _DefinitionCount - 1; index ++) { _SortedDefinitionArray[index] = _SortedDefinitionArray[index + 1]; _SortedDefinitionArray[index]->m_DefinitionMgrLink = index; } _SortedDefinitionArray[_DefinitionCount - 1] = NULL; definition->m_DefinitionMgrLink = -1; _DefinitionCount --; } return ; } ////////////////////////////////////////////////////////////////////////////////// // // Save // ////////////////////////////////////////////////////////////////////////////////// bool DefinitionMgrClass::Save ( ChunkSaveClass & csave ) { WWMEMLOG(MEM_GAMEDATA); bool retval = true; // // Create a chunk to contain the class variables we need to serialize. // csave.Begin_Chunk (CHUNKID_VARIABLES); Save_Variables (csave); csave.End_Chunk (); // // Have the base class write the objects to their own chunk. // csave.Begin_Chunk (CHUNKID_OBJECTS); retval &= Save_Objects (csave); csave.End_Chunk (); return retval; } ////////////////////////////////////////////////////////////////////////////////// // // Load // ////////////////////////////////////////////////////////////////////////////////// bool DefinitionMgrClass::Load (ChunkLoadClass &cload) { WWMEMLOG(MEM_GAMEDATA); bool retval = true; while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { // // If this is the chunk that contains the class variables, then // loop through and read each microchunk // case CHUNKID_VARIABLES: retval &= Load_Variables (cload); break; // // Load all the definition objects from this chunk // case CHUNKID_OBJECTS: retval &= Load_Objects (cload); break; } cload.Close_Chunk (); } return retval; } ////////////////////////////////////////////////////////////////////////////////// // // Save_Objects // ////////////////////////////////////////////////////////////////////////////////// bool DefinitionMgrClass::Save_Objects ( ChunkSaveClass & csave ) { bool retval = true; // // Loop through all the definition objects // for (int index = 0; index < _DefinitionCount; index ++) { DefinitionClass *definition = _SortedDefinitionArray[index]; if (definition != NULL && definition->Is_Save_Enabled ()) { // // Save this definition object // csave.Begin_Chunk (definition->Get_Factory ().Chunk_ID ()); definition->Get_Factory ().Save (csave, definition); csave.End_Chunk (); } } return retval; } ////////////////////////////////////////////////////////////////////////////////// // // Save_Variables // ////////////////////////////////////////////////////////////////////////////////// bool DefinitionMgrClass::Save_Variables (ChunkSaveClass &csave) { bool retval = true; return retval; } float _alloc_time; float _load_time; float _reg_time; ////////////////////////////////////////////////////////////////////////////////// // // Load_Objects // ////////////////////////////////////////////////////////////////////////////////// bool DefinitionMgrClass::Load_Objects (ChunkLoadClass &cload) { bool retval = true; while (cload.Open_Chunk ()) { // // Load this definition from the chunk (if possible) // PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ()); if (factory != NULL) { DefinitionClass *definition = (DefinitionClass *)factory->Load (cload); if (definition != NULL) { // // Add this definition to our array // Prepare_Definition_Array (); _SortedDefinitionArray[_DefinitionCount ++] = definition; } } cload.Close_Chunk (); } // // Sort the definitions // if (_DefinitionCount > 0) { ::qsort (_SortedDefinitionArray, _DefinitionCount, sizeof (DefinitionClass *), fnCompareDefinitionsCallback); } // // Assign a mgr link to each definition // for (int index = 0; index < _DefinitionCount; index ++) { _SortedDefinitionArray[index]->m_DefinitionMgrLink = index; } return retval; } ////////////////////////////////////////////////////////////////////////////////// // // Load_Variables // ////////////////////////////////////////////////////////////////////////////////// bool DefinitionMgrClass::Load_Variables (ChunkLoadClass &cload) { bool retval = true; // // Loop through all the microchunks that define the variables // while (cload.Open_Micro_Chunk ()) { switch (cload.Cur_Micro_Chunk_ID ()) { case VARID_NEXTDEFID: break; } cload.Close_Micro_Chunk (); } return retval; } ///////////////////////////////////////////////////////////////////// // // Get_New_ID // ///////////////////////////////////////////////////////////////////// uint32 DefinitionMgrClass::Get_New_ID (uint32 class_id) { uint32 idrange_start = (class_id - DEF_CLASSID_START) * IDRANGE_PER_CLASS; uint32 idrange_end = (idrange_start + IDRANGE_PER_CLASS); uint32 new_id = idrange_start + 1; // // Try to find the first empty slot in this ID range // for (int index = 0; index < _DefinitionCount; index ++) { DefinitionClass *definition = _SortedDefinitionArray[index]; if (definition != NULL) { // // Get this definition's ID // uint32 curr_id = definition->Get_ID (); // // Is this id in the range we are looking for? // if (curr_id >= idrange_start && curr_id < idrange_end) { bool is_ok = false; if (index < _DefinitionCount - 1) { // // Check to see if the next definition in our array leaves a hole in the // ID range. // DefinitionClass *next_definition = _SortedDefinitionArray[index + 1]; if (next_definition != NULL && next_definition->Get_ID () > (curr_id + 1)) { is_ok = true; } } else { is_ok = true; } // // Return the new ID // if (is_ok) { new_id = curr_id + 1; break; } } } } return new_id; } //////////////////////////////////////////////////////////////// // // fnCompareDefinitionsCallback // //////////////////////////////////////////////////////////////// int __cdecl DefinitionMgrClass::fnCompareDefinitionsCallback ( const void *elem1, const void *elem2 ) { WWASSERT (elem1 != NULL); WWASSERT (elem2 != NULL); DefinitionClass *definition1 = *((DefinitionClass **)elem1); DefinitionClass *definition2 = *((DefinitionClass **)elem2); // // Sort the definitions based on ID // int result = 0; if (definition1->Get_ID () > definition2->Get_ID ()) { result = 1; } else if (definition1->Get_ID () < definition2->Get_ID ()) { result = -1; } else { result = 0; } return result; }