EM_Task/CoreUObject/Public/UObject/ScriptSerialization.h

539 lines
14 KiB
C
Raw Normal View History

2026-02-13 16:18:33 +08:00
// 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