1 /*
2 ** dobjtype.cpp
3 ** Implements the type information class
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2008 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include "dobject.h"
36 #include "i_system.h"
37 #include "actor.h"
38 #include "templates.h"
39 #include "autosegs.h"
40 #include "v_text.h"
41 
42 TArray<PClass *> PClass::m_RuntimeActors;
43 TArray<PClass *> PClass::m_Types;
44 PClass *PClass::TypeHash[PClass::HASH_SIZE];
45 bool PClass::bShutdown;
46 
47 // A harmless non-NULL FlatPointer for classes without pointers.
48 static const size_t TheEnd = ~(size_t)0;
49 
cregcmp(const void * a,const void * b)50 static int STACK_ARGS cregcmp (const void *a, const void *b) NO_SANITIZE
51 {
52 	// VC++ introduces NULLs in the sequence. GCC seems to work as expected and not do it.
53 	const ClassReg *class1 = *(const ClassReg **)a;
54 	const ClassReg *class2 = *(const ClassReg **)b;
55 	if (class1 == NULL) return 1;
56 	if (class2 == NULL) return -1;
57 	return strcmp (class1->Name, class2->Name);
58 }
59 
StaticInit()60 void PClass::StaticInit ()
61 {
62 	atterm (StaticShutdown);
63 
64 	// Sort classes by name to remove dependance on how the compiler ordered them.
65 	REGINFO *head = &CRegHead;
66 	REGINFO *tail = &CRegTail;
67 
68 	// MinGW's linker is linking the object files backwards for me now...
69 	if (head > tail)
70 	{
71 		swapvalues (head, tail);
72 	}
73 	qsort (head + 1, tail - head - 1, sizeof(REGINFO), cregcmp);
74 
75 	FAutoSegIterator probe(CRegHead, CRegTail);
76 
77 	while (*++probe != NULL)
78 	{
79 		((ClassReg *)*probe)->RegisterClass ();
80 	}
81 }
82 
ClearRuntimeData()83 void PClass::ClearRuntimeData ()
84 {
85 	StaticShutdown();
86 
87 	m_RuntimeActors.Clear();
88 	m_Types.Clear();
89 	memset(TypeHash, 0, sizeof(TypeHash));
90 	bShutdown = false;
91 
92 	// Immediately reinitialize the internal classes
93 	FAutoSegIterator probe(CRegHead, CRegTail);
94 
95 	while (*++probe != NULL)
96 	{
97 		((ClassReg *)*probe)->RegisterClass ();
98 	}
99 }
100 
StaticShutdown()101 void PClass::StaticShutdown ()
102 {
103 	TArray<size_t *> uniqueFPs(64);
104 	unsigned int i, j;
105 
106 	for (i = 0; i < PClass::m_Types.Size(); ++i)
107 	{
108 		PClass *type = PClass::m_Types[i];
109 		PClass::m_Types[i] = NULL;
110 		if (type->FlatPointers != &TheEnd && type->FlatPointers != type->Pointers)
111 		{
112 			// FlatPointers are shared by many classes, so we must check for
113 			// duplicates and only delete those that are unique.
114 			for (j = 0; j < uniqueFPs.Size(); ++j)
115 			{
116 				if (type->FlatPointers == uniqueFPs[j])
117 				{
118 					break;
119 				}
120 			}
121 			if (j == uniqueFPs.Size())
122 			{
123 				uniqueFPs.Push(const_cast<size_t *>(type->FlatPointers));
124 			}
125 		}
126 		type->FlatPointers = NULL;
127 
128 		// For runtime classes, this call will also delete the PClass.
129 		PClass::StaticFreeData (type);
130 	}
131 	for (i = 0; i < uniqueFPs.Size(); ++i)
132 	{
133 		delete[] uniqueFPs[i];
134 	}
135 	bShutdown = true;
136 }
137 
StaticFreeData(PClass * type)138 void PClass::StaticFreeData (PClass *type)
139 {
140 	if (type->Defaults != NULL)
141 	{
142 		M_Free(type->Defaults);
143 		type->Defaults = NULL;
144 	}
145 	type->FreeStateList ();
146 
147 	if (type->ActorInfo != NULL)
148 	{
149 		if (type->ActorInfo->OwnedStates != NULL)
150 		{
151 			delete[] type->ActorInfo->OwnedStates;
152 			type->ActorInfo->OwnedStates = NULL;
153 		}
154 		if (type->ActorInfo->DamageFactors != NULL)
155 		{
156 			delete type->ActorInfo->DamageFactors;
157 			type->ActorInfo->DamageFactors = NULL;
158 		}
159 		if (type->ActorInfo->PainChances != NULL)
160 		{
161 			delete type->ActorInfo->PainChances;
162 			type->ActorInfo->PainChances = NULL;
163 		}
164 		if (type->ActorInfo->ColorSets != NULL)
165 		{
166 			delete type->ActorInfo->ColorSets;
167 			type->ActorInfo->ColorSets = NULL;
168 		}
169 		delete type->ActorInfo;
170 		type->ActorInfo = NULL;
171 	}
172 	if (type->bRuntimeClass)
173 	{
174 		delete type;
175 	}
176 	else
177 	{
178 		type->Symbols.ReleaseSymbols();
179 	}
180 }
181 
RegisterClass() const182 void ClassReg::RegisterClass () const
183 {
184 	assert (MyClass != NULL);
185 
186 	// Add type to list
187 	MyClass->ClassIndex = PClass::m_Types.Push (MyClass);
188 
189 	MyClass->TypeName = FName(Name+1);
190 	MyClass->ParentClass = ParentType;
191 	MyClass->Size = SizeOf;
192 	MyClass->Pointers = Pointers;
193 	MyClass->ConstructNative = ConstructNative;
194 	MyClass->InsertIntoHash ();
195 }
196 
InsertIntoHash()197 void PClass::InsertIntoHash ()
198 {
199 	// Add class to hash table. Classes are inserted into each bucket
200 	// in ascending order by name index.
201 	unsigned int bucket = TypeName % HASH_SIZE;
202 	PClass **hashpos = &TypeHash[bucket];
203 	while (*hashpos != NULL)
204 	{
205 		int lexx = int(TypeName) - int((*hashpos)->TypeName);
206 
207 		if (lexx > 0)
208 		{ // This type should come later in the chain
209 			hashpos = &((*hashpos)->HashNext);
210 		}
211 		else if (lexx == 0)
212 		{ // This type has already been inserted
213 		  // ... but there is no need whatsoever to make it a fatal error!
214 			Printf (TEXTCOLOR_RED"Tried to register class '%s' more than once.\n", TypeName.GetChars());
215 			break;
216 		}
217 		else
218 		{ // Type comes right here
219 			break;
220 		}
221 	}
222 	HashNext = *hashpos;
223 	*hashpos = this;
224 }
225 
226 // Find a type, passed the name as a name
FindClass(FName zaname)227 const PClass *PClass::FindClass (FName zaname)
228 {
229 	if (zaname == NAME_None)
230 	{
231 		return NULL;
232 	}
233 
234 	PClass *cls = TypeHash[zaname % HASH_SIZE];
235 
236 	while (cls != 0)
237 	{
238 		int lexx = int(zaname) - int(cls->TypeName);
239 		if (lexx > 0)
240 		{
241 			cls = cls->HashNext;
242 		}
243 		else if (lexx == 0)
244 		{
245 			return cls;
246 		}
247 		else
248 		{
249 			break;
250 		}
251 	}
252 	return NULL;
253 }
254 
255 // Create a new object that this class represents
CreateNew() const256 DObject *PClass::CreateNew () const
257 {
258 	BYTE *mem = (BYTE *)M_Malloc (Size);
259 	assert (mem != NULL);
260 
261 	// Set this object's defaults before constructing it.
262 	if (Defaults != NULL)
263 		memcpy (mem, Defaults, Size);
264 	else
265 		memset (mem, 0, Size);
266 
267 	ConstructNative (mem);
268 	((DObject *)mem)->SetClass (const_cast<PClass *>(this));
269 	return (DObject *)mem;
270 }
271 
272 // Create a new class based on an existing class
CreateDerivedClass(FName name,unsigned int size)273 PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
274 {
275 	assert (size >= Size);
276 	PClass *type;
277 	bool notnew;
278 
279 	const PClass *existclass = FindClass(name);
280 
281 	// This is a placeholder so fill it in
282 	if (existclass != NULL && existclass->Size == (unsigned)-1)
283 	{
284 		type = const_cast<PClass*>(existclass);
285 		if (!IsDescendantOf(type->ParentClass))
286 		{
287 			I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars());
288 		}
289 		DPrintf("Defining placeholder class %s\n", name.GetChars());
290 		notnew = true;
291 	}
292 	else
293 	{
294 		type = new PClass;
295 		notnew = false;
296 	}
297 
298 	type->TypeName = name;
299 	type->ParentClass = this;
300 	type->Size = size;
301 	type->Pointers = NULL;
302 	type->ConstructNative = ConstructNative;
303 	if (!notnew)
304 	{
305 		type->ClassIndex = m_Types.Push (type);
306 	}
307 	type->Meta = Meta;
308 
309 	// Set up default instance of the new class.
310 	type->Defaults = (BYTE *)M_Malloc(size);
311 	memcpy (type->Defaults, Defaults, Size);
312 	if (size > Size)
313 	{
314 		memset (type->Defaults + Size, 0, size - Size);
315 	}
316 
317 	type->FlatPointers = NULL;
318 	type->bRuntimeClass = true;
319 	type->ActorInfo = NULL;
320 	type->Symbols.SetParentTable (&this->Symbols);
321 	if (!notnew) type->InsertIntoHash();
322 
323 	// If this class has an actor info, then any classes derived from it
324 	// also need an actor info.
325 	if (this->ActorInfo != NULL)
326 	{
327 		FActorInfo *info = type->ActorInfo = new FActorInfo;
328 		info->Class = type;
329 		info->GameFilter = GAME_Any;
330 		info->SpawnID = 0;
331 		info->ConversationID = 0;
332 		info->DoomEdNum = -1;
333 		info->OwnedStates = NULL;
334 		info->NumOwnedStates = 0;
335 		info->Replacement = NULL;
336 		info->Replacee = NULL;
337 		info->StateList = NULL;
338 		info->DamageFactors = NULL;
339 		info->PainChances = NULL;
340 		info->PainFlashes = NULL;
341 		info->ColorSets = NULL;
342 		m_RuntimeActors.Push (type);
343 	}
344 	return type;
345 }
346 
347 // Add <extension> bytes to the end of this class. Returns the
348 // previous size of the class.
Extend(unsigned int extension)349 unsigned int PClass::Extend(unsigned int extension)
350 {
351 	assert(this->bRuntimeClass);
352 
353 	unsigned int oldsize = Size;
354 	Size += extension;
355 	Defaults = (BYTE *)M_Realloc(Defaults, Size);
356 	memset(Defaults + oldsize, 0, extension);
357 	return oldsize;
358 }
359 
360 // Like FindClass but creates a placeholder if no class
361 // is found. CreateDerivedClass will automatically fill
362 // in the placeholder when the actual class is defined.
FindClassTentative(FName name)363 const PClass *PClass::FindClassTentative (FName name)
364 {
365 	if (name == NAME_None)
366 	{
367 		return NULL;
368 	}
369 
370 	PClass *cls = TypeHash[name % HASH_SIZE];
371 
372 	while (cls != 0)
373 	{
374 		int lexx = int(name) - int(cls->TypeName);
375 		if (lexx > 0)
376 		{
377 			cls = cls->HashNext;
378 		}
379 		else if (lexx == 0)
380 		{
381 			return cls;
382 		}
383 		else
384 		{
385 			break;
386 		}
387 	}
388 	PClass *type = new PClass;
389 	DPrintf("Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars());
390 
391 	type->TypeName = name;
392 	type->ParentClass = this;
393 	type->Size = -1;
394 	type->Pointers = NULL;
395 	type->ConstructNative = NULL;
396 	type->ClassIndex = m_Types.Push (type);
397 	type->Defaults = NULL;
398 	type->FlatPointers = NULL;
399 	type->bRuntimeClass = true;
400 	type->ActorInfo = NULL;
401 	type->InsertIntoHash();
402 	return type;
403 }
404 
405 // This is used by DECORATE to assign ActorInfos to internal classes
InitializeActorInfo()406 void PClass::InitializeActorInfo ()
407 {
408 	Symbols.SetParentTable (&ParentClass->Symbols);
409 	Defaults = (BYTE *)M_Malloc(Size);
410 	if (ParentClass->Defaults != NULL)
411 	{
412 		memcpy (Defaults, ParentClass->Defaults, ParentClass->Size);
413 		if (Size > ParentClass->Size)
414 		{
415 			memset (Defaults + ParentClass->Size, 0, Size - ParentClass->Size);
416 		}
417 	}
418 	else
419 	{
420 		memset (Defaults, 0, Size);
421 	}
422 
423 	FActorInfo *info = ActorInfo = new FActorInfo;
424 	info->Class = this;
425 	info->GameFilter = GAME_Any;
426 	info->SpawnID = 0;
427 	info->ConversationID = 0;
428 	info->DoomEdNum = -1;
429 	info->OwnedStates = NULL;
430 	info->NumOwnedStates = 0;
431 	info->Replacement = NULL;
432 	info->Replacee = NULL;
433 	info->StateList = NULL;
434 	info->DamageFactors = NULL;
435 	info->PainChances = NULL;
436 	info->PainFlashes = NULL;
437 	info->ColorSets = NULL;
438 	m_RuntimeActors.Push (this);
439 }
440 
441 
442 // Create the FlatPointers array, if it doesn't exist already.
443 // It comprises all the Pointers from superclasses plus this class's own Pointers.
444 // If this class does not define any new Pointers, then FlatPointers will be set
445 // to the same array as the super class's.
BuildFlatPointers()446 void PClass::BuildFlatPointers ()
447 {
448 	if (FlatPointers != NULL)
449 	{ // Already built: Do nothing.
450 		return;
451 	}
452 	else if (ParentClass == NULL)
453 	{ // No parent: FlatPointers is the same as Pointers.
454 		if (Pointers == NULL)
455 		{ // No pointers: Make FlatPointers a harmless non-NULL.
456 			FlatPointers = &TheEnd;
457 		}
458 		else
459 		{
460 			FlatPointers = Pointers;
461 		}
462 	}
463 	else
464 	{
465 		ParentClass->BuildFlatPointers ();
466 		if (Pointers == NULL)
467 		{ // No new pointers: Just use the same FlatPointers as the parent.
468 			FlatPointers = ParentClass->FlatPointers;
469 		}
470 		else
471 		{ // New pointers: Create a new FlatPointers array and add them.
472 			int numPointers, numSuperPointers;
473 
474 			// Count pointers defined by this class.
475 			for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++)
476 			{ }
477 			// Count pointers defined by superclasses.
478 			for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++)
479 			{ }
480 
481 			// Concatenate them into a new array
482 			size_t *flat = new size_t[numPointers + numSuperPointers + 1];
483 			if (numSuperPointers > 0)
484 			{
485 				memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers);
486 			}
487 			memcpy (flat + numSuperPointers, Pointers, sizeof(size_t)*(numPointers+1));
488 			FlatPointers = flat;
489 		}
490 	}
491 }
492 
FreeStateList()493 void PClass::FreeStateList ()
494 {
495 	if (ActorInfo != NULL && ActorInfo->StateList != NULL)
496 	{
497 		ActorInfo->StateList->Destroy();
498 		M_Free (ActorInfo->StateList);
499 		ActorInfo->StateList = NULL;
500 	}
501 }
502 
NativeClass() const503 const PClass *PClass::NativeClass() const
504 {
505 	const PClass *cls = this;
506 
507 	while (cls && cls->bRuntimeClass)
508 		cls = cls->ParentClass;
509 
510 	return cls;
511 }
512 
GetReplacement() const513 PClass *PClass::GetReplacement() const
514 {
515 	return ActorInfo->GetReplacement()->Class;
516 }
517 
518 // Symbol tables ------------------------------------------------------------
519 
~PSymbol()520 PSymbol::~PSymbol()
521 {
522 }
523 
~PSymbolTable()524 PSymbolTable::~PSymbolTable ()
525 {
526 	ReleaseSymbols();
527 }
528 
ReleaseSymbols()529 void PSymbolTable::ReleaseSymbols()
530 {
531 	for (unsigned int i = 0; i < Symbols.Size(); ++i)
532 	{
533 		delete Symbols[i];
534 	}
535 	Symbols.Clear();
536 }
537 
SetParentTable(PSymbolTable * parent)538 void PSymbolTable::SetParentTable (PSymbolTable *parent)
539 {
540 	ParentSymbolTable = parent;
541 }
542 
FindSymbol(FName symname,bool searchparents) const543 PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const
544 {
545 	int min, max;
546 
547 	min = 0;
548 	max = (int)Symbols.Size() - 1;
549 
550 	while (min <= max)
551 	{
552 		unsigned int mid = (min + max) / 2;
553 		PSymbol *sym = Symbols[mid];
554 
555 		if (sym->SymbolName == symname)
556 		{
557 			return sym;
558 		}
559 		else if (sym->SymbolName < symname)
560 		{
561 			min = mid + 1;
562 		}
563 		else
564 		{
565 			max = mid - 1;
566 		}
567 	}
568 	if (searchparents && ParentSymbolTable != NULL)
569 	{
570 		return ParentSymbolTable->FindSymbol (symname, true);
571 	}
572 	return NULL;
573 }
574 
AddSymbol(PSymbol * sym)575 PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
576 {
577 	// Insert it in sorted order.
578 	int min, max, mid;
579 
580 	min = 0;
581 	max = (int)Symbols.Size() - 1;
582 
583 	while (min <= max)
584 	{
585 		mid = (min + max) / 2;
586 		PSymbol *tsym = Symbols[mid];
587 
588 		if (tsym->SymbolName == sym->SymbolName)
589 		{ // A symbol with this name already exists in the table
590 			return NULL;
591 		}
592 		else if (tsym->SymbolName < sym->SymbolName)
593 		{
594 			min = mid + 1;
595 		}
596 		else
597 		{
598 			max = mid - 1;
599 		}
600 	}
601 
602 	// Good. The symbol is not in the table yet.
603 	Symbols.Insert (MAX(min, max), sym);
604 	return sym;
605 }
606