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