| 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 |
} |