diff --git a/AssetStudio/StudioClasses/BinaryWriterExtensions.cs b/AssetStudio/StudioClasses/BinaryWriterExtensions.cs index 017d077..934b075 100644 --- a/AssetStudio/StudioClasses/BinaryWriterExtensions.cs +++ b/AssetStudio/StudioClasses/BinaryWriterExtensions.cs @@ -20,5 +20,23 @@ namespace AssetStudio { WriteArray(writer.Write, array); } + + public static void AlignStream(this BinaryWriter writer, int alignment) + { + var pos = writer.BaseStream.Position; + var mod = pos % alignment; + if (mod != 0) + { + writer.Write(new byte[alignment - mod]); + } + } + + public static void WriteAlignedString(this BinaryWriter writer, string str) + { + var bytes = Encoding.UTF8.GetBytes(str); + writer.Write(bytes.Length); + writer.Write(bytes); + writer.AlignStream(4); + } } } diff --git a/AssetStudio/StudioClasses/ClassStructHelper.cs b/AssetStudio/StudioClasses/ClassStructHelper.cs index 0865ea1..ca30792 100644 --- a/AssetStudio/StudioClasses/ClassStructHelper.cs +++ b/AssetStudio/StudioClasses/ClassStructHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Dynamic; +using System.IO; using System.Linq; using System.Text; @@ -71,7 +72,7 @@ namespace AssetStudio append = false; var str = reader.ReadAlignedString(); sb.AppendFormat("{0}{1} {2} = \"{3}\"\r\n", (new string('\t', level)), varTypeStr, varNameStr, str); - i += 3;//skip + i += 3; break; case "vector": { @@ -149,7 +150,140 @@ namespace AssetStudio reader.AlignStream(4); } - public static List GetMembers(List members, int level, int index) + public static ExpandoObject ReadDynamicClass(List members, EndianBinaryReader reader) + { + var obj = new ExpandoObject(); + var objdic = (IDictionary)obj; + for (int i = 0; i < members.Count; i++) + { + var member = members[i]; + var varNameStr = member.Name; + objdic[varNameStr] = ReadValue(members, reader, ref i); + } + return obj; + } + + private static object ReadValue(List members, EndianBinaryReader reader, ref int i) + { + var member = members[i]; + var level = member.Level; + var varTypeStr = member.Type; + object value; + var align = (member.Flag & 0x4000) != 0; + switch (varTypeStr) + { + case "SInt8": + value = reader.ReadSByte(); + break; + case "UInt8": + value = reader.ReadByte(); + break; + case "short": + case "SInt16": + value = reader.ReadInt16(); + break; + case "UInt16": + case "unsigned short": + value = reader.ReadUInt16(); + break; + case "int": + case "SInt32": + value = reader.ReadInt32(); + break; + case "UInt32": + case "unsigned int": + case "Type*": + value = reader.ReadUInt32(); + break; + case "long long": + case "SInt64": + value = reader.ReadInt64(); + break; + case "UInt64": + case "unsigned long long": + value = reader.ReadUInt64(); + break; + case "float": + value = reader.ReadSingle(); + break; + case "double": + value = reader.ReadDouble(); + break; + case "bool": + value = reader.ReadBoolean(); + break; + case "string": + value = reader.ReadAlignedString(); + i += 3; + break; + case "vector": + { + if ((members[i + 1].Flag & 0x4000) != 0) + align = true; + var size = reader.ReadInt32(); + var list = new List(size); + var vector = GetMembers(members, level, i); + i += vector.Count - 1; + vector.RemoveRange(0, 3); + for (int j = 0; j < size; j++) + { + int tmp = 0; + list.Add(ReadValue(vector, reader, ref tmp)); + } + value = list; + break; + } + case "map": + { + if ((members[i + 1].Flag & 0x4000) != 0) + align = true; + var size = reader.ReadInt32(); + var dic = new List>(size); + var map = GetMembers(members, level, i); + i += map.Count - 1; + map.RemoveRange(0, 4); + var first = GetMembers(map, map[0].Level, 0); + map.RemoveRange(0, first.Count); + var second = map; + for (int j = 0; j < size; j++) + { + int tmp1 = 0; + int tmp2 = 0; + dic.Add(new KeyValuePair(ReadValue(first, reader, ref tmp1), ReadValue(second, reader, ref tmp2))); + } + value = dic; + break; + } + case "TypelessData": + { + var size = reader.ReadInt32(); + value = reader.ReadBytes(size); + i += 2; + break; + } + default: + { + var @class = GetMembers(members, level, i); + @class.RemoveAt(0); + i += @class.Count; + var obj = new ExpandoObject(); + var objdic = (IDictionary)obj; + for (int j = 0; j < @class.Count; j++) + { + var classmember = @class[j]; + var name = classmember.Name; + objdic[name] = ReadValue(@class, reader, ref j); + } + value = obj; + break; + } + } + if (align) + reader.AlignStream(4); + return value; + } + + private static List GetMembers(List members, int level, int index) { var member2 = new List(); member2.Add(members[0]); @@ -165,5 +299,139 @@ namespace AssetStudio } return member2; } + + public static byte[] WriteDynamicClass(ExpandoObject obj, List members) + { + var stream = new MemoryStream(); + var write = new BinaryWriter(stream); + var objdic = (IDictionary)obj; + for (int i = 0; i < members.Count; i++) + { + var member = members[i]; + var varNameStr = member.Name; + WriteValue(objdic[varNameStr], members, write, ref i); + } + return stream.ToArray(); + } + + private static void WriteValue(object value, List members, BinaryWriter write, ref int i) + { + var member = members[i]; + var level = member.Level; + var varTypeStr = member.Type; + var align = (member.Flag & 0x4000) != 0; + switch (varTypeStr) + { + case "SInt8": + write.Write((sbyte)value); + break; + case "UInt8": + write.Write((byte)value); + break; + case "short": + case "SInt16": + write.Write((short)value); + break; + case "UInt16": + case "unsigned short": + write.Write((ushort)value); + break; + case "int": + case "SInt32": + write.Write((int)value); + break; + case "UInt32": + case "unsigned int": + case "Type*": + write.Write((uint)value); + break; + case "long long": + case "SInt64": + write.Write((long)value); + break; + case "UInt64": + case "unsigned long long": + write.Write((ulong)value); + break; + case "float": + write.Write((float)value); + break; + case "double": + write.Write((double)value); + break; + case "bool": + write.Write((bool)value); + break; + case "string": + write.WriteAlignedString((string)value); + i += 3; + break; + case "vector": + { + if ((members[i + 1].Flag & 0x4000) != 0) + align = true; + var list = (List)value; + var size = list.Count; + write.Write(size); + var vector = GetMembers(members, level, i); + i += vector.Count - 1; + vector.RemoveRange(0, 3); + for (int j = 0; j < size; j++) + { + int tmp = 0; + WriteValue(list[j], vector, write, ref tmp); + } + break; + } + case "map": + { + if ((members[i + 1].Flag & 0x4000) != 0) + align = true; + var dic = (List>)value; + var size = dic.Count; + write.Write(size); + var map = GetMembers(members, level, i); + i += map.Count - 1; + map.RemoveRange(0, 4); + var first = GetMembers(map, map[0].Level, 0); + map.RemoveRange(0, first.Count); + var second = map; + for (int j = 0; j < size; j++) + { + int tmp1 = 0; + int tmp2 = 0; + WriteValue(dic[j].Key, first, write, ref tmp1); + WriteValue(dic[j].Value, second, write, ref tmp2); + } + break; + } + case "TypelessData": + { + var bytes = ((object[])value).Cast().ToArray(); + var size = bytes.Length; + write.Write(size); + write.Write(bytes); + i += 2; + break; + } + default: + { + var @class = GetMembers(members, level, i); + @class.RemoveAt(0); + i += @class.Count; + var obj = (ExpandoObject)value; + var objdic = (IDictionary)obj; + for (int j = 0; j < @class.Count; j++) + { + var classmember = @class[j]; + var name = classmember.Name; + WriteValue(objdic[name], @class, write, ref j); + } + break; + } + } + if (align) + write.AlignStream(4); + } } }