From 6d441e25dbb2eab32a55272776d8850c0ac6d68f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 3 Apr 2016 22:31:45 +0200 Subject: [PATCH 01/10] - partially undid the 'repeated flash state' fix, because it didn't work with the stock weapons. I'm still not sure if a compatibility setting is needed. --- src/g_doom/a_doomweaps.cpp | 6 ++++++ src/g_strife/a_strifeweapons.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index b43b5a1d50..b47518f690 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -84,6 +84,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 (); @@ -275,6 +276,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 (); @@ -311,6 +313,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 (); @@ -384,12 +387,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; } } @@ -406,6 +411,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 8129a8bfc7..90a934b82b 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -714,6 +714,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) { From 1011e26eb98393a8b5e582df4e1d58d70012e5ee Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 3 Apr 2016 22:45:54 +0200 Subject: [PATCH 02/10] - fixed: P_RadiusAttack should not call P_DamageMobj for a damage value of 0. This could happen if the damage calculations resulted in a value between 0 and 1, which for the actual check was multiplied with the damage parameter of P_RadiusAttack which inflated the fractional value to something that looked like actual damage but was later truncated. --- src/p_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index aafe2d125c..c6439d9668 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -5363,7 +5363,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo points *= thing->GetClass()->RDFactor / (float)FRACUNIT; // 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; From 0cc2705b99f641012d2622d7f8c36de2692ba390 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 15:36:23 -0500 Subject: [PATCH 03/10] Added A_LogFloat --- src/thingdef/thingdef_codeptr.cpp | 16 ++++++++++++++++ wadsrc/static/actors/actor.txt | 1 + 2 files changed, 17 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 9fa547f461..c09615067a 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2761,6 +2761,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/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 7548cd6c91..aa751e3322 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 From 3357af32e59ef8be735685a941c5c5a44cb5fd7e Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 16:07:51 -0500 Subject: [PATCH 04/10] Generalize FxArrayElement to work with all numeric elements (not just 32-bit ints) --- src/thingdef/thingdef_expression.cpp | 32 ++++++++++++++++++++-------- src/thingdef/thingdef_parse.cpp | 1 - 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 7ecc3c3c47..0312126d45 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2994,10 +2994,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; } @@ -3014,7 +3014,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); @@ -3024,19 +3027,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 (1 << 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 cc2c71e34b..3119305086 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -588,7 +588,6 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cl sym->Offset = cls->Extend(type); if (symt->AddSymbol(sym) == NULL) { - delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; From 806d9d7a953cb8d935656cd41c63e1cb75abcd2f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 16:18:59 -0500 Subject: [PATCH 05/10] Add an AddField() override to PClass that extends the default instance --- src/dobjtype.cpp | 18 ++++++++++++++++++ src/dobjtype.h | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 552b8ee685..028b9e82b1 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2501,6 +2501,24 @@ unsigned int PClass::Extend(unsigned int extension, unsigned int alignment) return padto; } +//========================================================================== +// +// PClass :: AddField +// +//========================================================================== + +PField *PClass::AddField(FName name, PType *type, DWORD flags) +{ + 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 field; +} + //========================================================================== // // PClass :: FindClassTentative diff --git a/src/dobjtype.h b/src/dobjtype.h index e9ff982454..a1fcd13e55 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -585,7 +585,7 @@ 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(); protected: @@ -672,6 +672,7 @@ public: 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(); const PClass *NativeClass() const; From da496bbe62602ccdc7aae006f0409dcd909f558d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 15:48:09 -0500 Subject: [PATCH 06/10] Use AddField() to add user variables in DECORATE --- src/thingdef/thingdef_parse.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 3119305086..f5359a7d85 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -584,9 +584,8 @@ 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) { sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); From 85c82184417c1cb9fe4919d16304fbda67b576cc Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Apr 2016 23:10:43 -0500 Subject: [PATCH 07/10] 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. --- src/dobject.cpp | 45 +-- src/dobjtype.cpp | 894 +++++++++++++++++++++++++++++++++++++++++++++++ src/dobjtype.h | 85 +++++ src/farchive.cpp | 36 +- src/farchive.h | 7 + src/version.h | 2 +- 6 files changed, 1027 insertions(+), 42 deletions(-) diff --git a/src/dobject.cpp b/src/dobject.cpp index efb4370728..d7e23bdb05 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 028b9e82b1..1d781c02c9 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 a1fcd13e55..5ad5757f3e 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 8fd647fd29..1f805ae8ec 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 b646827def..2324c6b2ac 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 17fd288b1d..17e4fa3f14 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) From 15208188de87bbd34c9a648a275d0758f7519eec Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 16:25:08 -0500 Subject: [PATCH 08/10] Remove PClass::Extend() --- src/dobjtype.cpp | 21 --------------------- src/dobjtype.h | 2 -- 2 files changed, 23 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 1d781c02c9..11fd04e82c 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -3374,27 +3374,6 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) return type; } -//========================================================================== -// -// PClass :: Extend -// -// Add bytes to the end of this class and possibly more to meet -// alignment restrictions. Returns the start of the extended block. -// -//========================================================================== - -unsigned int PClass::Extend(unsigned int extension, unsigned int alignment) -{ - assert(this->bRuntimeClass); - - unsigned int oldsize = Size; - unsigned int padto = (oldsize + alignment - 1) & ~(alignment - 1); - Size = padto + extension; - Defaults = (BYTE *)M_Realloc(Defaults, Size); - memset(Defaults + oldsize, 0, Size - oldsize); - return padto; -} - //========================================================================== // // PClass :: AddField diff --git a/src/dobjtype.h b/src/dobjtype.h index 5ad5757f3e..6798045663 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -725,8 +725,6 @@ 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(); From 7c8cff64e682cb6ce39b4b111937a094b9571945 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 17:45:04 -0500 Subject: [PATCH 09/10] Added code to initialize and destroy string variables in classes - Will require being able to add strings to non-native classes to actually test this. --- src/dobject.cpp | 56 ++++++----- src/dobjtype.cpp | 144 +++++++++++++++++++++++++++ src/dobjtype.h | 27 +++++ src/tarray.h | 14 ++- src/thingdef/thingdef_expression.cpp | 2 +- 5 files changed, 212 insertions(+), 31 deletions(-) diff --git a/src/dobject.cpp b/src/dobject.cpp index d7e23bdb05..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); } } diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 11fd04e82c..fbc3ceb730 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -500,6 +500,36 @@ void PType::SkipValue(FArchive &ar, int tag) } } +//========================================================================== +// +// 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 @@ -1497,6 +1527,43 @@ bool PString::ReadValue(FArchive &ar, void *addr) const 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) @@ -2328,6 +2395,20 @@ bool PArray::ReadValue(FArchive &ar, void *addr) const 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 @@ -2581,6 +2662,23 @@ 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 @@ -3287,9 +3385,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 @@ -3388,6 +3527,11 @@ PField *PClass::AddField(FName name, PType *type, DWORD flags) { Defaults = (BYTE *)M_Realloc(Defaults, Size); memset(Defaults + oldsize, 0, Size - oldsize); + // 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 6798045663..fe04bc6343 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 @@ -200,6 +202,22 @@ public: 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); @@ -404,6 +422,9 @@ public: 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 ---------------------------------------------- @@ -577,6 +598,8 @@ public: 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(); }; @@ -637,6 +660,7 @@ public: 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; @@ -697,7 +721,9 @@ 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; @@ -728,6 +754,7 @@ public: 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. diff --git a/src/tarray.h b/src/tarray.h index 3ef4f2d990..bd28d86abc 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_expression.cpp b/src/thingdef/thingdef_expression.cpp index 0312126d45..e1a9e653ef 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3039,7 +3039,7 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { ExpEmit indexv(index->Emit(build)); int shiftbits = 0; - while (1 << shiftbits < arraytype->ElementSize) + while (1u << shiftbits < arraytype->ElementSize) { shiftbits++; } From c90a1c0c9627d2cb4d5ad105ae7020582c8b2cab Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 3 Apr 2016 18:10:09 -0500 Subject: [PATCH 10/10] Add "support" for user string variables in DECORATE - This is "support" in the very most basic sense. You can declare them, but you can't actually do anything with them, since the decorate parser can't handle expressions when it's parsing string arguments. However, they seem to be getting properly initialized and destroyed, which is what this was added to test. If it doesn't look like too much trouble, I might try to turn them into something actually worth something. --- src/thingdef/thingdef_parse.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index f5359a7d85..bf929128b3 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -533,12 +533,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