diff --git a/src/dobject.cpp b/src/dobject.cpp index efb4370728..511f3e5f01 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -257,46 +257,50 @@ DObject::DObject (PClass *inClass) DObject::~DObject () { - if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown) + if (!PClass::bShutdown) { - DObject **probe; PClass *type = GetClass(); - - if (!(ObjectFlags & OF_YesReallyDelete)) + if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown) { - Printf ("Warning: '%s' is freed outside the GC process.\n", - type != NULL ? type->TypeName.GetChars() : "==some object=="); - } + DObject **probe; - // Find all pointers that reference this object and NULL them. - StaticPointerSubstitution(this, NULL); - - // Now unlink this object from the GC list. - for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext)) - { - if (*probe == this) + if (!(ObjectFlags & OF_YesReallyDelete)) { - *probe = ObjNext; - if (&ObjNext == GC::SweepPos) - { - GC::SweepPos = probe; - } - break; + Printf("Warning: '%s' is freed outside the GC process.\n", + type != NULL ? type->TypeName.GetChars() : "==some object=="); } - } - // If it's gray, also unlink it from the gray list. - if (this->IsGray()) - { - for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext)) + // Find all pointers that reference this object and NULL them. + StaticPointerSubstitution(this, NULL); + + // Now unlink this object from the GC list. + for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext)) { if (*probe == this) { - *probe = GCNext; + *probe = ObjNext; + if (&ObjNext == GC::SweepPos) + { + GC::SweepPos = probe; + } break; } } + + // If it's gray, also unlink it from the gray list. + if (this->IsGray()) + { + for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext)) + { + if (*probe == this) + { + *probe = GCNext; + break; + } + } + } } + type->DestroySpecials(this); } } @@ -417,46 +421,15 @@ void DObject::SerializeUserVars(FArchive &arc) if (arc.IsStoring()) { - // Write all user variables. - for (; symt != NULL; symt = symt->ParentSymbolTable) - { - PSymbolTable::MapType::Iterator it(symt->Symbols); - PSymbolTable::MapType::Pair *pair; - - while (it.NextPair(pair)) - { - PField *var = dyn_cast(pair->Value); - if (var != NULL && !(var->Flags & VARF_Native)) - { - PType *type = var->Type; - PArray *arraytype = dyn_cast(type); - if (arraytype == NULL) - { - count = 1; - } - else - { - count = arraytype->ElementCount; - type = arraytype->ElementType; - } - assert(type == TypeSInt32); - varloc = (int *)(reinterpret_cast(this) + var->Offset); - - arc << var->SymbolName; - arc.WriteCount(count); - for (j = 0; j < count; ++j) - { - arc << varloc[j]; - } - } - } - } - // Write terminator. - varname = NAME_None; - arc << varname; + // Write all fields that aren't serialized by native code. + GetClass()->WriteValue(arc, this); + } + else if (SaveVersion >= 4535) + { + GetClass()->ReadValue(arc, this); } else - { + { // Old version that only deals with ints // Read user variables until 'None' is encountered. arc << varname; while (varname != NAME_None) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index fbc85d8602..cacfec84d4 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -39,6 +39,7 @@ #include "dobject.h" #include "i_system.h" +#include "farchive.h" #include "actor.h" #include "templates.h" #include "autosegs.h" @@ -382,6 +383,151 @@ bool PType::VisitedNodeSet::Check(const PType *node) return false; } +//========================================================================== +// +// PType :: WriteValue +// +//========================================================================== + +void PType::WriteValue(FArchive &ar, const void *addr) const +{ + assert(0 && "Cannot write value for this type"); +} + +//========================================================================== +// +// PType :: ReadValue +// +//========================================================================== + +bool PType::ReadValue(FArchive &ar, void *addr) const +{ + assert(0 && "Cannot read value for this type"); + SkipValue(ar); + return false; +} + +//========================================================================== +// +// PType :: SkipValue STATIC +// +//========================================================================== + +void PType::SkipValue(FArchive &ar) +{ + BYTE tag; + ar << tag; + SkipValue(ar, tag); +} + +void PType::SkipValue(FArchive &ar, int tag) +{ + assert(ar.IsLoading() && "SkipValue passed an archive that is writing"); + BYTE buff[8]; + + switch (tag) + { + case VAL_Zero: case VAL_One: + break; + + case VAL_Int8: case VAL_UInt8: + ar.Read(buff, 1); + break; + + case VAL_Int16: case VAL_UInt16: + ar.Read(buff, 2); + break; + + case VAL_Int32: case VAL_UInt32: case VAL_Float32: case VAL_Fixed: case VAL_BAM: + ar.Read(buff, 4); + break; + + case VAL_Int64: case VAL_UInt64: case VAL_Float64: + ar.Read(buff, 8); + break; + + case VAL_Name: + ar.ReadName(); + break; + + case VAL_Object: + { + DObject *skipper; + ar << skipper; + break; + } + case VAL_State: + { + FState *skipper; + ar << skipper; + break; + } + case VAL_String: + { + FString skipper; + ar << skipper; + break; + } + case VAL_Array: + { + DWORD count = ar.ReadCount(); + while (count-- > 0) + { + SkipValue(ar); + } + break; + } + case VAL_Struct: + { + const char *label; + for (label = ar.ReadName(); label != NULL; label = ar.ReadName()) + { + SkipValue(ar); + } + break; + } + case VAL_Class: + { + PClass *type; + for (ar.UserReadClass(type); type != NULL; ar.UserReadClass(type)) + { + SkipValue(ar, VAL_Struct); + } + break; + } + } +} + +//========================================================================== +// +// PType :: SetDefaultValue +// +//========================================================================== + +void PType::SetDefaultValue(void *base, unsigned offset, TArray *stroffs) const +{ +} + +//========================================================================== +// +// PType :: InitializeValue +// +//========================================================================== + +void PType::InitializeValue(void *addr, const void *def) const +{ +} + +//========================================================================== +// +// PType :: DestroyValue +// +//========================================================================== + +void PType::DestroyValue(void *addr) const +{ +} + //========================================================================== // // PType :: SetValue @@ -654,6 +800,177 @@ PInt::PInt(unsigned int size, bool unsign) } } +//========================================================================== +// +// PInt :: WriteValue +// +// Write the value using the minimum byte size needed to represent it. This +// means that the value as written is not necessarily of the same type as +// stored, but the signedness information is preserved. +// +//========================================================================== + +void PInt::WriteValue(FArchive &ar, const void *addr) const +{ + BYTE bval; + + // The process for bytes is the same whether signed or unsigned, since + // they can't be compacted into a representation with fewer bytes. + if (Size == 1) + { + bval = *(BYTE *)addr; + } + else if (Unsigned) + { + unsigned val; + if (Size == 8) + { + QWORD qval = *(QWORD *)addr; + if (qval & 0xFFFFFFFF00000000llu) + { // Value needs 64 bits + ar.WriteByte(VAL_UInt64); + ar.WriteInt64(qval); + return; + } + // Value can fit in 32 bits or less + val = (unsigned)qval; + goto check_u32; + } + else if (Size == 4) + { + val = *(DWORD *)addr; +check_u32: if (val & 0xFFFF0000u) + { // Value needs 32 bits + ar.WriteByte(VAL_UInt32); + ar.WriteInt32(val); + return; + } + // Value can fit in 16 bits or less + goto check_u16; + } + else// if (Size == 2) + { + val = *(WORD *)addr; +check_u16: if (val & 0xFFFFFF00u) + { // Value needs 16 bits + ar.WriteByte(VAL_UInt16); + ar.WriteInt16(val); + return; + } + // Value can fit in 8 bits + bval = (BYTE)val; + } + } + else // Signed + { + int val; + if (Size == 8) + { + SQWORD qval = *(SQWORD *)addr; + INT_MIN; + if (qval < (-0x7FFFFFFF - 1) || qval > 0x7FFFFFFF) + { // Value needs 64 bits + ar.WriteByte(VAL_Int64); + ar.WriteInt64(qval); + return; + } + // Value can fit in 32 bits or less + val = (int)qval; + goto check_s32; + } + else if (Size == 4) + { + val = *(SDWORD *)addr; +check_s32: if (val < -0x8000 || val > 0x7FFF) + { // Value needs 32 bits + ar.WriteByte(VAL_Int32); + ar.WriteInt32(val); + return; + } + // Value can fit in 16 bits or less + goto check_s16; + } + else// if (Size == 2) + { + val = *(SWORD *)addr; +check_s16: if (val < -0x80 || val > 0x7F) + { // Value needs 16 bits + ar.WriteByte(VAL_Int16); + ar.WriteInt16(val); + return; + } + // Value can fit in 8 bits + bval = (BYTE)val; + } + } + // If we get here, the value fits in a byte. Values of 0 and 1 are + // optimized away into the tag so they don't require any extra space + // to store. + if (bval & 0xFE) + { + BYTE out[2] = { Unsigned ? VAL_UInt8 : VAL_Int8, bval }; + ar.Write(out, 2); + } + else + { + ar.WriteByte(VAL_Zero + bval); + } +} + +//========================================================================== +// +// PInt :: ReadValue +// +//========================================================================== + +bool PInt::ReadValue(FArchive &ar, void *addr) const +{ + union + { + QWORD uval; + SQWORD sval; + }; + BYTE tag; + union + { + BYTE val8; + WORD val16; + DWORD val32; + fixed_t fix; + float single; + double dbl; + angle_t ang; + }; + + ar << tag; + switch (tag) + { + case VAL_Zero: uval = 0; break; + case VAL_One: uval = 1; break; + case VAL_Int8: ar << val8; sval = (SBYTE)val8; break; + case VAL_UInt8: ar << val8; uval = val8; break; + case VAL_Int16: ar << val16; sval = (SWORD)val16; break; + case VAL_UInt16: ar << val16; uval = val16; break; + case VAL_Int32: ar << val32; sval = (SDWORD)val32; break; + case VAL_UInt32: ar << val32; uval = val32; break; + case VAL_Int64: ar << sval; break; + case VAL_UInt64: ar << uval; break; + case VAL_Fixed: ar << fix; sval = fix >> FRACBITS; break; // fixed -> int + case VAL_BAM: ar << ang; uval = ang / ANGLE_1; break; // BAM -> degrees + case VAL_Float32: ar << single; sval = (SQWORD)single; break; + case VAL_Float64: ar << dbl; sval = (SQWORD)dbl; break; + default: SkipValue(ar, tag); return false; // Incompatible type + } + switch (Size) + { + case 1: *(BYTE *)addr = (BYTE)uval; break; + case 2: *(WORD *)addr = (WORD)uval; break; + case 4: *(DWORD *)addr = (DWORD)uval; break; + case 8: *(QWORD *)addr = uval; break; + } + return true; +} + //========================================================================== // // PInt :: SetValue @@ -939,6 +1256,97 @@ void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) } } +//========================================================================== +// +// PFloat :: WriteValue +// +//========================================================================== + +void PFloat::WriteValue(FArchive &ar, const void *addr) const +{ + float singleprecision; + if (Size == 8) + { + // If it can be written as single precision without information + // loss, then prefer that over writing a full-sized double. + double doubleprecision = *(double *)addr; + singleprecision = (float)doubleprecision; + if (singleprecision != doubleprecision) + { + ar.WriteByte(VAL_Float64); + ar << doubleprecision; + } + } + else + { + singleprecision = *(float *)addr; + } + ar.WriteByte(VAL_Float32); + ar << singleprecision; +} + +//========================================================================== +// +// PFloat :: ReadValue +// +//========================================================================== + +static bool ReadValueDbl(FArchive &ar, double *addr, unsigned tag) +{ + double val; + union + { + BYTE val8; + WORD val16; + DWORD val32; + QWORD val64; + fixed_t fix; + float single; + angle_t ang; + }; + + switch (tag) + { + case VAL_Zero: val = 0; break; + case VAL_One: val = 1; break; + case VAL_Int8: ar << val8; val = (SBYTE)val8; break; + case VAL_UInt8: ar << val8; val = val8; break; + case VAL_Int16: ar << val16; val = (SWORD)val16; break; + case VAL_UInt16: ar << val16; val = val16; break; + case VAL_Int32: ar << val32; val = (SDWORD)val32; break; + case VAL_UInt32: ar << val32; val = val32; break; + case VAL_Int64: ar << val64; val = (double)(SQWORD)val64; break; + case VAL_UInt64: ar << val64; val = (double)val64; break; + case VAL_Fixed: ar << fix; val = FIXED2DBL(fix); break; + case VAL_BAM: ar << ang; val = ang * (90.0 / ANGLE_90); break; // BAM -> degrees + case VAL_Float32: ar << single; val = single; break; + case VAL_Float64: ar << val; break; + default: PType::SkipValue(ar, tag); return false; // Incompatible type + } + *(double *)addr = val; + return true; +} + +bool PFloat::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + double val; + if (ReadValueDbl(ar, &val, tag)) + { + if (Size == 4) + { + *(float *)addr = (float)val; + } + else + { + *(double *)addr = val; + } + return true; + } + return false; +} + //========================================================================== // // PFloat :: SetValue @@ -1072,6 +1480,82 @@ int PString::GetRegType() const return REGT_STRING; } +//========================================================================== +// +// PString :: WriteValue +// +//========================================================================== + +void PString::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_String); + ar.WriteString(*(const FString *)addr); +} + +//========================================================================== +// +// PString :: ReadValue +// +//========================================================================== + +bool PString::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_String) + { + ar << *(FString *)addr; + } + else if (tag == VAL_Name) + { + const char *str = ar.ReadName(); + *(FString *)addr = str; + } + else + { + SkipValue(ar, tag); + return false; + } + return true; +} + +//========================================================================== +// +// PString :: SetDefaultValue +// +//========================================================================== + +void PString::SetDefaultValue(void *base, unsigned offset, TArray *special) const +{ + new((BYTE *)base + offset) FString; + if (special != NULL) + { + special->Push(std::make_pair(this, offset)); + } +} + +//========================================================================== +// +// PString :: InitializeValue +// +//========================================================================== + +void PString::InitializeValue(void *addr, const void *def) const +{ + new(addr) FString(*(FString *)def); +} + +//========================================================================== +// +// PString :: DestroyValue +// +//========================================================================== + +void PString::DestroyValue(void *addr) const +{ + ((FString *)addr)->~FString(); +} + /* PName ******************************************************************/ IMPLEMENT_CLASS(PName) @@ -1088,6 +1572,46 @@ PName::PName() assert(sizeof(FName) == __alignof(FName)); } +//========================================================================== +// +// PName :: WriteValue +// +//========================================================================== + +void PName::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Name); + ar.WriteName(((const FName *)addr)->GetChars()); +} + +//========================================================================== +// +// PName :: ReadValue +// +//========================================================================== + +bool PName::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Name) + { + *(FName *)addr = FName(ar.ReadName()); + } + else if (tag == VAL_String) + { + FString str; + ar << str; + *(FName *)addr = FName(str); + } + else + { + SkipValue(ar, tag); + return false; + } + return true; +} + /* PSound *****************************************************************/ IMPLEMENT_CLASS(PSound) @@ -1104,6 +1628,48 @@ PSound::PSound() assert(sizeof(FSoundID) == __alignof(FSoundID)); } +//========================================================================== +// +// PSound :: WriteValue +// +//========================================================================== + +void PSound::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Name); + ar.WriteName(*(const FSoundID *)addr); +} + +//========================================================================== +// +// PSound :: ReadValue +// +//========================================================================== + +bool PSound::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + + ar << tag; + if (tag == VAL_Name) + { + const char *str = ar.ReadName(); + *(FSoundID *)addr = FSoundID(str); + } + else if (tag == VAL_String) + { + FString str; + ar << str; + *(FSoundID *)addr = FSoundID(str); + } + else + { + SkipValue(ar, tag); + return false; + } + return true; +} + /* PColor *****************************************************************/ IMPLEMENT_CLASS(PColor) @@ -1168,6 +1734,37 @@ int PStatePointer::GetRegType() const return REGT_POINTER; } +//========================================================================== +// +// PStatePointer :: WriteValue +// +//========================================================================== + +void PStatePointer::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_State); + ar << *(FState **)addr; +} + +//========================================================================== +// +// PStatePointer :: ReadValue +// +//========================================================================== + +bool PStatePointer::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_State) + { + ar << *(FState **)addr; + return true; + } + SkipValue(ar, tag); + return false; +} + /* PPointer ***************************************************************/ IMPLEMENT_POINTY_CLASS(PPointer) @@ -1255,6 +1852,45 @@ void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const id2 = 0; } +//========================================================================== +// +// PPointer :: WriteValue +// +//========================================================================== + +void PPointer::WriteValue(FArchive &ar, const void *addr) const +{ + if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) + { + ar.WriteByte(VAL_Object); + ar << *(DObject **)addr; + } + else + { + assert(0 && "Pointer points to a type we don't handle"); + I_Error("Attempt to save pointer to unhandled type"); + } +} + +//========================================================================== +// +// PPointer :: ReadValue +// +//========================================================================== + +bool PPointer::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Object && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) + { + ar << *(DObject **)addr; + return true; + } + SkipValue(ar, tag); + return false; +} + //========================================================================== // // NewPointer @@ -1460,6 +2096,75 @@ void PArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const id2 = ElementCount; } +//========================================================================== +// +// PArray :: WriteValue +// +//========================================================================== + +void PArray::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Array); + ar.WriteCount(ElementCount); + const BYTE *addrb = (const BYTE *)addr; + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->WriteValue(ar, addrb); + addrb += ElementSize; + } +} + +//========================================================================== +// +// PArray :: ReadValue +// +//========================================================================== + +bool PArray::ReadValue(FArchive &ar, void *addr) const +{ + bool readsomething = false; + BYTE tag; + + ar << tag; + if (tag == VAL_Array) + { + unsigned count = ar.ReadCount(); + unsigned i; + BYTE *addrb = (BYTE *)addr; + for (i = 0; i < MIN(count, ElementCount); ++i) + { + readsomething |= ElementType->ReadValue(ar, addrb); + addrb += ElementSize; + } + if (i < ElementCount) + { + DPrintf("Array on disk (%u) is bigger than in memory (%u)\n", + count, ElementCount); + for (; i < ElementCount; ++i) + { + SkipValue(ar); + } + } + return readsomething; + } + SkipValue(ar, tag); + return false; +} + +//========================================================================== +// +// PArray :: SetDefaultValue +// +//========================================================================== + +void PArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const +{ + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->SetDefaultValue(base, offset + i*ElementSize, special); + } +} + //========================================================================== // // NewArray @@ -1713,6 +2418,112 @@ PStruct::PStruct(FName name, DObject *outer) { } +//========================================================================== +// +// PStruct :: SetDefaultValue +// +//========================================================================== + +void PStruct::SetDefaultValue(void *base, unsigned offset, TArray *special) const +{ + for (const PField *field : Fields) + { + if (!(field->Flags & VARF_Native)) + { + field->Type->SetDefaultValue(base, offset + field->Offset, special); + } + } +} + +//========================================================================== +// +// PStruct :: WriteValue +// +//========================================================================== + +void PStruct::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Struct); + WriteFields(ar, addr, Fields); +} + +//========================================================================== +// +// PStruct :: ReadValue +// +//========================================================================== + +bool PStruct::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Struct) + { + return ReadFields(ar, addr); + } + SkipValue(ar, tag); + return true; +} + +//========================================================================== +// +// PStruct :: WriteFields STATIC +// +//========================================================================== + +void PStruct::WriteFields(FArchive &ar, const void *addr, const TArray &fields) +{ + for (unsigned i = 0; i < fields.Size(); ++i) + { + const PField *field = fields[i]; + // Skip fields with native serialization + if (!(field->Flags & VARF_Native)) + { + ar.WriteName(field->SymbolName); + field->Type->WriteValue(ar, (const BYTE *)addr + field->Offset); + } + } + ar.WriteName(NULL); +} + +//========================================================================== +// +// PStruct :: ReadFields +// +//========================================================================== + +bool PStruct::ReadFields(FArchive &ar, void *addr) const +{ + bool readsomething = false; + const char *label = ar.ReadName(); + if (label == NULL) + { // If there is nothing to restore, we count it as success. + return true; + } + for (; label != NULL; label = ar.ReadName()) + { + const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); + if (sym == NULL) + { + DPrintf("Cannot find field %s in %s\n", + label, TypeName.GetChars()); + SkipValue(ar); + } + else if (!sym->IsKindOf(RUNTIME_CLASS(PField))) + { + DPrintf("Symbol %s in %s is not a field\n", + label, TypeName.GetChars()); + SkipValue(ar); + } + else + { + readsomething |= static_cast(sym)->Type->ReadValue(ar, + (BYTE *)addr + static_cast(sym)->Offset); + } + } + return readsomething; +} + //========================================================================== // // PStruct :: AddField @@ -1925,6 +2736,89 @@ IMPLEMENT_POINTY_CLASS(PClass) DECLARE_POINTER(ParentClass) END_POINTERS +//========================================================================== +// +// PClass :: WriteValue +// +// Similar to PStruct's version, except it also needs to traverse parent +// classes. +// +//========================================================================== + +static void RecurseWriteFields(const PClass *type, FArchive &ar, const void *addr) +{ + if (type != NULL) + { + RecurseWriteFields(type->ParentClass, ar, addr); + // Don't write this part if it has no non-native variables + for (unsigned i = 0; i < type->Fields.Size(); ++i) + { + if (!(type->Fields[i]->Flags & VARF_Native)) + { + // Tag this section with the class it came from in case + // a more-derived class has variables that shadow a less- + // derived class. Whether or not that is a language feature + // that will actually be allowed remains to be seen. + ar.UserWriteClass(const_cast(type)); + PStruct::WriteFields(ar, addr, type->Fields); + break; + } + } + } +} + +void PClass::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Class); + RecurseWriteFields(this, ar, addr); + ar.UserWriteClass(NULL); +} + +//========================================================================== +// +// PClass :: ReadValue +// +//========================================================================== + +bool PClass::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag != VAL_Class) + { + SkipValue(ar, tag); + return false; + } + else + { + bool readsomething = false; + PClass *type; + for (ar.UserReadClass(type); type != NULL; ar.UserReadClass(type)) + { + // Only read it if the type is related to this one. + const PClass *parent; + for (parent = this; parent != NULL; parent = parent->ParentClass) + { + if (parent == type) + { + break; + } + } + if (parent != NULL) + { + readsomething |= type->ReadFields(ar, addr); + } + else + { + DPrintf("Unknown superclass %s of class %s\n", + type->TypeName.GetChars(), TypeName.GetChars()); + SkipValue(ar, VAL_Struct); + } + } + return readsomething; + } +} + //========================================================================== // // cregcmp @@ -2188,6 +3082,26 @@ void PClass::InsertIntoHash () } } +//========================================================================== +// +// PClass :: FindParentClass +// +// Finds a parent class that matches the given name, including itself. +// +//========================================================================== + +const PClass *PClass::FindParentClass(FName name) const +{ + for (const PClass *type = this; type != NULL; type = type->ParentClass) + { + if (type->TypeName == name) + { + return type; + } + } + return NULL; +} + //========================================================================== // // PClass :: FindClass @@ -2227,9 +3141,50 @@ DObject *PClass::CreateNew() const ConstructNative (mem); ((DObject *)mem)->SetClass (const_cast(this)); + if (Defaults != NULL) + { + InitializeSpecials(mem); + } return (DObject *)mem; } +//========================================================================== +// +// PClass :: InitializeSpecials +// +// Initialize special fields of a newly-created instance (e.g. strings). +// +//========================================================================== + +void PClass::InitializeSpecials(void *addr) const +{ + if (ParentClass != NULL) + { + ParentClass->InitializeSpecials(addr); + } + for (auto tao : SpecialInits) + { + tao.first->InitializeValue((BYTE*)addr + tao.second, Defaults + tao.second); + } +} + +//========================================================================== +// +// PClass :: DestroySpecials +// +//========================================================================== + +void PClass::DestroySpecials(void *addr) const +{ + if (ParentClass != NULL) + { + ParentClass->DestroySpecials(addr); + } + for (auto tao : SpecialInits) + { + tao.first->DestroyValue((BYTE *)addr + tao.second); + } +} //========================================================================== // // PClass :: Derive @@ -2316,23 +3271,25 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) //========================================================================== // -// PClass :: Extend -// -// Add bytes to the end of this class and possibly more to meet -// alignment restrictions. Returns the start of the extended block. +// PClass :: AddField // //========================================================================== -unsigned int PClass::Extend(unsigned int extension, unsigned int alignment) +PField *PClass::AddField(FName name, PType *type, DWORD flags) { - assert(this->bRuntimeClass); - - unsigned int oldsize = Size; - unsigned int padto = (oldsize + alignment - 1) & ~(alignment - 1); - Size = padto + extension; + unsigned oldsize = Size; + PField *field = Super::AddField(name, type, flags); + if (field != NULL) + { Defaults = (BYTE *)M_Realloc(Defaults, Size); memset(Defaults + oldsize, 0, Size - oldsize); - return padto; + // If this is a native class, then we must not initialize and + // destroy any of its members. We do, however, initialize the + // default instance since it's not a normal instance of the class. + type->SetDefaultValue(Defaults, field->Offset, + bRuntimeClass ? &SpecialInits : NULL); + } + return field; } //========================================================================== diff --git a/src/dobjtype.h b/src/dobjtype.h index 7258579bb7..3c74817517 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -7,6 +7,8 @@ #include "vm.h" +typedef std::pair FTypeAndOffset; + // Variable/parameter/field flags ------------------------------------------- // Making all these different storage types use a common set of flags seems @@ -186,6 +188,36 @@ public: int FindConversion(PType *target, const Conversion **slots, int numslots); + // Writes the value of a variable of this type at (addr) to an archive, preceded by + // a tag indicating its type. The tag is there so that variable types can be changed + // without completely breaking savegames, provided that the change isn't between + // totally unrelated types. + virtual void WriteValue(FArchive &ar, const void *addr) const; + + // Returns true if the stored value was compatible. False otherwise. + // If the value was incompatible, then the memory at *addr is unchanged. + virtual bool ReadValue(FArchive &ar, void *addr) const; + + // Skips over a value written with WriteValue + static void SkipValue(FArchive &ar); + static void SkipValue(FArchive &ar, int tag); + + // Sets the default value for this type at (base + offset) + // If the default value is binary 0, then this function doesn't need + // to do anything, because PClass::Extend() takes care of that. + // + // The stroffs array is so that types that need special initialization + // and destruction (e.g. strings) can add their offsets to it for special + // initialization when the object is created and destruction when the + // object is destroyed. + virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const; + + // Initialize the value, if needed (e.g. strings) + virtual void InitializeValue(void *addr, const void *def) const; + + // Destroy the value, if needed (e.g. strings) + virtual void DestroyValue(void *addr) const; + // Sets the value of a variable of this type at (addr) virtual void SetValue(void *addr, int val); virtual void SetValue(void *addr, double val); @@ -321,6 +353,9 @@ class PInt : public PBasicType public: PInt(unsigned int size, bool unsign); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void SetValue(void *addr, int val); virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; @@ -347,6 +382,9 @@ class PFloat : public PBasicType public: PFloat(unsigned int size); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void SetValue(void *addr, int val); virtual void SetValue(void *addr, double val); virtual int GetValueInt(void *addr) const; @@ -381,6 +419,12 @@ public: PString(); virtual int GetRegType() const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; }; // Variations of integer types ---------------------------------------------- @@ -390,6 +434,9 @@ class PName : public PInt DECLARE_CLASS(PName, PInt); public: PName(); + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; }; class PSound : public PInt @@ -397,6 +444,9 @@ class PSound : public PInt DECLARE_CLASS(PSound, PInt); public: PSound(); + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; }; class PColor : public PInt @@ -414,6 +464,9 @@ class PStatePointer : public PBasicType public: PStatePointer(); + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual int GetStoreOp() const; virtual int GetLoadOp() const; virtual int GetRegType() const; @@ -434,6 +487,10 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + protected: PPointer(); }; @@ -500,6 +557,12 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + + void SetDefaultValue(void *base, unsigned offset, TArray *special) const override; + protected: PArray(); }; @@ -554,9 +617,16 @@ public: TArray Fields; - PField *AddField(FName name, PType *type, DWORD flags=0); + virtual PField *AddField(FName name, PType *type, DWORD flags=0); size_t PropagateMark(); + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) const override; + + static void WriteFields(FArchive &ar, const void *addr, const TArray &fields); + bool ReadFields(FArchive &ar, void *addr) const; protected: PStruct(); }; @@ -614,11 +684,16 @@ class PClass : public PStruct protected: // We unravel _WITH_META here just as we did for PType. enum { MetaClassNum = CLASSREG_PClassClass }; + TArray SpecialInits; virtual void Derive(PClass *newclass); + void InitializeSpecials(void *addr) const; public: typedef PClassClass MetaClass; MetaClass *GetClass() const; + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; + virtual void DeriveData(PClass *newclass) {} static void StaticInit(); static void StaticShutdown(); @@ -639,10 +714,10 @@ public: void InsertIntoHash(); DObject *CreateNew() const; PClass *CreateDerivedClass(FName name, unsigned int size); - unsigned int Extend(unsigned int extension, unsigned int alignment); - unsigned int Extend(const PType *type) { return Extend(type->Size, type->Align); } + PField *AddField(FName name, PType *type, DWORD flags=0) override; void InitializeActorInfo(); void BuildFlatPointers(); + void DestroySpecials(void *addr) const; const PClass *NativeClass() const; // Returns true if this type is an ancestor of (or same as) the passed type. @@ -662,6 +737,9 @@ public: } // Find a type, given its name. + const PClass *FindParentClass(FName name) const; + PClass *FindParentClass(FName name) { return const_cast(const_cast(this)->FindParentClass(name)); } + static PClass *FindClass(const char *name) { return FindClass(FName(name, true)); } static PClass *FindClass(const FString &name) { return FindClass(FName(name, true)); } static PClass *FindClass(ENamedName name) { return FindClass(FName(name)); } @@ -810,4 +888,31 @@ public: void ReleaseGlobalSymbols(); +// Enumerations for serializing types in an archive ------------------------- + +enum ETypeVal : BYTE +{ + VAL_Int8, + VAL_UInt8, + VAL_Int16, + VAL_UInt16, + VAL_Int32, + VAL_UInt32, + VAL_Int64, + VAL_UInt64, + VAL_Zero, + VAL_One, + VAL_Float32, + VAL_Float64, + VAL_Fixed, + VAL_BAM, + VAL_String, + VAL_Name, + VAL_Struct, + VAL_Array, + VAL_Object, + VAL_State, + VAL_Class, +}; + #endif diff --git a/src/farchive.cpp b/src/farchive.cpp index 93ec269d73..1780f862a6 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -721,6 +721,29 @@ void FArchive::Close () } } +void FArchive::WriteByte(BYTE val) +{ + m_File->Write(&val, 1); +} + +void FArchive::WriteInt16(WORD val) +{ + WORD out = LittleShort(val); + m_File->Write(&out, 2); +} + +void FArchive::WriteInt32(DWORD val) +{ + int out = LittleLong(val); + m_File->Write(&out, 4); +} + +void FArchive::WriteInt64(QWORD val) +{ + long long out = SWAP_QWORD(val); + m_File->Write(&out, 8); +} + void FArchive::WriteCount (DWORD count) { BYTE out; @@ -832,6 +855,14 @@ void FArchive::WriteString (const char *str) } } +void FArchive::WriteString(const FString &str) +{ + // The count includes the '\0' terminator, but we don't + // actually write it out. + WriteCount(str.Len() + 1); + Write(str, str.Len()); +} + FArchive &FArchive::operator<< (char *&str) { if (m_Storing) @@ -868,7 +899,7 @@ FArchive &FArchive::operator<< (FString &str) { if (m_Storing) { - WriteString (str.GetChars()); + WriteString (str); } else { @@ -883,8 +914,7 @@ FArchive &FArchive::operator<< (FString &str) char *str2 = (char *)alloca(size*sizeof(char)); size--; Read (str2, size); - str2[size] = 0; - str = str2; + str = FString(str2, size); } } return *this; diff --git a/src/farchive.h b/src/farchive.h index a3f38ff472..4a17153447 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -166,7 +166,14 @@ public: virtual void Write (const void *mem, unsigned int len); virtual void Read (void *mem, unsigned int len); + void WriteString(const FString &str); void WriteString (const char *str); + + void WriteByte(BYTE val); + void WriteInt16(WORD val); + void WriteInt32(DWORD val); + void WriteInt64(QWORD val); + void WriteCount (DWORD count); DWORD ReadCount (); diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 53f7572239..21119f8a0b 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -82,6 +82,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePistol) return 0; P_SetPsprite (self->player, ps_flash, weapon->FindState(NAME_Flash)); + self->player->psprites[ps_flash].processPending = true; } self->player->mo->PlayAttacking2 (); @@ -274,6 +275,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return 0; P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); + self->player->psprites[ps_flash].processPending = true; } player->mo->PlayAttacking2 (); @@ -310,6 +312,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun2) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 2)) return 0; P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); + self->player->psprites[ps_flash].processPending = true; } player->mo->PlayAttacking2 (); @@ -382,12 +385,14 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i { // we're ok so set the state P_SetPsprite (player, ps_flash, flashstate + index); + player->psprites[ps_flash].processPending = true; return; } else { // oh, no! The state is beyond the end of the state table so use the original flash state. P_SetPsprite (player, ps_flash, flashstate); + player->psprites[ps_flash].processPending = true; return; } } @@ -404,6 +409,7 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i index = 0; } P_SetPsprite (player, ps_flash, flashstate + index); + player->psprites[ps_flash].processPending = true; } // diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index 7fc4a105ff..96d53d4312 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -708,6 +708,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade) return 0; P_SetPsprite (player, ps_flash, flash); + self->player->psprites[ps_flash].processPending = true; if (grenadetype != NULL) { diff --git a/src/p_map.cpp b/src/p_map.cpp index 9700eda4e3..82c55eeaa6 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -5144,7 +5144,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo points *= thing->GetClass()->RDFactor; // points and bombdamage should be the same sign - if (((points * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) + if (((int(points) * bombdamage) > 0) && P_CheckSight(thing, bombspot, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { // OK to damage; target is in direct path double vz; double thrust; diff --git a/src/tarray.h b/src/tarray.h index a0c495523d..4d16ddfe2c 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -94,17 +94,23 @@ public: { return &Array[0]; } + const_iterator begin() const + { + return &Array[0]; + } + const_iterator cbegin() const + { + return &Array[0]; + } iterator end() { return &Array[Count]; } - - const_iterator cbegin() const + const_iterator end() const { - return &Array[0]; + return &Array[Count]; } - const_iterator cend() const { return &Array[Count]; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 3adaf5a99f..09a23d8437 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2745,6 +2745,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) return 0; } +//========================================================================= +// +// A_LogFloat +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogFloat) +{ + PARAM_ACTION_PROLOGUE; + PARAM_FLOAT(num); + IGNORE_FORMAT_PRE + Printf("%H\n", num); + IGNORE_FORMAT_POST + return 0; +} + //=========================================================================== // // A_SetTranslucent diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 441e53371b..1b570c62e3 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2995,10 +2995,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) } ValueType = arraytype->ElementType; - if (ValueType->GetRegType() != REGT_INT) + if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT) { // int arrays only for now - ScriptPosition.Message(MSG_ERROR, "Only integer arrays are supported."); + ScriptPosition.Message(MSG_ERROR, "Only numeric arrays are supported."); delete this; return NULL; } @@ -3015,7 +3015,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { ExpEmit start = Array->Emit(build); - ExpEmit dest(build, REGT_INT); + PArray *const arraytype = static_cast(Array->ValueType); + PType *const elementtype = arraytype->ElementType; + ExpEmit dest(build, elementtype->GetRegType()); + if (start.Konst) { ExpEmit tmpstart(build, REGT_POINTER); @@ -3025,19 +3028,30 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) if (index->isConstant()) { unsigned indexval = static_cast(index)->GetValue().GetInt(); - if (indexval >= static_cast(Array->ValueType)->ElementCount) + if (indexval >= arraytype->ElementCount) { I_Error("Array index out of bounds"); } - indexval <<= 2; - build->Emit(OP_LW, dest.RegNum, start.RegNum, build->GetConstantInt(indexval)); + indexval *= arraytype->ElementSize; + build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, + start.RegNum, build->GetConstantInt(indexval)); } else { ExpEmit indexv(index->Emit(build)); - build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, 2); - build->Emit(OP_BOUND, indexv.RegNum, static_cast(Array->ValueType)->ElementCount); - build->Emit(OP_LW_R, dest.RegNum, start.RegNum, indexv.RegNum); + int shiftbits = 0; + while (1u << shiftbits < arraytype->ElementSize) + { + shiftbits++; + } + assert(1 << shiftbits == arraytype->ElementSize && "Element sizes other than power of 2 are not implemented"); + build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount); + if (shiftbits > 0) + { + build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits); + } + build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that + dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register indexv.Free(build); } start.Free(build); diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index f94bbdf9f9..b2204066d4 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -525,12 +525,17 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl // Read the type and make sure it's acceptable. sc.MustGetAnyToken(); - if (sc.TokenType != TK_Int && sc.TokenType != TK_Float) + switch (sc.TokenType) { - sc.ScriptMessage("User variables must be of type 'int' or 'float'"); + case TK_Int: type = TypeSInt32; break; + case TK_Float: type = TypeFloat64; break; + case TK_String: type = TypeString; break; + default: + type = TypeError; + sc.ScriptMessage("User variables must be of type 'int' or 'float' or 'string'"); FScriptPosition::ErrorCounter++; + break; } - type = sc.TokenType == TK_Int ? (PType *)TypeSInt32 : (PType *)TypeFloat64; sc.MustGetToken(TK_Identifier); // For now, restrict user variables to those that begin with "user_" to guarantee @@ -576,11 +581,9 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl } sc.MustGetToken(';'); - PField *sym = new PField(symname, type, 0); - sym->Offset = cls->Extend(type); - if (symt->AddSymbol(sym) == NULL) + PField *sym = cls->AddField(symname, type, 0); + if (cls == NULL) { - delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index ed5d8ad5d1..2aecefafaa 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -203,6 +203,7 @@ ACTOR Actor native //: Thinker action native A_PrintBold(string whattoprint, float time = 0, name fontname = ""); action native A_Log(string whattoprint); action native A_LogInt(int whattoprint); + action native A_LogFloat(float whattoprint); action native A_SetTranslucent(float alpha, int style = 0); action native A_FadeIn(float reduce = 0.1, int flags = 0); action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true