public object Clone() { var zoneObject = new ZoneObject { ClassnameHash = this.ClassnameHash, Handle = this.Handle, Bmin = this.Bmin, Bmax = this.Bmax, Flags = this.Flags, BlockSize = this.BlockSize, Parent = this.Parent, Sibling = this.Sibling, Child = this.Child, Num = this.Num, NumProps = this.NumProps, Size = this.Size, Properties = this.Properties.ConvertAll(prop => prop), Classname = this.Classname, Description = this.Description, }; return(zoneObject); }
/// <summary> /// Attempt to read xml data from document. /// </summary> public void ReadFromXml(XDocument document) { var root = document.Root; //Read header var header = root.GetRequiredElement("Header"); Signature = header.GetRequiredAttributeValue("Signature").ToUint32(); Version = header.GetRequiredAttributeValue("Version").ToUint32(); NumObjects = header.GetRequiredAttributeValue("NumObjects").ToUint32(); NumHandles = header.GetRequiredAttributeValue("NumHandles").ToUint32(); DistrictHash = header.GetRequiredAttributeValue("DistrictHash").ToUint32(); DistrictFlags = header.GetRequiredAttributeValue("DistrictFlags").ToUint32(); //Would load relation data here if that section turns out to be necessary //Read zone objects var zoneObjectsNode = root.GetRequiredElement("ZoneObjects"); foreach (var objectNode in zoneObjectsNode.Elements("ZoneObject")) { var zoneObject = new ZoneObject(); zoneObject.ReadFromXml(objectNode); Objects.Add(zoneObject); } NumObjects = (uint)Objects.Count; //Update object count in case whoever edited the xml forgot to }
public ZoneObject DuplicateObject(ZoneObject obj) { int objIndex = Objects.IndexOf(obj); if (objIndex == -1) { return(null); } var newObj = obj.Clone() as ZoneObject; if (newObj == null) { return(null); } newObj.Num = GetUniqueNum(); //Further testing needed to see if this is necessary, likely can have same num as other objs as long as they are a different class newObj.Handle = GetUniqueHandle(); Objects.Insert(objIndex + 1, newObj); return(newObj); }
/// <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 }