summaryrefslogtreecommitdiff
path: root/Runtime/Serialize/TransferFunctions/ProxyTransfer.cpp
blob: de4f731288757a5d33523266088853f1f9cb6905 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include "UnityPrefix.h"
#include "ProxyTransfer.h"
#include "Runtime/Utilities/Word.h"
#include "Runtime/Serialize/CacheWrap.h"
#include "Runtime/Serialize/SerializationMetaFlags.h"

using namespace std;

ProxyTransfer::ProxyTransfer (TypeTree& t, int options, void* objectPtr, int objectSize)
	: m_TypeTree (t),
	  m_ActiveFather (NULL)
{
	m_Flags = options;
	
	m_ObjectPtr = (char*)objectPtr;
	m_ObjectSize = objectSize;
	m_Index = 0;
	m_SimulatedByteOffset = 0;
	m_RequireTypelessData = false;
	m_DidErrorAlignment = false;
}

void ProxyTransfer::BeginArrayTransfer (const char* name, const char* typeString, SInt32& size, TransferMetaFlags metaFlag)
{
	AssertIf (m_ActiveFather->m_IsArray);
	BeginTransfer (name, typeString, NULL, metaFlag);
	m_ActiveFather->m_IsArray = true;
	
	// transfer size
	Transfer (size, "size");
}

void ProxyTransfer::BeginTransfer (const char* name, const char* typeString, char* dataPtr, TransferMetaFlags metaFlag)
{
	#if !UNITY_RELEASE
	if (m_RequireTypelessData)
	{
		AssertString ("TransferTypeless needs to be followed by TransferTypelessData with no other variables in between!");
	}
	#endif

	#if DEBUGMODE
	// skip this check for debug mode inspector, as we can have interface names from C# in the debug data.
	if (!(metaFlag & kDebugPropertyMask) && (strstr (name, ".") != NULL || strstr (name, "Array[") != NULL))
	{
		string s = "Illegal serialize property name :";
		GetTypePath (m_ActiveFather, s);
		s += name;
		s += "\n The name may not contain '.' or Array[";
		ErrorString (s);
	}
	#endif
	
	TypeTree* typeTree;
	// Setup a normal typetree child
	if (m_ActiveFather != NULL)
	{
		// Check for multiple occurences of same name
		#if DEBUGMODE
		TypeTree::TypeTreeList::iterator i;
		for (i=m_ActiveFather->m_Children.begin ();i != m_ActiveFather->m_Children.end ();++i)
		{
			if (i->m_Name == name)
			{
				string s = "The same field name is serialized multiple names in the class or it's parent class. This is not supported: ";
				GetTypePath (m_ActiveFather, s);
				s += name;
				ErrorString (s);
			}
		}
		#endif

		m_ActiveFather->m_Children.push_back (TypeTree ());
		// Setup new type
		typeTree = &m_ActiveFather->m_Children.back ();
		typeTree->m_Father = m_ActiveFather;
		typeTree->m_Type = typeString;
		typeTree->m_Name = name;
		typeTree->m_MetaFlag = metaFlag | m_ActiveFather->m_MetaFlag;
		AssertIf(typeTree->m_MetaFlag & kAlignBytesFlag);
		typeTree->m_MetaFlag &= ~(kAnyChildUsesAlignBytesFlag);
		typeTree->m_ByteSize = 0;
	}
	// Setup root TypeTree
	else
	{
		m_TypeTree.m_Father = NULL;
		m_TypeTree.m_Type = typeString;
		m_TypeTree.m_Name = name;
		m_TypeTree.m_MetaFlag = metaFlag;
		m_TypeTree.m_ByteSize = 0;
		typeTree = &m_TypeTree;
	}
	
	// Calculate typetree index	
	if ((typeTree->m_MetaFlag & kDebugPropertyMask) == 0)
		typeTree->m_Index = m_Index++;
	else
	{
		if (m_Flags & kIgnoreDebugPropertiesForIndex)
			typeTree->m_Index = -1;
		else
			typeTree->m_Index = m_Index++;
	}
	
	m_ActiveFather = typeTree;
		
	int offset = dataPtr - m_ObjectPtr;
	if (m_ObjectPtr && dataPtr && offset >= 0 && offset < m_ObjectSize)
		m_ActiveFather->m_ByteOffset = offset;

	m_ActiveFather->m_DirectPtr = dataPtr;
}

void ProxyTransfer::AssertContainsNoPPtr (const TypeTree* typeTree)
{
	AssertIf(typeTree->m_Type.find("PPtr<") == 0);
	for (TypeTree::const_iterator i=typeTree->begin();i != typeTree->end();i++)
		AssertContainsNoPPtr(&*i);
}

void ProxyTransfer::AssertOptimizeTransfer (int sizeofSize)
{
	if (m_ActiveFather->IsBasicDataType())
	{
		AssertIf(sizeofSize != m_ActiveFather->m_ByteSize);
		return;
	}

	int size = 0;
	int baseOffset = m_ActiveFather->m_ByteOffset;
	for (TypeTree::const_iterator i=m_ActiveFather->begin();i != m_ActiveFather->end();i++)
	{
		AssertOptimizeTransferImpl(*i, baseOffset, &size);
	}
	
	// Assert if serialized size is different from sizeof size.
	// - Ignore when serializing for game release. We might be serializing differently in that case. (AnimationCurves)
	AssertIf(sizeofSize != size && (m_Flags & kSerializeGameRelease) == 0);
}

void ProxyTransfer::AssertOptimizeTransferImpl (const TypeTree& typetree, int baseOffset, int* totalSize)
{
	if (typetree.m_ByteOffset != -1)
		AssertIf (typetree.m_ByteOffset - baseOffset != *totalSize);
	
	AssertIf (typetree.m_MetaFlag & kAlignBytesFlag);

	if (typetree.IsBasicDataType())
	{
		*totalSize += typetree.m_ByteSize;
		return;
	}

	for (TypeTree::const_iterator i=typetree.begin();i != typetree.end();i++)
		AssertOptimizeTransferImpl(*i, baseOffset, totalSize);
}

void ProxyTransfer::EndTransfer ()
{
	TypeTree* current = m_ActiveFather;
	// Add bytesize to parent!
	m_ActiveFather = m_ActiveFather->m_Father;
	if (m_ActiveFather)
	{
		if (current->m_ByteSize != -1 && m_ActiveFather->m_ByteSize != -1)
			m_ActiveFather->m_ByteSize += current->m_ByteSize;
		else
			m_ActiveFather->m_ByteSize = -1;

		// Propagate if any child uses alignment up to parents
		if (current->m_MetaFlag & kAnyChildUsesAlignBytesFlag)
		{
			m_ActiveFather->m_MetaFlag |= kAnyChildUsesAlignBytesFlag;
		}

		DebugAssertIf (m_ActiveFather->m_ByteSize == 0 && m_ActiveFather->m_Type == "Generic Mono");
	}
}

void ProxyTransfer::EndArrayTransfer ()
{
	m_ActiveFather->m_ByteSize = -1;
	EndTransfer ();
}

void ProxyTransfer::SetVersion (int version)
{
	// You can not set the version twice on the same type.
	// Probably an inherited class already calls SetVersion
	AssertIf (m_ActiveFather->m_Version != 1);

	m_ActiveFather->m_Version = version;
}

void ProxyTransfer::TransferTypeless (unsigned* byteSize, const char* name, TransferMetaFlags metaFlag)
{
	SInt32 size;
	BeginArrayTransfer (name, "TypelessData", size, metaFlag);
	
	UInt8 temp;
	Transfer (temp, "data", metaFlag);
	
	m_RequireTypelessData = true;
	
	EndArrayTransfer ();
	
	Align ();
}

void ProxyTransfer::TransferTypelessData (unsigned byteSize, void* copyData, int metaData)
{
	m_RequireTypelessData = false;
}

void ProxyTransfer::Align ()
{
	m_SimulatedByteOffset = Align4(m_SimulatedByteOffset);

	if (m_ActiveFather && !m_ActiveFather->m_Children.empty())
	{
		m_ActiveFather->m_Children.back().m_MetaFlag |= kAlignBytesFlag;
		m_ActiveFather->m_MetaFlag |= kAnyChildUsesAlignBytesFlag;
	}
	else
	{
		AssertString("Trying to align type data before anything has been serialized!");
	}
}

#if UNITY_EDITOR
void ProxyTransfer::LogUnalignedTransfer ()
{
	if (m_DidErrorAlignment)
		return;

	// For now we only support 4 byte alignment
	int size = m_ActiveFather->m_ByteSize;
	if (size == 8)
		size = 4;
	if (m_SimulatedByteOffset % size == 0)
		return;
	
	m_DidErrorAlignment = true;
		
	string path;
	GetTypePath(m_ActiveFather, path);
	LogString(Format("Unaligned transfer in '%s' at variable '%s'.\nNext unaligned data path: %s", m_TypeTree.m_Type.c_str(), m_ActiveFather->m_Name.c_str(), path.c_str()));
}
#endif