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