private void TrySetClassnameString() { if (HashGuesser.TryGuessHashString(ClassnameHash, out var maybeClassname)) { Classname = maybeClassname; } }
public XElement WriteToXml() { //Create object node HashGuesser.TryGuessHashString(ClassnameHash, out string className); var objectRoot = new XElement("ZoneObject", new XAttribute("Type", className)); var objectData = new XElement("Data"); objectRoot.Add(objectData); //Write 56 byte data block objectData.Add(new XAttribute("ClassnameHash", ClassnameHash)); objectData.Add(new XElement("Handle", Handle)); objectData.Add(Bmin.ToXElement("Bmin")); objectData.Add(Bmax.ToXElement("Bmax")); var flagsNode = new XElement("Flags", new XAttribute("Value", Flags)); flagsNode.Add(new XElement("Flag0", (Flags & (ushort)ZoneObjectFlags.Flag0) != 0)); flagsNode.Add(new XElement("Flag1", (Flags & (ushort)ZoneObjectFlags.Flag1) != 0)); flagsNode.Add(new XElement("Flag2", (Flags & (ushort)ZoneObjectFlags.Flag2) != 0)); flagsNode.Add(new XElement("Flag3", (Flags & (ushort)ZoneObjectFlags.Flag3) != 0)); flagsNode.Add(new XElement("Flag4", (Flags & (ushort)ZoneObjectFlags.Flag4) != 0)); flagsNode.Add(new XElement("Flag5", (Flags & (ushort)ZoneObjectFlags.Flag5) != 0)); flagsNode.Add(new XElement("Flag6", (Flags & (ushort)ZoneObjectFlags.Flag6) != 0)); flagsNode.Add(new XElement("Flag7", (Flags & (ushort)ZoneObjectFlags.Flag7) != 0)); flagsNode.Add(new XElement("Flag8", (Flags & (ushort)ZoneObjectFlags.Flag8) != 0)); flagsNode.Add(new XElement("Flag9", (Flags & (ushort)ZoneObjectFlags.Flag9) != 0)); flagsNode.Add(new XElement("Flag10", (Flags & (ushort)ZoneObjectFlags.Flag10) != 0)); flagsNode.Add(new XElement("Flag11", (Flags & (ushort)ZoneObjectFlags.Flag11) != 0)); flagsNode.Add(new XElement("Flag12", (Flags & (ushort)ZoneObjectFlags.Flag12) != 0)); flagsNode.Add(new XElement("Flag13", (Flags & (ushort)ZoneObjectFlags.Flag13) != 0)); flagsNode.Add(new XElement("Flag14", (Flags & (ushort)ZoneObjectFlags.Flag14) != 0)); flagsNode.Add(new XElement("Flag15", (Flags & (ushort)ZoneObjectFlags.Flag15) != 0)); objectData.Add(flagsNode); objectData.Add(new XAttribute("BlockSize", BlockSize)); objectData.Add(new XElement("Parent", Parent)); objectData.Add(new XElement("Sibling", Sibling)); objectData.Add(new XElement("Child", Child)); objectData.Add(new XElement("Num", Num)); objectData.Add(new XAttribute("NumProps", NumProps)); objectData.Add(new XAttribute("Size", Size)); var propertiesNode = new XElement("Properties"); objectRoot.Add(propertiesNode); //Write properties foreach (var property in Properties) { propertiesNode.Add(property.WriteToXml()); } //Return object node return(objectRoot); }
/// <summary> /// Attempt to read the rfgzone_pc/layer_pc format from the stream. Fill this class with that data. /// </summary> /// <param name="stream">The stream to read the data from.</param> public void ReadFromBinary(Stream stream) { var reader = new BinaryReader(stream); //Read header Signature = reader.ReadUInt32(); if (Signature != 1162760026) //Should equal ascii value for "ZONE" { Console.WriteLine($"Error! Invalid file signature. Expected 1162760026, detected {Signature}. Make sure the vpp/str2 containing this file was extracted properly. Exiting."); return; } Version = reader.ReadUInt32(); if (Version != 36) //Only have seen and reversed version 36 { Console.WriteLine($"Error! Unsupported format version. Expected 36, detected {Version}. Exiting."); return; } NumObjects = reader.ReadUInt32(); NumHandles = reader.ReadUInt32(); DistrictHash = reader.ReadUInt32(); //Todo: Figure out how district hashes are generated DistrictFlags = reader.ReadUInt32(); //Try to get district name from hash. Note that p_ files always have 0 for the hash. Game likely pulls from matching non p_ file if (HashGuesser.TryGuessHashString(DistrictHash, out string districtName)) { DistrictName = districtName; } //Todo: Make sure it's safe to skip this on zones with numHandles != 0 //Todo: Try deleting relational data on all zones and seeing if that causes an issue. /* * Relation data section. Likely a structure directly mapped to memory. * Isn't present if districtFlags equals 1, 4, or 5 (if the 0th and 2nd bits are true) * I've only seen layer_pc have a flag equal to 4 and rfgzone_pc either 1 or 0 (0 means they have relational_data) * Skip by default, since in my tests so far this entire section can be zeroed out without issue. * Can later add code to handle it if it turns out to be required. */ #if IgnoreRelationData if ((DistrictFlags & 5) == 0) //Relation data section only present when this condition is met. { reader.BaseStream.Seek(87368, SeekOrigin.Current); //Offset: 87392 } #else if ((DistrictFlags & 5) == 0) { //Totals to 87368 var stupid_vtable_padding = reader.ReadBytes(4); //4 var free = reader.ReadBytes(2); //2 var slot = reader.ReadBytes(14560); //2 * 7280 //starts at 30 var next = reader.ReadBytes(14560); //2 * 7280 //starts at 14590 var more_stupid_padding = reader.ReadBytes(2); //2 //starts at 29150 var entry_key = reader.ReadBytes(29120); //4 * 7280 //starts at 29152 //Might be 4 * numObjects bytes of data for keys, rest is useless, can be deleted/omitted. (Same with other arrays) var entry_data = reader.ReadBytes(29120); //4 * 7280 //starts at 58272 //Ends at 87392 } #endif //Read zone objects for (int i = 0; i < NumObjects; i++) { var zoneObject = new ZoneObject(); zoneObject.ReadFromBinary(stream); Objects.Add(zoneObject); } #if DEBUG Console.WriteLine($"Last zone object ends at offset {reader.BaseStream.Position}"); #endif }