1 |
using System; |
2 |
using System.Collections.Generic; |
3 |
using System.IO; |
4 |
|
5 |
namespace Oni.Metadata |
6 |
{ |
7 |
internal class CopyVisitor : MetaTypeVisitor |
8 |
{ |
9 |
#region Private data |
10 |
private BinaryReader input; |
11 |
private BinaryWriter output; |
12 |
private Action<CopyVisitor> callback; |
13 |
private Stack<ActiveField> activeFields; |
14 |
private MetaType topLevelType; |
15 |
private MetaType currentType; |
16 |
private byte[] buffer; |
17 |
private int fieldSize; |
18 |
private int position; |
19 |
#endregion |
20 |
|
21 |
#region private class ActiveField |
22 |
|
23 |
private class ActiveField |
24 |
{ |
25 |
public Field Field; |
26 |
public int Index; |
27 |
|
28 |
public ActiveField(Field field) |
29 |
{ |
30 |
Field = field; |
31 |
Index = -1; |
32 |
} |
33 |
} |
34 |
|
35 |
#endregion |
36 |
|
37 |
public CopyVisitor(BinaryReader reader, BinaryWriter writer, Action<CopyVisitor> callback) |
38 |
{ |
39 |
this.input = reader; |
40 |
this.output = writer; |
41 |
this.callback = callback; |
42 |
this.activeFields = new Stack<ActiveField>(); |
43 |
} |
44 |
|
45 |
public MetaType TopLevelType => topLevelType; |
46 |
|
47 |
public MetaType Type => currentType; |
48 |
|
49 |
public Field Field |
50 |
{ |
51 |
get |
52 |
{ |
53 |
if (activeFields.Count == 0) |
54 |
return null; |
55 |
|
56 |
return activeFields.Peek().Field; |
57 |
} |
58 |
} |
59 |
|
60 |
public int Position => position; |
61 |
|
62 |
public byte GetByte() => buffer[0]; |
63 |
|
64 |
public short GetInt16() => (short)(buffer[0] | (buffer[1] << 8)); |
65 |
|
66 |
public ushort GetUInt16() => (ushort)(buffer[0] | (buffer[1] << 8)); |
67 |
|
68 |
public int GetInt32() => buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); |
69 |
|
70 |
public uint GetUInt32() => (uint)buffer[0] | ((uint)buffer[1] << 8) | ((uint)buffer[2] << 16) | ((uint)buffer[3] << 24); |
71 |
|
72 |
public void SetInt32(int value) |
73 |
{ |
74 |
buffer[0] = (byte)value; |
75 |
buffer[1] = (byte)(value >> 8); |
76 |
buffer[2] = (byte)(value >> 16); |
77 |
buffer[3] = (byte)(value >> 24); |
78 |
} |
79 |
|
80 |
public string GetCurrentFieldName() |
81 |
{ |
82 |
if (activeFields.Count == 0) |
83 |
return null; |
84 |
|
85 |
List<string> names = new List<string>(); |
86 |
|
87 |
foreach (ActiveField state in activeFields) |
88 |
{ |
89 |
if (string.IsNullOrEmpty(state.Field.Name)) |
90 |
return null; |
91 |
|
92 |
if (state.Index >= 0) |
93 |
names.Add(string.Format("{0}[{1}]", state.Field.Name, state.Index)); |
94 |
else |
95 |
names.Add(state.Field.Name); |
96 |
} |
97 |
|
98 |
names.Add(topLevelType.Name); |
99 |
names.Reverse(); |
100 |
|
101 |
return string.Join(".", names.ToArray()); |
102 |
} |
103 |
|
104 |
public string GetParentFieldName() |
105 |
{ |
106 |
if (activeFields.Count == 0) |
107 |
return null; |
108 |
|
109 |
List<string> names = new List<string>(); |
110 |
|
111 |
foreach (ActiveField state in activeFields) |
112 |
{ |
113 |
if (string.IsNullOrEmpty(state.Field.Name)) |
114 |
return null; |
115 |
|
116 |
if (state.Index >= 0) |
117 |
names.Add(string.Format("{0}[{1}]", state.Field.Name, state.Index)); |
118 |
else |
119 |
names.Add(state.Field.Name); |
120 |
} |
121 |
|
122 |
names.Add(topLevelType.Name); |
123 |
names.Reverse(); |
124 |
names.RemoveAt(names.Count - 1); |
125 |
|
126 |
return string.Join(".", names.ToArray()); |
127 |
} |
128 |
|
129 |
public override void VisitByte(MetaByte type) => CopyBytes(type); |
130 |
public override void VisitInt16(MetaInt16 type) => CopyBytes(type); |
131 |
public override void VisitUInt16(MetaUInt16 type) => CopyBytes(type); |
132 |
public override void VisitInt32(MetaInt32 type) => CopyBytes(type); |
133 |
public override void VisitUInt32(MetaUInt32 type) => CopyBytes(type); |
134 |
public override void VisitInt64(MetaInt64 type) => CopyBytes(type); |
135 |
public override void VisitUInt64(MetaUInt64 type) => CopyBytes(type); |
136 |
public override void VisitFloat(MetaFloat type) => CopyBytes(type); |
137 |
public override void VisitColor(MetaColor type) => CopyBytes(type); |
138 |
public override void VisitRawOffset(MetaRawOffset type) => CopyBytes(type); |
139 |
public override void VisitSepOffset(MetaSepOffset type) => CopyBytes(type); |
140 |
public override void VisitPointer(MetaPointer type) => CopyBytes(type); |
141 |
public override void VisitBoundingBox(MetaBoundingBox type) => CopyBytes(type); |
142 |
public override void VisitBoundingSphere(MetaBoundingSphere type) => CopyBytes(type); |
143 |
public override void VisitMatrix4x3(MetaMatrix4x3 type) => CopyBytes(type); |
144 |
public override void VisitPlane(MetaPlane type) => CopyBytes(type); |
145 |
public override void VisitQuaternion(MetaQuaternion type) => CopyBytes(type); |
146 |
public override void VisitVector2(MetaVector2 type) => CopyBytes(type); |
147 |
public override void VisitVector3(MetaVector3 type) => CopyBytes(type); |
148 |
|
149 |
private void CopyBytes(MetaType type) |
150 |
{ |
151 |
BeginCopy(type, 1); |
152 |
EndCopy(); |
153 |
} |
154 |
|
155 |
public override void VisitString(MetaString type) |
156 |
{ |
157 |
BeginCopy(type, 1); |
158 |
|
159 |
bool zeroFound = false; |
160 |
|
161 |
for (int i = 0; i < type.Size; i++) |
162 |
{ |
163 |
if (zeroFound) |
164 |
buffer[i] = 0; |
165 |
else if (buffer[i] == 0) |
166 |
zeroFound = true; |
167 |
} |
168 |
|
169 |
EndCopy(); |
170 |
} |
171 |
|
172 |
public override void VisitPadding(MetaPadding type) |
173 |
{ |
174 |
BeginCopy(type, 1); |
175 |
|
176 |
if (type.FillByte == 0) |
177 |
{ |
178 |
Array.Clear(buffer, 0, type.Size); |
179 |
} |
180 |
else |
181 |
{ |
182 |
for (int i = 0; i < type.Size; i++) |
183 |
buffer[i] = type.FillByte; |
184 |
} |
185 |
|
186 |
EndCopy(); |
187 |
} |
188 |
|
189 |
public override void VisitStruct(MetaStruct type) |
190 |
{ |
191 |
if (topLevelType == null) |
192 |
topLevelType = type; |
193 |
|
194 |
foreach (Field field in type.Fields) |
195 |
{ |
196 |
BeginCopyField(field); |
197 |
field.Type.Accept(this); |
198 |
EndCopyField(field); |
199 |
} |
200 |
} |
201 |
|
202 |
internal void BeginCopyField(Field field) |
203 |
{ |
204 |
activeFields.Push(new ActiveField(field)); |
205 |
} |
206 |
|
207 |
internal void EndCopyField(Field field) |
208 |
{ |
209 |
if (activeFields.Peek().Field != field) |
210 |
throw new InvalidOperationException(); |
211 |
|
212 |
activeFields.Pop(); |
213 |
} |
214 |
|
215 |
public override void VisitArray(MetaArray type) |
216 |
{ |
217 |
CopyArray(type.ElementType, type.Count); |
218 |
} |
219 |
|
220 |
public override void VisitVarArray(MetaVarArray type) |
221 |
{ |
222 |
BeginCopyField(type.CountField); |
223 |
type.CountField.Type.Accept(this); |
224 |
EndCopyField(type.CountField); |
225 |
|
226 |
int length; |
227 |
|
228 |
if (type.CountField.Type == MetaType.Int16) |
229 |
length = GetInt16(); |
230 |
else |
231 |
length = GetInt32(); |
232 |
|
233 |
if (length < 0) |
234 |
throw new InvalidDataException(string.Format("Invalid array length: 0x{0:x} at offset 0x{1:x}", length, position)); |
235 |
|
236 |
CopyArray(type.ElementType, length); |
237 |
} |
238 |
|
239 |
private void CopyArray(MetaType elementType, int count) |
240 |
{ |
241 |
if (elementType.IsBlittable) |
242 |
{ |
243 |
BeginCopy(elementType, count); |
244 |
EndCopy(); |
245 |
return; |
246 |
} |
247 |
|
248 |
for (int i = 0; i < count; i++) |
249 |
{ |
250 |
BeginCopyArrayElement(i); |
251 |
elementType.Accept(this); |
252 |
EndCopyArrayElement(i); |
253 |
} |
254 |
} |
255 |
|
256 |
private void BeginCopyArrayElement(int index) |
257 |
{ |
258 |
if (activeFields.Count == 0) |
259 |
return; |
260 |
|
261 |
activeFields.Peek().Index = index; |
262 |
} |
263 |
|
264 |
private void EndCopyArrayElement(int index) |
265 |
{ |
266 |
if (activeFields.Count == 0) |
267 |
return; |
268 |
|
269 |
ActiveField field = activeFields.Peek(); |
270 |
|
271 |
if (field.Index != index) |
272 |
throw new InvalidOperationException(); |
273 |
|
274 |
field.Index = -1; |
275 |
} |
276 |
|
277 |
private void BeginCopy(MetaType type, int count) |
278 |
{ |
279 |
currentType = type; |
280 |
|
281 |
fieldSize = type.Size * count; |
282 |
|
283 |
if (buffer == null || buffer.Length < fieldSize) |
284 |
buffer = new byte[fieldSize * 2]; |
285 |
|
286 |
input.Read(buffer, 0, fieldSize); |
287 |
} |
288 |
|
289 |
private void EndCopy() |
290 |
{ |
291 |
if (callback != null) |
292 |
callback(this); |
293 |
|
294 |
if (output != null) |
295 |
output.Write(buffer, 0, fieldSize); |
296 |
|
297 |
position += fieldSize; |
298 |
} |
299 |
} |
300 |
} |