public void TestCustomSerializationStructsInMap() { var tester = new UAsset(Path.Combine("TestCustomSerializationStructsInMap", "wtf.uasset"), UE4Version.VER_UE4_25); Assert.IsTrue(tester.VerifyBinaryEquality()); // Get the map property in export 2 Export exportTwo = FPackageIndex.FromRawIndex(2).ToExport(tester); Assert.IsTrue(exportTwo is NormalExport); NormalExport exportTwoNormal = (NormalExport)exportTwo; var mapPropertyName = FName.FromString("KekWait"); MapPropertyData testMap = exportTwoNormal[mapPropertyName] as MapPropertyData; Assert.IsNotNull(testMap); Assert.IsTrue(testMap == exportTwoNormal[mapPropertyName.Value.Value]); // Get the first entry of the map StructPropertyData entryKey = testMap?.Value?.Keys?.ElementAt(0) as StructPropertyData; StructPropertyData entryValue = testMap?.Value?[0] as StructPropertyData; Assert.IsNotNull(entryKey?.Value?[0]); Assert.IsNotNull(entryValue?.Value?[0]); // Check that the properties are correct Assert.IsTrue(entryKey.Value[0] is VectorPropertyData); Assert.IsTrue(entryValue.Value[0] is LinearColorPropertyData); }
public override ZeroPaddingMode Read2(BinaryReader reader, int nextStarting) { // Find an ObjectProperty named RowStruct string decidedStructType = "Generic"; foreach (PropertyData thisData in Data) { if (thisData.Name == "RowStruct" && thisData is ObjectPropertyData thisObjData) { decidedStructType = Asset.GetHeaderReference((int)thisObjData.Value.Property); break; } } Debug.WriteLine(decidedStructType); reader.ReadInt32(); Data2 = new DataTable(); int numEntries = reader.ReadInt32(); for (int i = 0; i < numEntries; i++) { string rowName = Asset.GetHeaderReference(reader.ReadInt32()); int duplicateIndex = reader.ReadInt32(); var nextStruct = new StructPropertyData(rowName, Asset) { StructType = decidedStructType }; nextStruct.Read(reader, false, 0); Data2.Table.Add(new DataTableEntry(nextStruct, duplicateIndex)); } return(ZeroPaddingMode.Unknown); }
public override void Read(AssetBinaryReader reader, int nextStarting) { base.Read(reader, nextStarting); // Find an ObjectProperty named RowStruct FName decidedStructType = new FName("Generic"); foreach (PropertyData thisData in Data) { if (thisData.Name.Value.Value == "RowStruct" && thisData is ObjectPropertyData thisObjData && thisObjData.Value.IsImport()) { decidedStructType = thisObjData.ToImport(reader.Asset).ObjectName; break; } } reader.ReadInt32(); Table = new UDataTable(); int numEntries = reader.ReadInt32(); for (int i = 0; i < numEntries; i++) { FName rowName = reader.ReadFName(); var nextStruct = new StructPropertyData(rowName) { StructType = decidedStructType }; nextStruct.Read(reader, false, 1); Table.Data.Add(nextStruct); } }
public void TestDataTables() { var tester = new UAsset(Path.Combine("TestDatatables", "PB_DT_RandomizerRoomCheck.uasset"), UE4Version.VER_UE4_18); Assert.IsTrue(tester.VerifyBinaryEquality()); Assert.IsTrue(CheckAllExportsParsedCorrectly(tester)); Assert.IsTrue(tester.Exports.Count == 1); var ourDataTableExport = tester.Exports[0] as DataTableExport; var ourTable = ourDataTableExport?.Table; Assert.IsNotNull(ourTable); // Check out the first entry to make sure it's parsing alright, and flip all the flags for later testing StructPropertyData firstEntry = ourTable.Data[0]; bool didFindTestName = false; for (int i = 0; i < firstEntry.Value.Count; i++) { var propData = firstEntry.Value[i]; Debug.WriteLine(i + ": " + propData.Name + ", " + propData.PropertyType); if (propData.Name == new FName("AcceleratorANDDoubleJump")) { didFindTestName = true; } if (propData is BoolPropertyData boolProp) { boolProp.Value = !boolProp.Value; } } Assert.IsTrue(didFindTestName); // Save the modified table tester.Write(Path.Combine("TestDatatables", "MODIFIED.uasset")); // Load the modified table back in and make sure we're good var tester2 = new UAsset(Path.Combine("TestDatatables", "MODIFIED.uasset"), UE4Version.VER_UE4_18); Assert.IsTrue(tester2.VerifyBinaryEquality()); Assert.IsTrue(CheckAllExportsParsedCorrectly(tester2)); Assert.IsTrue(tester2.Exports.Count == 1); // Flip the flags back to what they originally were firstEntry = (tester2.Exports[0] as DataTableExport)?.Table?.Data?[0]; Assert.IsNotNull(firstEntry); for (int i = 0; i < firstEntry.Value.Count; i++) { if (firstEntry.Value[i] is BoolPropertyData boolProp) { boolProp.Value = !boolProp.Value; } } // Save and check that it's binary equal to what we originally had tester2.Write(tester2.FilePath); Assert.IsTrue(File.ReadAllBytes(Path.Combine("TestDatatables", "PB_DT_RandomizerRoomCheck.uasset")).SequenceEqual(File.ReadAllBytes(Path.Combine("TestDatatables", "MODIFIED.uasset")))); }
public override int Write(BinaryWriter writer, bool includeHeader) { if (Value.Length > 0) { ArrayType = Value[0].Type; } if (includeHeader) { writer.Write((long)Asset.SearchHeaderReference(ArrayType)); writer.Write((byte)0); } int here = (int)writer.BaseStream.Position; writer.Write(Value.Length); if (ArrayType == "StructProperty") { StructPropertyData firstElem = Value.Length == 0 ? DummyStruct : (StructPropertyData)Value[0]; string fullType = firstElem.StructType; writer.Write((long)Asset.SearchHeaderReference(firstElem.Name)); writer.Write((long)Asset.SearchHeaderReference("StructProperty")); int lengthLoc = (int)writer.BaseStream.Position; writer.Write((long)0); writer.Write((long)Asset.SearchHeaderReference(fullType)); writer.Write(firstElem.StructGUID.ToByteArray()); writer.Write((byte)0); for (int i = 0; i < Value.Length; i++) { ((StructPropertyData)Value[i]).StructType = fullType; Value[i].Write(writer, false); } int fullLen = (int)writer.BaseStream.Position - lengthLoc; int newLoc = (int)writer.BaseStream.Position; writer.Seek(lengthLoc, SeekOrigin.Begin); writer.Write(fullLen - 32 - (includeHeader ? 1 : 0)); writer.Seek(newLoc, SeekOrigin.Begin); } else { for (int i = 0; i < Value.Length; i++) { Value[i].Write(writer, false); } } return((int)writer.BaseStream.Position - here); }
private PropertyData MapTypeToClass(string type, string name, AssetReader asset, BinaryReader reader, long leng, bool includeHeader) { switch (type) { case "StructProperty": StructPropertyData data = new StructPropertyData(name, asset, "Generic"); data.Read(reader, false, leng); return(data); default: var res = MainSerializer.TypeToClass(type, name, asset, null, leng); res.Read(reader, includeHeader, leng); return(res); } }
private PropertyData MapTypeToClass(FName type, FName name, AssetBinaryReader reader, int leng, bool includeHeader, bool isKey) { switch (type.Value.Value) { case "StructProperty": FName strucType = null; if (reader.Asset.MapStructTypeOverride.ContainsKey(name.Value.Value)) { if (isKey) { strucType = reader.Asset.MapStructTypeOverride[name.Value.Value].Item1; } else { strucType = reader.Asset.MapStructTypeOverride[name.Value.Value].Item2; } } if (strucType == null) { strucType = new FName("Generic"); } StructPropertyData data = new StructPropertyData(name, strucType); data.Offset = reader.BaseStream.Position; data.Read(reader, false, 1); return(data); default: var res = MainSerializer.TypeToClass(type, name, reader.Asset, null, leng); res.Offset = reader.BaseStream.Position; res.Read(reader, includeHeader, leng); return(res); } }
public static PropertyData TypeToClass(string type, string name, AssetReader asset, BinaryReader reader = null, long leng = 0, bool includeHeader = true) { //Debug.WriteLine(type); PropertyData data; switch (type) { case "BoolProperty": data = new BoolPropertyData(name, asset); break; case "Int8Property": data = new Int8PropertyData(name, asset); break; case "Int16Property": data = new Int16PropertyData(name, asset); break; case "IntProperty": data = new IntPropertyData(name, asset); break; case "Int64Property": data = new Int64PropertyData(name, asset); break; case "UInt16Property": data = new UInt16PropertyData(name, asset); break; case "UInt32Property": data = new UInt32PropertyData(name, asset); break; case "UInt64Property": data = new UInt64PropertyData(name, asset); break; case "FloatProperty": data = new FloatPropertyData(name, asset); break; case "TextProperty": data = new TextPropertyData(name, asset); break; case "StrProperty": data = new StrPropertyData(name, asset); break; case "ObjectProperty": data = new ObjectPropertyData(name, asset); break; case "EnumProperty": data = new EnumPropertyData(name, asset); break; case "ByteProperty": data = new BytePropertyData(name, asset); break; case "NameProperty": data = new NamePropertyData(name, asset); break; case "ArrayProperty": data = new ArrayPropertyData(name, asset); break; case "MapProperty": data = new MapPropertyData(name, asset); break; case "StructProperty": data = new StructPropertyData(name, asset); break; case "Guid": data = new GuidPropertyData(name, asset); break; case "LinearColor": data = new LinearColorPropertyData(name, asset); break; case "Color": data = new ColorPropertyData(name, asset); break; case "Vector": data = new VectorPropertyData(name, asset); break; case "Vector2D": data = new Vector2DPropertyData(name, asset); break; case "Box": data = new BoxPropertyData(name, asset); break; case "IntPoint": data = new IntPointPropertyData(name, asset); break; case "DateTime": data = new DateTimePropertyData(name, asset); break; case "Timespan": data = new TimespanPropertyData(name, asset); break; case "Rotator": data = new RotatorPropertyData(name, asset); break; case "Quat": data = new QuatPropertyData(name, asset); break; case "Vector4": data = new Vector4PropertyData(name, asset); break; case "SoftObjectProperty": data = new SoftObjectPropertyData(name, asset); break; case "MulticastDelegateProperty": data = new MulticastDelegatePropertyData(name, asset); break; default: #if DEBUG Debug.WriteLine("Last type: " + lastType); #endif if (reader == null) { throw new FormatException("Unknown property type: " + type + " (on " + name + ")"); } throw new FormatException("Unknown property type: " + type + " (on " + name + " at " + reader.BaseStream.Position + ")"); } #if DEBUG lastType = type; #endif if (reader != null) { data.Read(reader, includeHeader, leng); } return(data); }
public DataTableEntry(StructPropertyData data, int duplicateIndex) { Data = data; DuplicateIndex = duplicateIndex; }
public override void Read(AssetBinaryReader reader, bool includeHeader, long leng1, long leng2 = 0) { if (includeHeader) { ArrayType = reader.ReadFName(); PropertyGuid = reader.ReadPropertyGuid(); } int numEntries = reader.ReadInt32(); if (ArrayType.Value.Value == "StructProperty" && ShouldSerializeStructsDifferently) { var results = new PropertyData[numEntries]; FName name = this.Name; long structLength = 1; FName fullType = new FName("Generic"); Guid structGUID = new Guid(); if (reader.Asset.EngineVersion >= UE4Version.VER_UE4_INNER_ARRAY_TAG_INFO) { name = reader.ReadFName(); if (name.Value.Value.Equals("None")) { Value = results; return; } FName thisArrayType = reader.ReadFName(); if (thisArrayType.Value.Value.Equals("None")) { Value = results; return; } if (thisArrayType.Value.Value != ArrayType.Value.Value) { throw new FormatException("Invalid array type: " + thisArrayType.ToString() + " vs " + ArrayType.ToString()); } structLength = reader.ReadInt64(); // length value fullType = reader.ReadFName(); structGUID = new Guid(reader.ReadBytes(16)); reader.ReadPropertyGuid(); } if (numEntries == 0) { DummyStruct = new StructPropertyData(name, fullType) { StructGUID = structGUID }; } else { for (int i = 0; i < numEntries; i++) { var data = new StructPropertyData(name, fullType); data.Offset = reader.BaseStream.Position; data.Read(reader, false, structLength); data.StructGUID = structGUID; results[i] = data; } DummyStruct = (StructPropertyData)results[0]; } Value = results; } else { var results = new PropertyData[numEntries]; if (numEntries > 0) { int averageSizeEstimate1 = (int)(leng1 / numEntries); int averageSizeEstimate2 = (int)((leng1 - 4) / numEntries); for (int i = 0; i < numEntries; i++) { results[i] = MainSerializer.TypeToClass(ArrayType, new FName(i.ToString(), int.MinValue), reader.Asset); results[i].Offset = reader.BaseStream.Position; if (results[i] is StructPropertyData) { ((StructPropertyData)results[i]).StructType = new FName("Generic"); } results[i].Read(reader, false, averageSizeEstimate1, averageSizeEstimate2); } } Value = results; } }
public override int Write(AssetBinaryWriter writer, bool includeHeader) { if (Value.Length > 0) { ArrayType = Value[0].PropertyType; } if (includeHeader) { writer.Write(ArrayType); writer.WritePropertyGuid(PropertyGuid); } int here = (int)writer.BaseStream.Position; writer.Write(Value.Length); if (ArrayType.Value.Value == "StructProperty" && ShouldSerializeStructsDifferently) { if (Value.Length == 0 && DummyStruct == null) { throw new InvalidOperationException("No dummy struct present in an empty StructProperty array, cannot serialize"); } if (Value.Length > 0) { DummyStruct = (StructPropertyData)Value[0]; } FName fullType = DummyStruct.StructType; int lengthLoc = -1; if (writer.Asset.EngineVersion >= UE4Version.VER_UE4_INNER_ARRAY_TAG_INFO) { writer.Write(DummyStruct.Name); writer.Write(new FName("StructProperty")); lengthLoc = (int)writer.BaseStream.Position; writer.Write((long)0); writer.Write(fullType); if (writer.Asset.EngineVersion >= UE4Version.VER_UE4_STRUCT_GUID_IN_PROPERTY_TAG) { writer.Write(DummyStruct.StructGUID.ToByteArray()); } if (writer.Asset.EngineVersion >= UE4Version.VER_UE4_PROPERTY_GUID_IN_PROPERTY_TAG) { writer.Write((byte)0); } } for (int i = 0; i < Value.Length; i++) { ((StructPropertyData)Value[i]).StructType = fullType; Value[i].Offset = writer.BaseStream.Position; Value[i].Write(writer, false); } if (writer.Asset.EngineVersion >= UE4Version.VER_UE4_INNER_ARRAY_TAG_INFO) { int fullLen = (int)writer.BaseStream.Position - lengthLoc; int newLoc = (int)writer.BaseStream.Position; writer.Seek(lengthLoc, SeekOrigin.Begin); writer.Write(fullLen - 32 - (includeHeader ? 1 : 0)); writer.Seek(newLoc, SeekOrigin.Begin); } } else { for (int i = 0; i < Value.Length; i++) { Value[i].Offset = writer.BaseStream.Position; Value[i].Write(writer, false); } } return((int)writer.BaseStream.Position - here); }
public override void Read(BinaryReader reader, bool includeHeader, long leng) { if (includeHeader) { ArrayType = Asset.GetHeaderReference((int)reader.ReadInt64()); reader.ReadByte(); // null byte } int numEntries = reader.ReadInt32(); if (ArrayType == "StructProperty") { var results = new PropertyData[numEntries]; string name = Asset.GetHeaderReference((int)reader.ReadInt64()); if (name.Equals("None")) { Value = results; return; } if (Asset.GetHeaderReference((int)reader.ReadInt64()) != ArrayType) { throw new FormatException("Invalid array type"); } reader.ReadInt64(); // length value string fullType = Asset.GetHeaderReference((int)reader.ReadInt64()); Guid structGUID = new Guid(reader.ReadBytes(16)); reader.ReadByte(); if (numEntries == 0) { DummyStruct = new StructPropertyData(name, Asset, fullType) { StructGUID = structGUID }; } else { for (int i = 0; i < numEntries; i++) { var data = new StructPropertyData(name, Asset, fullType); data.Read(reader, false, 0); data.StructGUID = structGUID; results[i] = data; } } Value = results; } else { var results = new PropertyData[numEntries]; if (numEntries > 0) { int averageSize = (int)(leng / numEntries); for (int i = 0; i < numEntries; i++) { results[i] = MainSerializer.TypeToClass(ArrayType, Name, Asset); results[i].Read(reader, false, averageSize); } } Value = results; } }