539 lines
14 KiB
C
539 lines
14 KiB
C
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "HAL/Platform.h"
|
|
|
|
// [[ IncludeTool: Inline ]] // Markup to tell IncludeTool that this file is state changing and cannot be optimized out.
|
|
|
|
/**
|
|
* This header contains the code for serialization of script bytecode and [eventually] tagged property values.
|
|
* Extracted to header file to allow custom definitions of the macros used by these methods
|
|
*/
|
|
#ifdef VISUAL_ASSIST_HACK
|
|
EExprToken SerializeExpr(int32&, FArchive&);
|
|
int32 iCode = 0;
|
|
FArchive Ar;
|
|
TArray<uint8> Script;
|
|
EExprToken Expr = (EExprToken)0;
|
|
#endif
|
|
|
|
#ifndef XFER
|
|
#define XFER(T) \
|
|
{ \
|
|
T Temp; \
|
|
if (!Ar.IsLoading()) \
|
|
{ \
|
|
Temp = FPlatformMemory::ReadUnaligned<T>(&Script[iCode]); \
|
|
} \
|
|
Ar << Temp; \
|
|
if (!Ar.IsSaving()) \
|
|
{ \
|
|
FPlatformMemory::WriteUnaligned<T>(&Script[iCode], Temp); \
|
|
} \
|
|
iCode += sizeof(T); \
|
|
}
|
|
#endif
|
|
|
|
// FScriptName
|
|
#ifndef XFERNAME
|
|
#define XFERNAME() \
|
|
{ \
|
|
FName Name; \
|
|
FScriptName ScriptName; \
|
|
if (!Ar.IsLoading()) \
|
|
{ \
|
|
FMemory::Memcpy( &ScriptName, &Script[iCode], sizeof(FScriptName) ); \
|
|
Name = ScriptNameToName(ScriptName); \
|
|
} \
|
|
Ar << Name; \
|
|
if (!Ar.IsSaving()) \
|
|
{ \
|
|
ScriptName = NameToScriptName(Name); \
|
|
FMemory::Memcpy( &Script[iCode], &ScriptName, sizeof(FScriptName) ); \
|
|
} \
|
|
iCode += sizeof(FScriptName); \
|
|
}
|
|
#endif // XFERNAME
|
|
|
|
// ASCII string
|
|
#ifndef XFERSTRING
|
|
#define XFERSTRING() \
|
|
{ \
|
|
do XFER(uint8) while( Script[iCode-1] ); \
|
|
}
|
|
#endif // XFERSTRING
|
|
|
|
// UTF-16 string
|
|
#ifndef XFERUNICODESTRING
|
|
#define XFERUNICODESTRING() \
|
|
{ \
|
|
do XFER(uint16) while( Script[iCode-1] || Script[iCode-2] ); \
|
|
}
|
|
#endif // XFERUNICODESTRING
|
|
|
|
// FText
|
|
#ifndef XFERTEXT
|
|
#define XFERTEXT() \
|
|
{ \
|
|
XFER(uint8); \
|
|
const EBlueprintTextLiteralType TextLiteralType = (EBlueprintTextLiteralType)Script[iCode - 1]; \
|
|
switch (TextLiteralType) \
|
|
{ \
|
|
case EBlueprintTextLiteralType::Empty: \
|
|
break; \
|
|
case EBlueprintTextLiteralType::LocalizedText: \
|
|
SerializeExpr( iCode, Ar ); \
|
|
SerializeExpr( iCode, Ar ); \
|
|
SerializeExpr( iCode, Ar ); \
|
|
break; \
|
|
case EBlueprintTextLiteralType::InvariantText: \
|
|
SerializeExpr( iCode, Ar ); \
|
|
break; \
|
|
case EBlueprintTextLiteralType::LiteralString: \
|
|
SerializeExpr( iCode, Ar ); \
|
|
break; \
|
|
case EBlueprintTextLiteralType::StringTableEntry: \
|
|
XFER_OBJECT_POINTER( UObject* ); \
|
|
FIXUP_EXPR_OBJECT_POINTER( UObject* ); \
|
|
SerializeExpr( iCode, Ar ); \
|
|
SerializeExpr( iCode, Ar ); \
|
|
break; \
|
|
default: \
|
|
checkf(false, TEXT("Unknown EBlueprintTextLiteralType! Please update XFERTEXT to handle this type of text.")); \
|
|
break; \
|
|
} \
|
|
}
|
|
#endif // XFERTEXT
|
|
|
|
#ifndef XFERPTR
|
|
#define XFERPTR(T) \
|
|
{ \
|
|
T AlignedPtr = NULL; \
|
|
ScriptPointerType TempCode; \
|
|
if (!Ar.IsLoading()) \
|
|
{ \
|
|
FMemory::Memcpy( &TempCode, &Script[iCode], sizeof(ScriptPointerType) ); \
|
|
AlignedPtr = (T)(TempCode); \
|
|
} \
|
|
Ar << AlignedPtr; \
|
|
if (!Ar.IsSaving()) \
|
|
{ \
|
|
TempCode = (ScriptPointerType)(AlignedPtr); \
|
|
FMemory::Memcpy( &Script[iCode], &TempCode, sizeof(ScriptPointerType) ); \
|
|
} \
|
|
iCode += sizeof(ScriptPointerType); \
|
|
}
|
|
#endif // XFERPTR
|
|
|
|
#ifndef XFER_FUNC_POINTER
|
|
#define XFER_FUNC_POINTER XFERPTR(UStruct*)
|
|
#endif // XFER_FUNC_POINTER
|
|
|
|
#ifndef XFER_FUNC_NAME
|
|
#define XFER_FUNC_NAME XFERNAME()
|
|
#endif // XFER_FUNC_NAME
|
|
|
|
#ifndef XFER_PROP_POINTER
|
|
#define XFER_PROP_POINTER XFERPTR(FProperty*)
|
|
#endif
|
|
|
|
#ifndef XFER_OBJECT_POINTER
|
|
#define XFER_OBJECT_POINTER(Type) XFERPTR(Type)
|
|
#endif
|
|
|
|
#ifndef FIXUP_EXPR_OBJECT_POINTER
|
|
// sometimes after a UOBject* expression is loaded it may require some post-
|
|
// processing (see: the overridden FIXUP_EXPR_OBJECT_POINTER(), defined in Class.cpp)
|
|
#define FIXUP_EXPR_OBJECT_POINTER(Type)
|
|
#endif
|
|
|
|
/** UStruct::SerializeExpr() */
|
|
#ifdef SERIALIZEEXPR_INC
|
|
EExprToken Expr = (EExprToken)0;
|
|
|
|
// Get expr token.
|
|
XFER(uint8);
|
|
Expr = (EExprToken)Script[iCode - 1];
|
|
|
|
switch (Expr)
|
|
{
|
|
case EX_PrimitiveCast: {
|
|
// A type conversion.
|
|
XFER(uint8); // which kind of conversion
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_ObjToInterfaceCast:
|
|
case EX_CrossInterfaceCast:
|
|
case EX_InterfaceToObjCast: {
|
|
// A conversion from an object or interface variable to a native interface variable.
|
|
// We use a different bytecode to avoid the branching each time we process a cast token.
|
|
|
|
XFER_OBJECT_POINTER(UClass*); // the interface class to convert to
|
|
FIXUP_EXPR_OBJECT_POINTER(UClass*);
|
|
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_Let: {
|
|
XFER_PROP_POINTER;
|
|
}
|
|
case EX_LetObj:
|
|
case EX_LetWeakObjPtr:
|
|
case EX_LetBool:
|
|
case EX_LetDelegate:
|
|
case EX_LetMulticastDelegate: {
|
|
SerializeExpr(iCode, Ar); // Variable expr.
|
|
SerializeExpr(iCode, Ar); // Assignment expr.
|
|
break;
|
|
}
|
|
case EX_LetValueOnPersistentFrame: {
|
|
XFER_PROP_POINTER; // Destination property.
|
|
SerializeExpr(iCode, Ar); // Assignment expr.
|
|
break;
|
|
}
|
|
case EX_StructMemberContext: {
|
|
XFERPTR(FProperty*); // struct member expr.
|
|
SerializeExpr(iCode, Ar); // struct expr.
|
|
break;
|
|
}
|
|
case EX_Jump: {
|
|
XFER(CodeSkipSizeType); // Code offset.
|
|
break;
|
|
}
|
|
case EX_ComputedJump: {
|
|
SerializeExpr(iCode, Ar); // Integer expression, specifying code offset.
|
|
break;
|
|
}
|
|
case EX_LocalVariable:
|
|
case EX_InstanceVariable:
|
|
case EX_DefaultVariable:
|
|
case EX_LocalOutVariable:
|
|
case EX_ClassSparseDataVariable:
|
|
case EX_PropertyConst: {
|
|
XFER_PROP_POINTER;
|
|
break;
|
|
}
|
|
case EX_InterfaceContext: {
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_PushExecutionFlow: {
|
|
XFER(CodeSkipSizeType); // location to push
|
|
break;
|
|
}
|
|
case EX_Nothing:
|
|
case EX_EndOfScript:
|
|
case EX_EndFunctionParms:
|
|
case EX_EndStructConst:
|
|
case EX_EndArray:
|
|
case EX_EndArrayConst:
|
|
case EX_EndSet:
|
|
case EX_EndMap:
|
|
case EX_EndSetConst:
|
|
case EX_EndMapConst:
|
|
case EX_IntZero:
|
|
case EX_IntOne:
|
|
case EX_True:
|
|
case EX_False:
|
|
case EX_NoObject:
|
|
case EX_NoInterface:
|
|
case EX_Self:
|
|
case EX_EndParmValue:
|
|
case EX_PopExecutionFlow:
|
|
case EX_DeprecatedOp4A: {
|
|
break;
|
|
}
|
|
case EX_WireTracepoint:
|
|
case EX_Tracepoint: {
|
|
break;
|
|
}
|
|
case EX_Breakpoint: {
|
|
if (Ar.IsLoading())
|
|
{
|
|
// Turn breakpoints into tracepoints on load
|
|
Script[iCode - 1] = EX_Tracepoint;
|
|
}
|
|
break;
|
|
}
|
|
case EX_InstrumentationEvent: {
|
|
if (Script[iCode] == EScriptInstrumentation::InlineEvent)
|
|
{
|
|
iCode += sizeof(FScriptName);
|
|
}
|
|
iCode += sizeof(uint8);
|
|
break;
|
|
}
|
|
case EX_Return: {
|
|
SerializeExpr(iCode, Ar); // Return expression.
|
|
break;
|
|
}
|
|
case EX_CallMath:
|
|
case EX_LocalFinalFunction:
|
|
case EX_FinalFunction: {
|
|
XFER_FUNC_POINTER; // Stack node.
|
|
FIXUP_EXPR_OBJECT_POINTER(UStruct*);
|
|
while (SerializeExpr(iCode, Ar) != EX_EndFunctionParms)
|
|
; // Parms.
|
|
break;
|
|
}
|
|
case EX_LocalVirtualFunction:
|
|
case EX_VirtualFunction: {
|
|
XFER_FUNC_NAME; // Virtual function name.
|
|
while (SerializeExpr(iCode, Ar) != EX_EndFunctionParms)
|
|
; // Parms.
|
|
break;
|
|
}
|
|
case EX_CallMulticastDelegate: {
|
|
XFER_FUNC_POINTER; // Stack node.
|
|
FIXUP_EXPR_OBJECT_POINTER(UStruct*);
|
|
while (SerializeExpr(iCode, Ar) != EX_EndFunctionParms)
|
|
; // Parms.
|
|
break;
|
|
}
|
|
case EX_ClassContext:
|
|
case EX_Context:
|
|
case EX_Context_FailSilent: {
|
|
SerializeExpr(iCode, Ar); // Object expression.
|
|
XFER(CodeSkipSizeType); // Code offset for NULL expressions.
|
|
XFERPTR(FField*); // Property corresponding to the r-value data, in case the l-value needs to be mem-zero'd
|
|
SerializeExpr(iCode, Ar); // Context expression.
|
|
break;
|
|
}
|
|
case EX_AddMulticastDelegate:
|
|
case EX_RemoveMulticastDelegate: {
|
|
SerializeExpr(iCode, Ar); // Delegate property to assign to
|
|
SerializeExpr(iCode, Ar); // Delegate to add to the MC delegate for broadcast
|
|
break;
|
|
}
|
|
case EX_ClearMulticastDelegate: {
|
|
SerializeExpr(iCode, Ar); // Delegate property to clear
|
|
break;
|
|
}
|
|
case EX_IntConst: {
|
|
XFER(int32);
|
|
break;
|
|
}
|
|
case EX_Int64Const: {
|
|
XFER(int64);
|
|
break;
|
|
}
|
|
case EX_UInt64Const: {
|
|
XFER(uint64);
|
|
break;
|
|
}
|
|
case EX_SkipOffsetConst: {
|
|
XFER(CodeSkipSizeType);
|
|
break;
|
|
}
|
|
case EX_FloatConst: {
|
|
XFER(float);
|
|
break;
|
|
}
|
|
case EX_StringConst: {
|
|
XFERSTRING();
|
|
break;
|
|
}
|
|
case EX_UnicodeStringConst: {
|
|
XFERUNICODESTRING();
|
|
break;
|
|
}
|
|
case EX_TextConst: {
|
|
XFERTEXT();
|
|
break;
|
|
}
|
|
case EX_ObjectConst: {
|
|
XFER_OBJECT_POINTER(UObject*);
|
|
FIXUP_EXPR_OBJECT_POINTER(UObject*);
|
|
|
|
break;
|
|
}
|
|
case EX_SoftObjectConst: {
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_FieldPathConst: {
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_NameConst: {
|
|
XFERNAME();
|
|
break;
|
|
}
|
|
case EX_RotationConst: {
|
|
XFER(int32);
|
|
XFER(int32);
|
|
XFER(int32);
|
|
break;
|
|
}
|
|
case EX_VectorConst: {
|
|
XFER(float);
|
|
XFER(float);
|
|
XFER(float);
|
|
break;
|
|
}
|
|
case EX_TransformConst: {
|
|
// Rotation
|
|
XFER(float);
|
|
XFER(float);
|
|
XFER(float);
|
|
XFER(float);
|
|
// Translation
|
|
XFER(float);
|
|
XFER(float);
|
|
XFER(float);
|
|
// Scale
|
|
XFER(float);
|
|
XFER(float);
|
|
XFER(float);
|
|
break;
|
|
}
|
|
case EX_StructConst: {
|
|
XFERPTR(UScriptStruct*); // Struct.
|
|
XFER(int32); // Serialized struct size
|
|
while (SerializeExpr(iCode, Ar) != EX_EndStructConst)
|
|
;
|
|
break;
|
|
}
|
|
case EX_SetArray: {
|
|
// If not loading, or its a newer version
|
|
if ((!GetLinker()) || !Ar.IsLoading() || (Ar.UE4Ver() >= VER_UE4_CHANGE_SETARRAY_BYTECODE))
|
|
{
|
|
// Array property to assign to
|
|
EExprToken TargetToken = SerializeExpr(iCode, Ar);
|
|
}
|
|
else
|
|
{
|
|
// Array Inner Prop
|
|
XFERPTR(FProperty*);
|
|
}
|
|
|
|
while (SerializeExpr(iCode, Ar) != EX_EndArray)
|
|
;
|
|
break;
|
|
}
|
|
case EX_SetSet:
|
|
SerializeExpr(iCode, Ar); // set property
|
|
XFER(int32); // Number of elements
|
|
while (SerializeExpr(iCode, Ar) != EX_EndSet)
|
|
;
|
|
break;
|
|
case EX_SetMap:
|
|
SerializeExpr(iCode, Ar); // map property
|
|
XFER(int32); // Number of elements
|
|
while (SerializeExpr(iCode, Ar) != EX_EndMap)
|
|
;
|
|
break;
|
|
case EX_ArrayConst: {
|
|
XFERPTR(FProperty*); // Inner property
|
|
XFER(int32); // Number of elements
|
|
while (SerializeExpr(iCode, Ar) != EX_EndArrayConst)
|
|
;
|
|
break;
|
|
}
|
|
case EX_SetConst: {
|
|
XFERPTR(FProperty*); // Inner property
|
|
XFER(int32); // Number of elements
|
|
while (SerializeExpr(iCode, Ar) != EX_EndSetConst)
|
|
;
|
|
break;
|
|
}
|
|
case EX_MapConst: {
|
|
XFERPTR(FProperty*); // Key property
|
|
XFERPTR(FProperty*); // Val property
|
|
XFER(int32); // Number of elements
|
|
while (SerializeExpr(iCode, Ar) != EX_EndMapConst)
|
|
;
|
|
break;
|
|
}
|
|
case EX_ByteConst:
|
|
case EX_IntConstByte: {
|
|
XFER(uint8);
|
|
break;
|
|
}
|
|
case EX_MetaCast: {
|
|
XFER_OBJECT_POINTER(UClass*);
|
|
FIXUP_EXPR_OBJECT_POINTER(UClass*);
|
|
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_DynamicCast: {
|
|
XFER_OBJECT_POINTER(UClass*);
|
|
FIXUP_EXPR_OBJECT_POINTER(UClass*);
|
|
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_JumpIfNot: {
|
|
XFER(CodeSkipSizeType); // Code offset.
|
|
SerializeExpr(iCode, Ar); // Boolean expr.
|
|
break;
|
|
}
|
|
case EX_PopExecutionFlowIfNot: {
|
|
SerializeExpr(iCode, Ar); // Boolean expr.
|
|
break;
|
|
}
|
|
case EX_Assert: {
|
|
XFER(uint16); // Line number.
|
|
XFER(uint8); // debug mode or not
|
|
SerializeExpr(iCode, Ar); // Assert expr.
|
|
break;
|
|
}
|
|
case EX_Skip: {
|
|
XFER(CodeSkipSizeType); // Skip size.
|
|
SerializeExpr(iCode, Ar); // Expression to possibly skip.
|
|
break;
|
|
}
|
|
case EX_InstanceDelegate: {
|
|
XFER_FUNC_NAME; // the name of the function assigned to the delegate.
|
|
break;
|
|
}
|
|
case EX_BindDelegate: {
|
|
XFER_FUNC_NAME;
|
|
SerializeExpr(iCode, Ar); // Delegate property to assign to
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
case EX_SwitchValue: {
|
|
XFER(uint16); // number of cases, without default one
|
|
const uint16 NumCases = FPlatformMemory::ReadUnaligned<uint16>(&Script[iCode - sizeof(uint16)]);
|
|
XFER(CodeSkipSizeType); // Code offset, go to it, when done.
|
|
SerializeExpr(iCode, Ar); // index term
|
|
|
|
for (uint16 CaseIndex = 0; CaseIndex < NumCases; ++CaseIndex)
|
|
{
|
|
SerializeExpr(iCode, Ar); // case index value term
|
|
XFER(CodeSkipSizeType); // offset to the next case
|
|
SerializeExpr(iCode, Ar); // case term
|
|
}
|
|
|
|
SerializeExpr(iCode, Ar); // default term
|
|
break;
|
|
}
|
|
case EX_ArrayGetByRef: {
|
|
SerializeExpr(iCode, Ar);
|
|
SerializeExpr(iCode, Ar);
|
|
break;
|
|
}
|
|
default: {
|
|
// This should never occur.
|
|
UE_LOG(LogScriptSerialization, Warning, TEXT("Error: Unknown bytecode 0x%02X; ignoring it"), (uint8)Expr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif // SERIALIZEEXPR_INC
|
|
|
|
#ifdef SERIALIZEEXPR_AUTO_UNDEF_XFER_MACROS
|
|
#undef XFER
|
|
#undef XFERPTR
|
|
#undef XFERNAME
|
|
#undef XFERSTRING
|
|
#undef XFERUNICODESTRING
|
|
#undef XFERTEXT
|
|
#undef XFER_FUNC_POINTER
|
|
#undef XFER_FUNC_NAME
|
|
#undef XFER_PROP_POINTER
|
|
#undef FIXUP_EXPR_OBJECT_POINTER
|
|
#endif // SERIALIZEEXPR_AUTO_UNDEF_XFER_MACROS
|