public SetObject(SetObjectType type, string typeName, uint objID) { ObjectType = typeName; ObjectID = objID; foreach (var param in type.Parameters) { if (param is SetObjectTypeParamGroup group) { var g = new SetObjectParamGroup(group.Padding); var groupParams = g.Parameters; foreach (var p in group.Parameters) { groupParams.Add(new SetObjectParam(p.DataType, p.DefaultValue)); } Parameters.Add(g); continue; } Parameters.Add(new SetObjectParam(param.DataType, param.DefaultValue)); } }
LoadObjectTemplates(string directory) { if (!Directory.Exists(directory)) { throw new DirectoryNotFoundException(); } var objectTemplates = new Dictionary <string, SetObjectType>(); foreach (string dir in Directory.GetDirectories(directory)) { string category = new DirectoryInfo(dir).Name; foreach (string file in Directory.GetFiles(dir, $"*{Extension}")) { var template = new SetObjectType() { Category = category }; string objTypeName = Path.GetFileNameWithoutExtension(file); template.Load(file); if (objectTemplates.ContainsKey(objTypeName)) { Console.WriteLine("WARNING: Skipping over duplicate template \"{0}\".", objTypeName); continue; } objectTemplates.Add(objTypeName, template); } } return(objectTemplates); }
public static XElement WriteObject(SetObject obj, SetObjectType type = null) { // Parameters var elem = new XElement(obj.ObjectType); for (int i = 0; i < obj.Parameters.Count; ++i) { elem.Add(new XElement((type == null) ? $"Parameter{i + 1}" : type.Parameters[i].Name, obj.Parameters[i].Data)); } // MultiSetTransforms if (obj.Children.Length > 0) { var multiSetParam = new XElement("MultiSetParam"); for (int i = 0; i < obj.Children.Length;) { var multiSetElem = new XElement("Element"); multiSetElem.AddElem("Index", ++i); WriteTransform(obj.Children[i], multiSetElem); multiSetParam.Add(multiSetElem); } elem.AddElem("BaseLine", 0); elem.AddElem("Count", obj.Children.Length + 1); // TODO: Is this right? elem.AddElem("Direction", 0); elem.AddElem("Interval", 1.5f); elem.AddElem("IntervalBase", 0); elem.AddElem("PositionBase", 0); elem.AddElem("RotationBase", 0); elem.Add(multiSetParam); } // Transform WriteTransform(obj.Transform, elem); // Special Parameters elem.AddElem("Range", obj.GetCustomDataValue <float>("Range", 100)); elem.AddElem("SetObjectID", obj.ObjectID); foreach (var customData in obj.CustomData) { if (customData.Key == "Range") { continue; } elem.Add(new XElement( customData.Key, customData.Value)); } return(elem); }
private static SetObject ReadObject(ExtendedBinaryReader reader, SetObjectType objTemplate, string objType, SOBJType type, bool rawDataMode = false) // true = full, false = only remaining bytes { // For some reason these separate values are saved as one uint rather than two ushorts. // Because of this, the values are in a different order depending on endianness, and // this is the easiest known way to read them. uint unknownValue = reader.ReadUInt32(); ushort unknown1 = (ushort)((unknownValue >> 16) & 0xFFFF); ushort objID = (ushort)(unknownValue & 0xFFFF); var obj = new SetObject() { ObjectType = objType, ObjectID = objID }; uint unknown2 = reader.ReadUInt32(); uint unknown3 = reader.ReadUInt32(); float unknown4 = reader.ReadSingle(); float rangeIn = reader.ReadSingle(); float rangeOut = reader.ReadSingle(); uint parent = (type == SOBJType.LostWorld) ? reader.ReadUInt32() : 0; uint transformsOffset = reader.ReadUInt32(); uint transformCount = reader.ReadUInt32(); uint unknown5 = reader.ReadUInt32(); uint unknown6 = (type == SOBJType.LostWorld) ? reader.ReadUInt32() : 0; uint unknown7 = (type == SOBJType.LostWorld) ? reader.ReadUInt32() : 0; // Call me crazy, but I have a weird feeling these values aren't JUST padding if (unknown3 != 0 || unknown5 != 0 || unknown6 != 0 || unknown7 != 0) { Console.WriteLine("WARNING: Not padding?! ({0},{1},{2},{3})", unknown3, unknown5, unknown6, unknown7); } // Add custom data to object obj.CustomData.Add("Unknown1", new SetObjectParam(typeof(ushort), unknown1)); obj.CustomData.Add("Unknown2", new SetObjectParam(typeof(uint), unknown2)); obj.CustomData.Add("Unknown3", new SetObjectParam(typeof(uint), unknown3)); obj.CustomData.Add("Unknown4", new SetObjectParam(typeof(float), unknown4)); obj.CustomData.Add("RangeIn", new SetObjectParam(typeof(float), rangeIn)); obj.CustomData.Add("RangeOut", new SetObjectParam(typeof(float), rangeOut)); if (type == SOBJType.LostWorld) { obj.CustomData.Add("Parent", new SetObjectParam(typeof(uint), parent)); } // Skip loading parameters if template doesn't exist if (objTemplate != null) { // Get Raw Byte Length var rawDataLenExtra = objTemplate.GetExtra("RawByteLength"); long paramBegin = reader.BaseStream.Position; int rawLength = 0; if (rawDataLenExtra != null && !string.IsNullOrEmpty(rawDataLenExtra.Value)) { int.TryParse(rawDataLenExtra.Value, out rawLength); } // Read all the data then return to beginning if (rawDataMode == true && rawLength != 0) { obj.CustomData.Add("RawParamData", new SetObjectParam(typeof(byte[]), reader.ReadBytes(rawLength))); reader.JumpTo(paramBegin); } // Parameters foreach (var param in objTemplate.Parameters) { // For compatibility with SonicGlvl templates. if (param.Name == "Unknown1" || param.Name == "Unknown2" || param.Name == "Unknown3" || param.Name == "RangeIn" || param.Name == "RangeOut" || param.Name == "Parent") { continue; } // Read Special Types/Fix Padding if (param.DataType == typeof(uint[])) { // Data Info reader.FixPadding(4); uint arrOffset = reader.ReadUInt32(); uint arrLength = reader.ReadUInt32(); uint arrUnknown = reader.ReadUInt32(); long curPos = reader.BaseStream.Position; // Data var arr = new uint[arrLength]; reader.JumpTo(arrOffset, false); for (uint i = 0; i < arrLength; ++i) { arr[i] = reader.ReadUInt32(); } obj.Parameters.Add(new SetObjectParam(param.DataType, arr)); reader.BaseStream.Position = curPos; continue; } else if (param.DataType == typeof(string)) { // Data Info uint strOffset = reader.ReadUInt32(); uint strUnknown = reader.ReadUInt32(); string str = null; // Data if (strOffset != 0) { long curPos = reader.BaseStream.Position; reader.JumpTo(strOffset, false); str = reader.ReadNullTerminatedString(); reader.BaseStream.Position = curPos; } obj.Parameters.Add(new SetObjectParam(param.DataType, str)); continue; } else if (param.DataType == typeof(float) || param.DataType == typeof(int) || param.DataType == typeof(uint)) { reader.FixPadding(4); } else if (type == SOBJType.LostWorld && param.DataType == typeof(Vector3)) { reader.FixPadding(16); } // Read Data var objParam = new SetObjectParam(param.DataType, reader.ReadByType(param.DataType)); obj.Parameters.Add(objParam); } if (rawDataMode == false) { long knownParamLength = (reader.BaseStream.Position - paramBegin); long remainingBytes = (rawLength - knownParamLength); obj.CustomData.Add("RawParamData", new SetObjectParam(typeof(byte[]), reader.ReadBytes((int)remainingBytes))); } } // Transforms uint childCount = transformCount - 1; obj.Children = new SetObjectTransform[childCount]; reader.JumpTo(transformsOffset, false); obj.Transform = ReadTransform(reader, type == SOBJType.LostWorld); for (uint i = 0; i < childCount; ++i) { obj.Children[i] = ReadTransform(reader, type == SOBJType.LostWorld); } return(obj); }