/* ** dobjtype.cpp ** Implements the type information class ** **--------------------------------------------------------------------------- ** Copyright 1998-2008 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "dobject.h" #include "i_system.h" #include "actor.h" #include "templates.h" #include "autosegs.h" #include "v_text.h" TArray PClass::m_RuntimeActors; TArray PClass::m_Types; PClass *PClass::TypeHash[PClass::HASH_SIZE]; bool PClass::bShutdown; // A harmless non-NULL FlatPointer for classes without pointers. static const size_t TheEnd = ~(size_t)0; static int STACK_ARGS cregcmp (const void *a, const void *b) { // VC++ introduces NULLs in the sequence. GCC seems to work as expected and not do it. const ClassReg *class1 = *(const ClassReg **)a; const ClassReg *class2 = *(const ClassReg **)b; if (class1 == NULL) return 1; if (class2 == NULL) return -1; return strcmp (class1->Name, class2->Name); } void PClass::StaticInit () { atterm (StaticShutdown); // Sort classes by name to remove dependance on how the compiler ordered them. REGINFO *head = &CRegHead; REGINFO *tail = &CRegTail; // MinGW's linker is linking the object files backwards for me now... if (head > tail) { swapvalues (head, tail); } qsort (head + 1, tail - head - 1, sizeof(REGINFO), cregcmp); FAutoSegIterator probe(CRegHead, CRegTail); while (*++probe != NULL) { ((ClassReg *)*probe)->RegisterClass (); } } void PClass::StaticShutdown () { TArray uniqueFPs(64); unsigned int i, j; for (i = 0; i < PClass::m_Types.Size(); ++i) { PClass *type = PClass::m_Types[i]; PClass::m_Types[i] = NULL; if (type->FlatPointers != &TheEnd && type->FlatPointers != type->Pointers) { // FlatPointers are shared by many classes, so we must check for // duplicates and only delete those that are unique. for (j = 0; j < uniqueFPs.Size(); ++j) { if (type->FlatPointers == uniqueFPs[j]) { break; } } if (j == uniqueFPs.Size()) { uniqueFPs.Push(const_cast(type->FlatPointers)); } } // For runtime classes, this call will also delete the PClass. PClass::StaticFreeData (type); } for (i = 0; i < uniqueFPs.Size(); ++i) { delete[] uniqueFPs[i]; } bShutdown = true; } void PClass::StaticFreeData (PClass *type) { if (type->Defaults != NULL) { M_Free(type->Defaults); type->Defaults = NULL; } type->FreeStateList (); if (type->ActorInfo != NULL) { if (type->ActorInfo->OwnedStates != NULL) { delete[] type->ActorInfo->OwnedStates; type->ActorInfo->OwnedStates = NULL; } if (type->ActorInfo->DamageFactors != NULL) { delete type->ActorInfo->DamageFactors; type->ActorInfo->DamageFactors = NULL; } if (type->ActorInfo->PainChances != NULL) { delete type->ActorInfo->PainChances; type->ActorInfo->PainChances = NULL; } if (type->ActorInfo->ColorSets != NULL) { delete type->ActorInfo->ColorSets; type->ActorInfo->ColorSets = NULL; } delete type->ActorInfo; type->ActorInfo = NULL; } if (type->bRuntimeClass) { delete type; } else { type->Symbols.ReleaseSymbols(); } } void ClassReg::RegisterClass () const { assert (MyClass != NULL); // Add type to list MyClass->ClassIndex = PClass::m_Types.Push (MyClass); MyClass->TypeName = FName(Name+1); MyClass->ParentClass = ParentType; MyClass->Size = SizeOf; MyClass->Pointers = Pointers; MyClass->ConstructNative = ConstructNative; MyClass->InsertIntoHash (); } void PClass::InsertIntoHash () { // Add class to hash table. Classes are inserted into each bucket // in ascending order by name index. unsigned int bucket = TypeName % HASH_SIZE; PClass **hashpos = &TypeHash[bucket]; while (*hashpos != NULL) { int lexx = int(TypeName) - int((*hashpos)->TypeName); if (lexx > 0) { // This type should come later in the chain hashpos = &((*hashpos)->HashNext); } else if (lexx == 0) { // This type has already been inserted // ... but there is no need whatsoever to make it a fatal error! Printf (TEXTCOLOR_RED"Tried to register class '%s' more than once.\n", TypeName.GetChars()); break; } else { // Type comes right here break; } } HashNext = *hashpos; *hashpos = this; } // Find a type, passed the name as a name const PClass *PClass::FindClass (FName zaname) { if (zaname == NAME_None) { return NULL; } PClass *cls = TypeHash[zaname % HASH_SIZE]; while (cls != 0) { int lexx = int(zaname) - int(cls->TypeName); if (lexx > 0) { cls = cls->HashNext; } else if (lexx == 0) { return cls->Size<0? NULL : cls; } else { break; } } return NULL; } // Create a new object that this class represents DObject *PClass::CreateNew () const { BYTE *mem = (BYTE *)M_Malloc (Size); assert (mem != NULL); // Set this object's defaults before constructing it. if (Defaults != NULL) memcpy (mem, Defaults, Size); else memset (mem, 0, Size); ConstructNative (mem); ((DObject *)mem)->SetClass (const_cast(this)); return (DObject *)mem; } // Create a new class based on an existing class PClass *PClass::CreateDerivedClass (FName name, unsigned int size) { assert (size >= Size); PClass *type; bool notnew; const PClass *existclass = FindClass(name); // This is a placeholder so fill it in if (existclass != NULL && existclass->Size == (unsigned)-1) { type = const_cast(existclass); if (!IsDescendantOf(type->ParentClass)) { I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars()); } DPrintf("Defining placeholder class %s\n", name.GetChars()); notnew = true; } else { type = new PClass; notnew = false; } type->TypeName = name; type->ParentClass = this; type->Size = size; type->Pointers = NULL; type->ConstructNative = ConstructNative; if (!notnew) { type->ClassIndex = m_Types.Push (type); } type->Meta = Meta; // Set up default instance of the new class. type->Defaults = (BYTE *)M_Malloc(size); memcpy (type->Defaults, Defaults, Size); if (size > Size) { memset (type->Defaults + Size, 0, size - Size); } type->FlatPointers = NULL; type->bRuntimeClass = true; type->ActorInfo = NULL; type->Symbols.SetParentTable (&this->Symbols); if (!notnew) type->InsertIntoHash(); // If this class has an actor info, then any classes derived from it // also need an actor info. if (this->ActorInfo != NULL) { FActorInfo *info = type->ActorInfo = new FActorInfo; info->Class = type; info->GameFilter = GAME_Any; info->SpawnID = 0; info->DoomEdNum = -1; info->OwnedStates = NULL; info->NumOwnedStates = 0; info->Replacement = NULL; info->Replacee = NULL; info->StateList = NULL; info->DamageFactors = NULL; info->PainChances = NULL; info->ColorSets = NULL; m_RuntimeActors.Push (type); } return type; } // Add bytes to the end of this class. Returns the // previous size of the class. unsigned int PClass::Extend(unsigned int extension) { assert(this->bRuntimeClass); unsigned int oldsize = Size; Size += extension; Defaults = (BYTE *)M_Realloc(Defaults, Size); memset(Defaults + oldsize, 0, extension); return oldsize; } // Like FindClass but creates a placeholder if no class // is found. CreateDerivedClass will automatcally fill in // the placeholder when the actual class is defined. const PClass *PClass::FindClassTentative (FName name) { if (name == NAME_None) { return NULL; } PClass *cls = TypeHash[name % HASH_SIZE]; while (cls != 0) { int lexx = int(name) - int(cls->TypeName); if (lexx > 0) { cls = cls->HashNext; } else if (lexx == 0) { return cls; } else { break; } } PClass *type = new PClass; DPrintf("Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars()); type->TypeName = name; type->ParentClass = this; type->Size = -1; type->Pointers = NULL; type->ConstructNative = NULL; type->ClassIndex = m_Types.Push (type); type->Defaults = NULL; type->FlatPointers = NULL; type->bRuntimeClass = true; type->ActorInfo = NULL; type->InsertIntoHash(); return type; } // This is used by DECORATE to assign ActorInfos to internal classes void PClass::InitializeActorInfo () { Symbols.SetParentTable (&ParentClass->Symbols); Defaults = (BYTE *)M_Malloc(Size); if (ParentClass->Defaults != NULL) { memcpy (Defaults, ParentClass->Defaults, ParentClass->Size); if (Size > ParentClass->Size) { memset (Defaults + ParentClass->Size, 0, Size - ParentClass->Size); } } else { memset (Defaults, 0, Size); } FActorInfo *info = ActorInfo = new FActorInfo; info->Class = this; info->GameFilter = GAME_Any; info->SpawnID = 0; info->DoomEdNum = -1; info->OwnedStates = NULL; info->NumOwnedStates = 0; info->Replacement = NULL; info->Replacee = NULL; info->StateList = NULL; info->DamageFactors = NULL; info->PainChances = NULL; info->ColorSets = NULL; m_RuntimeActors.Push (this); } // Create the FlatPointers array, if it doesn't exist already. // It comprises all the Pointers from superclasses plus this class's own Pointers. // If this class does not define any new Pointers, then FlatPointers will be set // to the same array as the super class's. void PClass::BuildFlatPointers () { if (FlatPointers != NULL) { // Already built: Do nothing. return; } else if (ParentClass == NULL) { // No parent: FlatPointers is the same as Pointers. if (Pointers == NULL) { // No pointers: Make FlatPointers a harmless non-NULL. FlatPointers = &TheEnd; } else { FlatPointers = Pointers; } } else { ParentClass->BuildFlatPointers (); if (Pointers == NULL) { // No new pointers: Just use the same FlatPointers as the parent. FlatPointers = ParentClass->FlatPointers; } else { // New pointers: Create a new FlatPointers array and add them. int numPointers, numSuperPointers; // Count pointers defined by this class. for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++) { } // Count pointers defined by superclasses. for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++) { } // Concatenate them into a new array size_t *flat = new size_t[numPointers + numSuperPointers + 1]; if (numSuperPointers > 0) { memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers); } memcpy (flat + numSuperPointers, Pointers, sizeof(size_t)*(numPointers+1)); FlatPointers = flat; } } } void PClass::FreeStateList () { if (ActorInfo != NULL && ActorInfo->StateList != NULL) { ActorInfo->StateList->Destroy(); M_Free (ActorInfo->StateList); ActorInfo->StateList = NULL; } } const PClass *PClass::NativeClass() const { const PClass *cls = this; while (cls && cls->bRuntimeClass) cls = cls->ParentClass; return cls; } // Symbol tables ------------------------------------------------------------ PSymbol::~PSymbol() { } PSymbolTable::~PSymbolTable () { ReleaseSymbols(); } void PSymbolTable::ReleaseSymbols() { for (unsigned int i = 0; i < Symbols.Size(); ++i) { delete Symbols[i]; } Symbols.Clear(); } void PSymbolTable::SetParentTable (PSymbolTable *parent) { ParentSymbolTable = parent; } PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const { int min, max; min = 0; max = (int)Symbols.Size() - 1; while (min <= max) { unsigned int mid = (min + max) / 2; PSymbol *sym = Symbols[mid]; if (sym->SymbolName == symname) { return sym; } else if (sym->SymbolName < symname) { min = mid + 1; } else { max = mid - 1; } } if (searchparents && ParentSymbolTable != NULL) { return ParentSymbolTable->FindSymbol (symname, true); } return NULL; } PSymbol *PSymbolTable::AddSymbol (PSymbol *sym) { // Insert it in sorted order. int min, max, mid; min = 0; max = (int)Symbols.Size() - 1; while (min <= max) { mid = (min + max) / 2; PSymbol *tsym = Symbols[mid]; if (tsym->SymbolName == sym->SymbolName) { // A symbol with this name already exists in the table return NULL; } else if (tsym->SymbolName < sym->SymbolName) { min = mid + 1; } else { max = mid - 1; } } // Good. The symbol is not in the table yet. Symbols.Insert (MAX(min, max), sym); return sym; }