diff --git a/src/dobject.cpp b/src/dobject.cpp index efb437072..d7e23bdb0 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -417,46 +417,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 028b9e82b..1d781c02c 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" @@ -384,6 +385,121 @@ 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 :: SetValue @@ -662,6 +778,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 @@ -947,6 +1234,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 @@ -1080,6 +1458,45 @@ 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; +} + /* PName ******************************************************************/ IMPLEMENT_CLASS(PName) @@ -1096,6 +1513,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) @@ -1112,6 +1569,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) @@ -1143,6 +1642,45 @@ PFixed::PFixed() { } +//========================================================================== +// +// PFixed :: WriteValue +// +//========================================================================== + +void PFixed::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_Fixed); + ar << *(fixed_t *)addr; +} + +//========================================================================== +// +// PFixed :: ReadValue +// +//========================================================================== + +bool PFixed::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_Fixed) + { + ar << *(fixed_t *)addr; + return true; + } + else + { + double val; + if (ReadValueDbl(ar, &val, tag)) + { + *(fixed_t *)addr = FLOAT2FIXED(val); + return true; + } + return false; + } +} + //========================================================================== // // PFixed :: SetValue @@ -1222,6 +1760,45 @@ PAngle::PAngle() { } +//========================================================================== +// +// PAngle :: WriteValue +// +//========================================================================== + +void PAngle::WriteValue(FArchive &ar, const void *addr) const +{ + ar.WriteByte(VAL_BAM); + ar.WriteInt32(*(angle_t *)addr); +} + +//========================================================================== +// +// PAngle :: ReadValue +// +//========================================================================== + +bool PAngle::ReadValue(FArchive &ar, void *addr) const +{ + BYTE tag; + ar << tag; + if (tag == VAL_BAM) + { + ar << *(angle_t *)addr; + return true; + } + else + { + double val; + if (ReadValueDbl(ar, &val, tag)) + { + *(angle_t *)addr = FLOAT2ANGLE(val); + return true; + } + return false; + } +} + //========================================================================== // // PAngle :: SetValue @@ -1334,6 +1911,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) @@ -1421,6 +2029,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 @@ -1626,6 +2273,61 @@ 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; +} + //========================================================================== // // NewArray @@ -1879,6 +2581,95 @@ PStruct::PStruct(FName name, DObject *outer) { } +//========================================================================== +// +// 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 @@ -2091,6 +2882,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 @@ -2354,6 +3228,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 diff --git a/src/dobjtype.h b/src/dobjtype.h index a1fcd13e5..5ad5757f3 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -186,6 +186,20 @@ 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 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 +335,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 +364,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 +401,9 @@ public: PString(); virtual int GetRegType() const; + + void WriteValue(FArchive &ar, const void *addr) const override; + bool ReadValue(FArchive &ar, void *addr) const override; }; // Variations of integer types ---------------------------------------------- @@ -390,6 +413,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 +423,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 @@ -415,6 +444,9 @@ class PFixed : public PFloat public: PFixed(); + 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; @@ -429,6 +461,9 @@ class PAngle : public PFloat public: PAngle(); + 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; @@ -445,6 +480,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; @@ -465,6 +503,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(); }; @@ -531,6 +573,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: PArray(); }; @@ -588,6 +634,12 @@ public: 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; + + static void WriteFields(FArchive &ar, const void *addr, const TArray &fields); + bool ReadFields(FArchive &ar, void *addr) const; protected: PStruct(); }; @@ -650,6 +702,9 @@ 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(); @@ -694,6 +749,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)); } @@ -844,4 +902,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 8fd647fd2..1f805ae8e 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 b646827de..2324c6b2a 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/version.h b/src/version.h index 17fd288b1..17e4fa3f1 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4534 +#define SAVEVER 4535 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x)