Added methods for PTypes to serialize their values

- Values are tagged to allow for some measure of changing variable types
  without automatically breaking savegames.
- Use these new methods to serialize the non-native variables in an
  object. This allows for achiving non-ints.
This commit is contained in:
Randy Heit 2016-04-02 23:10:43 -05:00
parent da496bbe62
commit 85c8218441
6 changed files with 1027 additions and 42 deletions

View file

@ -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<PField>(pair->Value);
if (var != NULL && !(var->Flags & VARF_Native))
{
PType *type = var->Type;
PArray *arraytype = dyn_cast<PArray>(type);
if (arraytype == NULL)
{
count = 1;
}
else
{
count = arraytype->ElementCount;
type = arraytype->ElementType;
}
assert(type == TypeSInt32);
varloc = (int *)(reinterpret_cast<BYTE *>(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)

View file

@ -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<PField *> &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<const PField *>(sym)->Type->ReadValue(ar,
(BYTE *)addr + static_cast<const PField *>(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<PClass *>(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

View file

@ -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<PField *> &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<PClass *>(const_cast<const PClass *>(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

View file

@ -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;

View file

@ -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 ();

View file

@ -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)