diff --git a/LibreMetaverse.sln b/LibreMetaverse.sln index 1226d094..60f4a192 100644 --- a/LibreMetaverse.sln +++ b/LibreMetaverse.sln @@ -35,6 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VoiceTest", "Programs\Voice EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse.PrimMesher", "PrimMesher\LibreMetaverse.PrimMesher.csproj", "{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mapgenerator", "Programs\mapgenerator\mapgenerator.csproj", "{2867B4B3-0000-0000-0000-000000000000}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,6 +103,10 @@ Global {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|Any CPU.Build.0 = Release|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Programs/mapgenerator/ProtocolManager.cs b/Programs/mapgenerator/ProtocolManager.cs new file mode 100644 index 00000000..9653fd2f --- /dev/null +++ b/Programs/mapgenerator/ProtocolManager.cs @@ -0,0 +1,694 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace mapgenerator +{ + /// + /// + /// + public enum PacketFrequency + { + /// + Low, + /// + Medium, + /// + High + } + + /// + /// + /// + public enum FieldType + { + /// + U8, + /// + U16, + /// + U32, + /// + U64, + /// + S8, + /// + S16, + /// + S32, + /// + F32, + /// + F64, + /// + LLUUID, + /// + BOOL, + /// + LLVector3, + /// + LLVector3d, + /// + LLVector4, + /// + LLQuaternion, + /// + IPADDR, + /// + IPPORT, + /// + Variable, + /// + Fixed, + /// + Single, + /// + Multiple + } + + /// + /// + /// + public class MapField : IComparable + { + /// + public int KeywordPosition; + /// + public string Name; + /// + public FieldType Type; + /// + public int Count; + + /// + /// + /// + /// + /// + public int CompareTo(object obj) + { + MapField temp = (MapField)obj; + + if (this.KeywordPosition > temp.KeywordPosition) + { + return 1; + } + else + { + if(temp.KeywordPosition == this.KeywordPosition) + { + return 0; + } + else + { + return -1; + } + } + } + } + + /// + /// + /// + public class MapBlock : IComparable + { + /// + public int KeywordPosition; + /// + public string Name; + /// + public int Count; + /// + public List Fields; + + /// + /// + /// + /// + /// + public int CompareTo(object obj) + { + MapBlock temp = (MapBlock)obj; + + if (this.KeywordPosition > temp.KeywordPosition) + { + return 1; + } + else + { + if(temp.KeywordPosition == this.KeywordPosition) + { + return 0; + } + else + { + return -1; + } + } + } + } + + /// + /// + /// + public class MapPacket + { + /// + public ushort ID; + /// + public string Name; + /// + public PacketFrequency Frequency; + /// + public bool Trusted; + /// + public bool Encoded; + /// + public List Blocks; + } + + /// + /// + /// + public class ProtocolManager + { + /// + public Dictionary TypeSizes; + /// + public Dictionary KeywordPositions; + /// + public MapPacket[] LowMaps; + /// + public MapPacket[] MediumMaps; + /// + public MapPacket[] HighMaps; + + /// + /// + /// + /// + /// + /// + public ProtocolManager(string mapFile) + { + // Initialize the map arrays + LowMaps = new MapPacket[65536]; + MediumMaps = new MapPacket[256]; + HighMaps = new MapPacket[256]; + + // Build the type size hash table + TypeSizes = new Dictionary(); + TypeSizes.Add(FieldType.U8, 1); + TypeSizes.Add(FieldType.U16, 2); + TypeSizes.Add(FieldType.U32, 4); + TypeSizes.Add(FieldType.U64, 8); + TypeSizes.Add(FieldType.S8, 1); + TypeSizes.Add(FieldType.S16, 2); + TypeSizes.Add(FieldType.S32, 4); + TypeSizes.Add(FieldType.F32, 4); + TypeSizes.Add(FieldType.F64, 8); + TypeSizes.Add(FieldType.LLUUID, 16); + TypeSizes.Add(FieldType.BOOL, 1); + TypeSizes.Add(FieldType.LLVector3, 12); + TypeSizes.Add(FieldType.LLVector3d, 24); + TypeSizes.Add(FieldType.LLVector4, 16); + TypeSizes.Add(FieldType.LLQuaternion, 16); + TypeSizes.Add(FieldType.IPADDR, 4); + TypeSizes.Add(FieldType.IPPORT, 2); + TypeSizes.Add(FieldType.Variable, -1); + TypeSizes.Add(FieldType.Fixed, -2); + + KeywordPositions = new Dictionary(); + LoadMapFile(mapFile); + } + + /// + /// + /// + /// + /// + public MapPacket Command(string command) + { + foreach (MapPacket map in HighMaps) + { + if (map != null) + { + if (command == map.Name) + { + return map; + } + } + } + + foreach (MapPacket map in MediumMaps) + { + if (map != null) + { + if (command == map.Name) + { + return map; + } + } + } + + foreach (MapPacket map in LowMaps) + { + if (map != null) + { + if (command == map.Name) + { + return map; + } + } + } + + throw new Exception("Cannot find map for command \"" + command + "\""); + } + + /// + /// + /// + /// + /// + public MapPacket Command(byte[] data) + { + ushort command; + + if (data.Length < 5) + { + return null; + } + + if (data[4] == 0xFF) + { + if ((byte)data[5] == 0xFF) + { + // Low frequency + command = (ushort)(data[6] * 256 + data[7]); + return Command(command, PacketFrequency.Low); + } + else + { + // Medium frequency + command = (ushort)data[5]; + return Command(command, PacketFrequency.Medium); + } + } + else + { + // High frequency + command = (ushort)data[4]; + return Command(command, PacketFrequency.High); + } + } + + /// + /// + /// + /// + /// + /// + public MapPacket Command(ushort command, PacketFrequency frequency) + { + switch (frequency) + { + case PacketFrequency.High: + return HighMaps[command]; + case PacketFrequency.Medium: + return MediumMaps[command]; + case PacketFrequency.Low: + return LowMaps[command]; + } + + throw new Exception("Cannot find map for command \"" + command + "\" with frequency \"" + frequency + "\""); + } + + /// + /// + /// + public void PrintMap(TextWriter writer) + { + PrintOneMap(writer, LowMaps, "Low "); + PrintOneMap(writer, MediumMaps, "Medium"); + PrintOneMap(writer, HighMaps, "High "); + } + + /// + /// + /// + /// + /// + private void PrintOneMap(TextWriter writer, MapPacket[] map, string frequency) { + int i; + + for (i = 0; i < map.Length; ++i) + { + if (map[i] != null) + { + writer.WriteLine("{0} {1,5} - {2} - {3} - {4}", frequency, i, map[i].Name, + map[i].Trusted ? "Trusted" : "Untrusted", + map[i].Encoded ? "Zerocoded" : "Unencoded"); + + foreach (MapBlock block in map[i].Blocks) + { + if (block.Count == -1) + { + writer.WriteLine("\t{0,4} {1} (Variable)", block.KeywordPosition, block.Name); + } + else + { + writer.WriteLine("\t{0,4} {1} ({2})", block.KeywordPosition, block.Name, block.Count); + } + + foreach (MapField field in block.Fields) + { + writer.WriteLine("\t\t{0,4} {1} ({2} / {3})", field.KeywordPosition, field.Name, + field.Type, field.Count); + } + } + } + } + } + + /// + /// + /// + /// + /// + public static void DecodeMapFile(string mapFile, string outputFile) + { + byte magicKey = 0; + byte[] buffer = new byte[2048]; + int nread; + BinaryReader map; + BinaryWriter output; + + try + { + map = new BinaryReader(new FileStream(mapFile, FileMode.Open)); + } + catch(Exception e) + { + throw new Exception("Map file error", e); + } + + try + { + output = new BinaryWriter(new FileStream(outputFile, FileMode.CreateNew)); + } + catch(Exception e) + { + throw new Exception("Map file error", e); + } + + while ((nread = map.Read(buffer, 0, 2048)) != 0) + { + for (int i = 0; i < nread; ++i) + { + buffer[i] ^= magicKey; + magicKey += 43; + } + + output.Write(buffer, 0, nread); + } + + map.Close(); + output.Close(); + } + + /// + /// + /// + /// + private void LoadMapFile(string mapFile) + { + FileStream map; + + // Load the protocol map file + try + { + map = new FileStream(mapFile, FileMode.Open, FileAccess.Read); + } + catch(Exception e) + { + throw new Exception("Map file error", e); + } + + try + { + StreamReader r = new StreamReader(map); + r.BaseStream.Seek(0, SeekOrigin.Begin); + string newline; + string trimmedline; + bool inPacket = false; + bool inBlock = false; + MapPacket currentPacket = null; + MapBlock currentBlock = null; + char[] trimArray = new char[] {' ', '\t'}; + + // While not at the end of the file + while (r.Peek() > -1) + { + #region ParseMap + + newline = r.ReadLine(); + trimmedline = System.Text.RegularExpressions.Regex.Replace(newline, @"\s+", " "); + trimmedline = trimmedline.Trim(trimArray); + + if (!inPacket) + { + // Outside of all packet blocks + + if (trimmedline == "{") + { + inPacket = true; + } + } + else + { + // Inside of a packet block + + if (!inBlock) + { + // Inside a packet block, outside of the blocks + + if (trimmedline == "{") + { + inBlock = true; + } + else if (trimmedline == "}") + { + // Reached the end of the packet + // currentPacket.Blocks.Sort(); + inPacket = false; + } + else + { + // Skip comments + if (trimmedline.StartsWith("//")) continue; + + // The packet header + #region ParsePacketHeader + + // Splice the string in to tokens + string[] tokens = trimmedline.Split(new char[] {' ', '\t'}); + + if (tokens.Length > 3) + { + //Hash packet name to insure correct keyword ordering + KeywordPosition(tokens[0]); + + uint packetID; + + // Remove the leading "0x" + if (tokens[2].Length > 2 && tokens[2].Substring(0, 2) == "0x") + { + tokens[2] = tokens[2].Substring(2, tokens[2].Length - 2); + packetID = UInt32.Parse(tokens[2], System.Globalization.NumberStyles.HexNumber); + } else { + packetID = UInt32.Parse(tokens[2]); + } + + + if (tokens[1] == "Fixed") + { + + // Truncate the id to a short + packetID &= 0xFFFF; + LowMaps[packetID] = new MapPacket(); + LowMaps[packetID].ID = (ushort)packetID; + LowMaps[packetID].Frequency = PacketFrequency.Low; + LowMaps[packetID].Name = tokens[0]; + LowMaps[packetID].Trusted = (tokens[3] == "Trusted"); + LowMaps[packetID].Encoded = (tokens[4] == "Zerocoded"); + LowMaps[packetID].Blocks = new List(); + + currentPacket = LowMaps[packetID]; + } + else if (tokens[1] == "Low") + { + LowMaps[packetID] = new MapPacket(); + LowMaps[packetID].ID = (ushort)packetID; + LowMaps[packetID].Frequency = PacketFrequency.Low; + LowMaps[packetID].Name = tokens[0]; + LowMaps[packetID].Trusted = (tokens[2] == "Trusted"); + LowMaps[packetID].Encoded = (tokens[4] == "Zerocoded"); + LowMaps[packetID].Blocks = new List(); + + currentPacket = LowMaps[packetID]; + + } + else if (tokens[1] == "Medium") + { + MediumMaps[packetID] = new MapPacket(); + MediumMaps[packetID].ID = (ushort)packetID; + MediumMaps[packetID].Frequency = PacketFrequency.Medium; + MediumMaps[packetID].Name = tokens[0]; + MediumMaps[packetID].Trusted = (tokens[2] == "Trusted"); + MediumMaps[packetID].Encoded = (tokens[4] == "Zerocoded"); + MediumMaps[packetID].Blocks = new List(); + + currentPacket = MediumMaps[packetID]; + + } + else if (tokens[1] == "High") + { + HighMaps[packetID] = new MapPacket(); + HighMaps[packetID].ID = (ushort)packetID; + HighMaps[packetID].Frequency = PacketFrequency.High; + HighMaps[packetID].Name = tokens[0]; + HighMaps[packetID].Trusted = (tokens[2] == "Trusted"); + HighMaps[packetID].Encoded = (tokens[4] == "Zerocoded"); + HighMaps[packetID].Blocks = new List(); + + currentPacket = HighMaps[packetID]; + + } + else + { + //Client.Log("Unknown packet frequency", Helpers.LogLevel.Error); + throw new Exception("Unknown packet frequency"); + } + } + + #endregion + } + } + else + { + if (trimmedline.Length > 0 && trimmedline.Substring(0, 1) == "{") + { + // A field + #region ParseField + + MapField field = new MapField(); + + // Splice the string in to tokens + string[] tokens = trimmedline.Split(new char[] {' ', '\t'}); + + field.Name = tokens[1]; + field.KeywordPosition = KeywordPosition(field.Name); + field.Type = (FieldType)Enum.Parse(typeof(FieldType), tokens[2], true); + + if (tokens[3] != "}") + { + field.Count = Int32.Parse(tokens[3]); + } + else + { + field.Count = 1; + } + + // Save this field to the current block + currentBlock.Fields.Add(field); + + #endregion + } + else if (trimmedline == "}") + { + // currentBlock.Fields.Sort(); + inBlock = false; + } + else if (trimmedline.Length != 0 && trimmedline.Substring(0, 2) != "//") + { + // The block header + #region ParseBlockHeader + + currentBlock = new MapBlock(); + + // Splice the string in to tokens + string[] tokens = trimmedline.Split(new char[] {' ', '\t'}); + + currentBlock.Name = tokens[0]; + currentBlock.KeywordPosition = KeywordPosition(currentBlock.Name); + currentBlock.Fields = new List(); + currentPacket.Blocks.Add(currentBlock); + + if (tokens[1] == "Single") + { + currentBlock.Count = 1; + } + else if (tokens[1] == "Multiple") + { + currentBlock.Count = Int32.Parse(tokens[2]); + } + else if (tokens[1] == "Variable") + { + currentBlock.Count = -1; + } + else + { + //Client.Log("Unknown block frequency", Helpers.LogLevel.Error); + throw new Exception("Unknown block frequency"); + } + + #endregion + } + } + } + + #endregion + } + + r.Close(); + map.Close(); + } + catch (Exception e) + { + throw e; + } + } + + private int KeywordPosition(string keyword) + { + if (KeywordPositions.ContainsKey(keyword)) + { + return KeywordPositions[keyword]; + } + + int hash = 0; + for (int i = 1; i < keyword.Length; i++) + { + hash = (hash + (int)(keyword[i])) * 2; + } + hash *= 2; + hash &= 0x1FFF; + + int startHash = hash; + + while (KeywordPositions.ContainsValue(hash)) + { + hash++; + hash &= 0x1FFF; + if (hash == startHash) + { + //Give up looking, went through all values and they were all taken. + throw new Exception("All hash values are taken. Failed to add keyword: " + keyword); + } + } + + KeywordPositions[keyword] = hash; + return hash; + } + } +} diff --git a/Programs/mapgenerator/mapgenerator.cs b/Programs/mapgenerator/mapgenerator.cs new file mode 100644 index 00000000..185e2feb --- /dev/null +++ b/Programs/mapgenerator/mapgenerator.cs @@ -0,0 +1,1129 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace mapgenerator +{ + class mapgenerator + { + static void WriteFieldMember(TextWriter writer, MapField field) + { + string type = String.Empty; + + switch (field.Type) + { + case FieldType.BOOL: + type = "bool"; + break; + case FieldType.F32: + type = "float"; + break; + case FieldType.F64: + type = "double"; + break; + case FieldType.IPPORT: + case FieldType.U16: + type = "ushort"; + break; + case FieldType.IPADDR: + case FieldType.U32: + type = "uint"; + break; + case FieldType.LLQuaternion: + type = "Quaternion"; + break; + case FieldType.LLUUID: + type = "UUID"; + break; + case FieldType.LLVector3: + type = "Vector3"; + break; + case FieldType.LLVector3d: + type = "Vector3d"; + break; + case FieldType.LLVector4: + type = "Vector4"; + break; + case FieldType.S16: + type = "short"; + break; + case FieldType.S32: + type = "int"; + break; + case FieldType.S8: + type = "sbyte"; + break; + case FieldType.U64: + type = "ulong"; + break; + case FieldType.U8: + type = "byte"; + break; + case FieldType.Fixed: + type = "byte[]"; + break; + } + if (field.Type != FieldType.Variable) + { + //writer.WriteLine(" /// " + field.Name + " field"); + writer.WriteLine(" public " + type + " " + field.Name + ";"); + } + else + { + writer.WriteLine(" public byte[] " + field.Name + ";"); + + //writer.WriteLine(" private byte[] _" + field.Name.ToLower() + ";"); + ////writer.WriteLine(" /// " + field.Name + " field"); + //writer.WriteLine(" public byte[] " + field.Name + Environment.NewLine + " {"); + //writer.WriteLine(" get { return _" + field.Name.ToLower() + "; }"); + //writer.WriteLine(" set" + Environment.NewLine + " {"); + //writer.WriteLine(" if (value == null) { _" + + // field.Name.ToLower() + " = null; return; }"); + //writer.WriteLine(" if (value.Length > " + + // ((field.Count == 1) ? "255" : "1100") + ") { throw new OverflowException(" + + // "\"Value exceeds " + ((field.Count == 1) ? "255" : "1100") + " characters\"); }"); + //writer.WriteLine(" else { _" + field.Name.ToLower() + + // " = new byte[value.Length]; Buffer.BlockCopy(value, 0, _" + + // field.Name.ToLower() + ", 0, value.Length); }"); + //writer.WriteLine(" }" + Environment.NewLine + " }"); + } + } + + static void WriteFieldFromBytes(TextWriter writer, MapField field) + { + switch (field.Type) + { + case FieldType.BOOL: + writer.WriteLine(" " + + field.Name + " = (bytes[i++] != 0) ? (bool)true : (bool)false;"); + break; + case FieldType.F32: + writer.WriteLine(" " + + field.Name + " = Utils.BytesToFloat(bytes, i); i += 4;"); + break; + case FieldType.F64: + writer.WriteLine(" " + + field.Name + " = Utils.BytesToDouble(bytes, i); i += 8;"); + break; + case FieldType.Fixed: + writer.WriteLine(" " + field.Name + " = new byte[" + field.Count + "];"); + writer.WriteLine(" Buffer.BlockCopy(bytes, i, " + field.Name + + ", 0, " + field.Count + "); i += " + field.Count + ";"); + break; + case FieldType.IPADDR: + case FieldType.U32: + writer.WriteLine(" " + field.Name + + " = (uint)(bytes[i++] + (bytes[i++] << 8) + (bytes[i++] << 16) + (bytes[i++] << 24));"); + break; + case FieldType.IPPORT: + // IPPORT is big endian while U16/S16 are little endian. Go figure + writer.WriteLine(" " + field.Name + + " = (ushort)((bytes[i++] << 8) + bytes[i++]);"); + break; + case FieldType.U16: + writer.WriteLine(" " + field.Name + + " = (ushort)(bytes[i++] + (bytes[i++] << 8));"); + break; + case FieldType.LLQuaternion: + writer.WriteLine(" " + field.Name + ".FromBytes(bytes, i, true); i += 12;"); + break; + case FieldType.LLUUID: + writer.WriteLine(" " + field.Name + ".FromBytes(bytes, i); i += 16;"); + break; + case FieldType.LLVector3: + writer.WriteLine(" " + field.Name + ".FromBytes(bytes, i); i += 12;"); + break; + case FieldType.LLVector3d: + writer.WriteLine(" " + field.Name + ".FromBytes(bytes, i); i += 24;"); + break; + case FieldType.LLVector4: + writer.WriteLine(" " + field.Name + ".FromBytes(bytes, i); i += 16;"); + break; + case FieldType.S16: + writer.WriteLine(" " + field.Name + + " = (short)(bytes[i++] + (bytes[i++] << 8));"); + break; + case FieldType.S32: + writer.WriteLine(" " + field.Name + + " = (int)(bytes[i++] + (bytes[i++] << 8) + (bytes[i++] << 16) + (bytes[i++] << 24));"); + break; + case FieldType.S8: + writer.WriteLine(" " + field.Name + + " = (sbyte)bytes[i++];"); + break; + case FieldType.U64: + writer.WriteLine(" " + field.Name + + " = (ulong)((ulong)bytes[i++] + ((ulong)bytes[i++] << 8) + " + + "((ulong)bytes[i++] << 16) + ((ulong)bytes[i++] << 24) + " + + "((ulong)bytes[i++] << 32) + ((ulong)bytes[i++] << 40) + " + + "((ulong)bytes[i++] << 48) + ((ulong)bytes[i++] << 56));"); + break; + case FieldType.U8: + writer.WriteLine(" " + field.Name + + " = (byte)bytes[i++];"); + break; + case FieldType.Variable: + if (field.Count == 1) + { + writer.WriteLine(" length = bytes[i++];"); + } + else + { + writer.WriteLine(" length = (bytes[i++] + (bytes[i++] << 8));"); + } + writer.WriteLine(" " + field.Name + " = new byte[length];"); + writer.WriteLine(" Buffer.BlockCopy(bytes, i, " + field.Name + ", 0, length); i += length;"); + break; + default: + writer.WriteLine("!!! ERROR: Unhandled FieldType: " + field.Type.ToString() + " !!!"); + break; + } + } + + static void WriteFieldToBytes(TextWriter writer, MapField field) + { + writer.Write(" "); + + switch (field.Type) + { + case FieldType.BOOL: + writer.WriteLine("bytes[i++] = (byte)((" + field.Name + ") ? 1 : 0);"); + break; + case FieldType.F32: + writer.WriteLine("Utils.FloatToBytes(" + field.Name + ", bytes, i); i += 4;"); + break; + case FieldType.F64: + writer.WriteLine("Utils.DoubleToBytes(" + field.Name + ", bytes, i); i += 8;"); + break; + case FieldType.Fixed: + writer.WriteLine("Buffer.BlockCopy(" + field.Name + ", 0, bytes, i, " + field.Count + ");" + + "i += " + field.Count + ";"); + break; + case FieldType.IPPORT: + // IPPORT is big endian while U16/S16 is little endian. Go figure + writer.WriteLine("bytes[i++] = (byte)((" + field.Name + " >> 8) % 256);"); + writer.WriteLine(" bytes[i++] = (byte)(" + field.Name + " % 256);"); + break; + case FieldType.U16: + case FieldType.S16: + writer.WriteLine("bytes[i++] = (byte)(" + field.Name + " % 256);"); + writer.WriteLine(" bytes[i++] = (byte)((" + field.Name + " >> 8) % 256);"); + break; + case FieldType.LLQuaternion: + case FieldType.LLVector3: + writer.WriteLine(field.Name + ".ToBytes(bytes, i); i += 12;"); + break; + case FieldType.LLUUID: + case FieldType.LLVector4: + writer.WriteLine(field.Name + ".ToBytes(bytes, i); i += 16;"); + break; + case FieldType.LLVector3d: + writer.WriteLine(field.Name + ".ToBytes(bytes, i); i += 24;"); + break; + case FieldType.U8: + writer.WriteLine("bytes[i++] = " + field.Name + ";"); + break; + case FieldType.S8: + writer.WriteLine("bytes[i++] = (byte)" + field.Name + ";"); + break; + case FieldType.IPADDR: + case FieldType.U32: + writer.WriteLine("Utils.UIntToBytes(" + field.Name + ", bytes, i); i += 4;"); + break; + case FieldType.S32: + writer.WriteLine("Utils.IntToBytes(" + field.Name + ", bytes, i); i += 4;"); + break; + case FieldType.U64: + writer.WriteLine("Utils.UInt64ToBytes(" + field.Name + ", bytes, i); i += 8;"); + break; + case FieldType.Variable: + //writer.WriteLine("if(" + field.Name + " == null) { Console.WriteLine(\"Warning: " + field.Name + " is null, in \" + this.GetType()); }"); + //writer.Write(" "); + if (field.Count == 1) + { + writer.WriteLine("bytes[i++] = (byte)" + field.Name + ".Length;"); + } + else + { + writer.WriteLine("bytes[i++] = (byte)(" + field.Name + ".Length % 256);"); + writer.WriteLine(" bytes[i++] = (byte)((" + + field.Name + ".Length >> 8) % 256);"); + } + writer.WriteLine(" Buffer.BlockCopy(" + field.Name + ", 0, bytes, i, " + + field.Name + ".Length); " + "i += " + field.Name + ".Length;"); + break; + default: + writer.WriteLine("!!! ERROR: Unhandled FieldType: " + field.Type.ToString() + " !!!"); + break; + } + } + + static int GetFieldLength(TextWriter writer, MapField field) + { + switch (field.Type) + { + case FieldType.BOOL: + case FieldType.U8: + case FieldType.S8: + return 1; + case FieldType.U16: + case FieldType.S16: + case FieldType.IPPORT: + return 2; + case FieldType.U32: + case FieldType.S32: + case FieldType.F32: + case FieldType.IPADDR: + return 4; + case FieldType.U64: + case FieldType.F64: + return 8; + case FieldType.LLVector3: + case FieldType.LLQuaternion: + return 12; + case FieldType.LLUUID: + case FieldType.LLVector4: + return 16; + case FieldType.LLVector3d: + return 24; + case FieldType.Fixed: + return field.Count; + case FieldType.Variable: + return 0; + default: + writer.WriteLine("!!! ERROR: Unhandled FieldType " + field.Type.ToString() + " !!!"); + return 0; + } + } + + static void WriteBlockClass(TextWriter writer, MapBlock block, MapPacket packet) + { + int variableFieldCountBytes = 0; + + //writer.WriteLine(" /// " + block.Name + " block"); + writer.WriteLine(" /// "); + writer.WriteLine(" public sealed class " + block.Name + "Block : PacketBlock" + Environment.NewLine + " {"); + + foreach (MapField field in block.Fields) + { + WriteFieldMember(writer, field); + if (field.Type == FieldType.Variable) { variableFieldCountBytes += field.Count; } + } + + // Length property + writer.WriteLine(""); + //writer.WriteLine(" /// Length of this block serialized in bytes"); + writer.WriteLine(" public override int Length" + Environment.NewLine + + " {" + Environment.NewLine + + " get" + Environment.NewLine + + " {"); + int length = variableFieldCountBytes; + + // Figure out the length of this block + foreach (MapField field in block.Fields) + { + length += GetFieldLength(writer, field); + } + + if (variableFieldCountBytes == 0) + { + writer.WriteLine(" return " + length + ";"); + } + else + { + writer.WriteLine(" int length = " + length + ";"); + + foreach (MapField field in block.Fields) + { + if (field.Type == FieldType.Variable) + { + writer.WriteLine(" if (" + field.Name + + " != null) { length += " + field.Name + ".Length; }"); + } + } + + writer.WriteLine(" return length;"); + } + + writer.WriteLine(" }" + Environment.NewLine + " }" + Environment.NewLine); + + // Default constructor + //writer.WriteLine(" /// Default constructor"); + writer.WriteLine(" public " + block.Name + "Block() { }"); + + // Constructor for building the class from bytes + //writer.WriteLine(" /// Constructor for building the block from a byte array"); + writer.WriteLine(" public " + block.Name + "Block(byte[] bytes, ref int i)" + Environment.NewLine + + " {" + Environment.NewLine + + " FromBytes(bytes, ref i);" + Environment.NewLine + + " }" + Environment.NewLine); + + // Initiates instance variables from a byte message + writer.WriteLine(" public override void FromBytes(byte[] bytes, ref int i)" + Environment.NewLine + + " {"); + + // Declare a length variable if we need it for variable fields in this constructor + if (variableFieldCountBytes > 0) { writer.WriteLine(" int length;"); } + + // Start of the try catch block + writer.WriteLine(" try" + Environment.NewLine + " {"); + + foreach (MapField field in block.Fields) + { + WriteFieldFromBytes(writer, field); + } + + writer.WriteLine(" }" + Environment.NewLine + + " catch (Exception)" + Environment.NewLine + + " {" + Environment.NewLine + + " throw new MalformedDataException();" + Environment.NewLine + + " }" + Environment.NewLine + " }" + Environment.NewLine); + + // ToBytes() function + //writer.WriteLine(" /// Serialize this block to a byte array"); + writer.WriteLine(" public override void ToBytes(byte[] bytes, ref int i)" + Environment.NewLine + + " {"); + + foreach (MapField field in block.Fields) + { + WriteFieldToBytes(writer, field); + } + + writer.WriteLine(" }" + Environment.NewLine); + writer.WriteLine(" }" + Environment.NewLine); + } + + static void WritePacketClass(TextWriter writer, MapPacket packet) + { + bool hasVariableBlocks = false; + string sanitizedName; + + //writer.WriteLine(" /// " + packet.Name + " packet"); + writer.WriteLine(" /// "); + writer.WriteLine(" public sealed class " + packet.Name + "Packet : Packet" + Environment.NewLine + " {"); + + // Write out each block class + foreach (MapBlock block in packet.Blocks) + { + WriteBlockClass(writer, block, packet); + } + + // Length member + writer.WriteLine(" public override int Length" + Environment.NewLine + + " {" + Environment.NewLine + " get" + Environment.NewLine + + " {"); + + int length = 0; + if (packet.Frequency == PacketFrequency.Low) { length = 10; } + else if (packet.Frequency == PacketFrequency.Medium) { length = 8; } + else { length = 7; } + + foreach (MapBlock block in packet.Blocks) + { + if (block.Count == -1) + { + hasVariableBlocks = true; + ++length; + } + } + + writer.WriteLine(" int length = " + length + ";"); + + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + // Variable count block + writer.WriteLine(" for (int j = 0; j < " + sanitizedName + ".Length; j++)"); + writer.WriteLine(" length += " + sanitizedName + "[j].Length;"); + } + else if (block.Count == 1) + { + writer.WriteLine(" length += " + sanitizedName + ".Length;"); + } + else + { + // Multiple count block + writer.WriteLine(" for (int j = 0; j < " + block.Count + "; j++)"); + writer.WriteLine(" length += " + sanitizedName + "[j].Length;"); + } + } + writer.WriteLine(" return length;"); + writer.WriteLine(" }" + Environment.NewLine + " }"); + + // Block members + foreach (MapBlock block in packet.Blocks) + { + // TODO: More thorough name blacklisting + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + //writer.WriteLine(" /// " + block.Name + " block"); + writer.WriteLine(" public " + block.Name + "Block" + + ((block.Count != 1) ? "[]" : "") + " " + sanitizedName + ";"); + } + + writer.WriteLine(""); + + // Default constructor + //writer.WriteLine(" /// Default constructor"); + writer.WriteLine(" public " + packet.Name + "Packet()" + Environment.NewLine + " {"); + writer.WriteLine(" HasVariableBlocks = " + hasVariableBlocks.ToString().ToLowerInvariant() + ";"); + writer.WriteLine(" Type = PacketType." + packet.Name + ";"); + writer.WriteLine(" Header = new Header();"); + writer.WriteLine(" Header.Frequency = PacketFrequency." + packet.Frequency + ";"); + writer.WriteLine(" Header.ID = " + packet.ID + ";"); + writer.WriteLine(" Header.Reliable = true;"); // Turn the reliable flag on by default + if (packet.Encoded) { writer.WriteLine(" Header.Zerocoded = true;"); } + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == 1) + { + // Single count block + writer.WriteLine(" " + sanitizedName + " = new " + block.Name + "Block();"); + } + else if (block.Count == -1) + { + // Variable count block + writer.WriteLine(" " + sanitizedName + " = null;"); + } + else + { + // Multiple count block + writer.WriteLine(" " + sanitizedName + " = new " + block.Name + "Block[" + block.Count + "];"); + } + } + writer.WriteLine(" }" + Environment.NewLine); + + // Constructor that takes a byte array and beginning position only (no prebuilt header) + bool seenVariable = false; + //writer.WriteLine(" /// Constructor that takes a byte array and beginning position (no prebuilt header)"); + writer.WriteLine(" public " + packet.Name + "Packet(byte[] bytes, ref int i) : this()" + Environment.NewLine + + " {" + Environment.NewLine + + " int packetEnd = bytes.Length - 1;" + Environment.NewLine + + " FromBytes(bytes, ref i, ref packetEnd, null);" + Environment.NewLine + + " }" + Environment.NewLine); + + writer.WriteLine(" override public void FromBytes(byte[] bytes, ref int i, ref int packetEnd, byte[] zeroBuffer)" + Environment.NewLine + " {"); + writer.WriteLine(" Header.FromBytes(bytes, ref i, ref packetEnd);"); + writer.WriteLine(" if (Header.Zerocoded && zeroBuffer != null)"); + writer.WriteLine(" {"); + writer.WriteLine(" packetEnd = Helpers.ZeroDecode(bytes, packetEnd + 1, zeroBuffer) - 1;"); + writer.WriteLine(" bytes = zeroBuffer;"); + writer.WriteLine(" }"); + + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == 1) + { + // Single count block + writer.WriteLine(" " + sanitizedName + ".FromBytes(bytes, ref i);"); + } + else if (block.Count == -1) + { + // Variable count block + if (!seenVariable) + { + writer.WriteLine(" int count = (int)bytes[i++];"); + seenVariable = true; + } + else + { + writer.WriteLine(" count = (int)bytes[i++];"); + } + writer.WriteLine(" if(" + sanitizedName + " == null || " + sanitizedName + ".Length != " + block.Count + ") {"); + writer.WriteLine(" " + sanitizedName + " = new " + block.Name + "Block[count];"); + writer.WriteLine(" for(int j = 0; j < count; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j] = new " + block.Name + "Block(); }"); + writer.WriteLine(" }"); + writer.WriteLine(" for (int j = 0; j < count; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j].FromBytes(bytes, ref i); }"); + } + else + { + // Multiple count block + writer.WriteLine(" if(" + sanitizedName + " == null || " + sanitizedName + ".Length != " + block.Count + ") {"); + writer.WriteLine(" " + sanitizedName + " = new " + block.Name + "Block[" + block.Count + "];"); + writer.WriteLine(" for(int j = 0; j < " + block.Count + "; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j] = new " + block.Name + "Block(); }"); + writer.WriteLine(" }"); + writer.WriteLine(" for (int j = 0; j < " + block.Count + "; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j].FromBytes(bytes, ref i); }"); + } + } + writer.WriteLine(" }" + Environment.NewLine); + + seenVariable = false; + + // Constructor that takes a byte array and a prebuilt header + //writer.WriteLine(" /// Constructor that takes a byte array and a prebuilt header"); + writer.WriteLine(" public " + packet.Name + "Packet(Header head, byte[] bytes, ref int i): this()" + Environment.NewLine + + " {" + Environment.NewLine + + " int packetEnd = bytes.Length - 1;" + Environment.NewLine + + " FromBytes(head, bytes, ref i, ref packetEnd);" + Environment.NewLine + + " }" + Environment.NewLine); + + writer.WriteLine(" override public void FromBytes(Header header, byte[] bytes, ref int i, ref int packetEnd)" + Environment.NewLine + + " {"); + writer.WriteLine(" Header = header;"); + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == 1) + { + // Single count block + writer.WriteLine(" " + sanitizedName + ".FromBytes(bytes, ref i);"); + } + else if (block.Count == -1) + { + // Variable count block + if (!seenVariable) + { + writer.WriteLine(" int count = (int)bytes[i++];"); + seenVariable = true; + } + else + { + writer.WriteLine(" count = (int)bytes[i++];"); + } + writer.WriteLine(" if(" + sanitizedName + " == null || " + sanitizedName + ".Length != count) {"); + writer.WriteLine(" " + sanitizedName + " = new " + block.Name + "Block[count];"); + writer.WriteLine(" for(int j = 0; j < count; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j] = new " + block.Name + "Block(); }"); + writer.WriteLine(" }"); + writer.WriteLine(" for (int j = 0; j < count; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j].FromBytes(bytes, ref i); }"); + } + else + { + // Multiple count block + writer.WriteLine(" if(" + sanitizedName + " == null || " + sanitizedName + ".Length != " + block.Count + ") {"); + writer.WriteLine(" " + sanitizedName + " = new " + block.Name + "Block[" + block.Count + "];"); + writer.WriteLine(" for(int j = 0; j < " + block.Count + "; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j] = new " + block.Name + "Block(); }"); + writer.WriteLine(" }"); + writer.WriteLine(" for (int j = 0; j < " + block.Count + "; j++)"); + writer.WriteLine(" { " + sanitizedName + "[j].FromBytes(bytes, ref i); }"); + } + } + writer.WriteLine(" }" + Environment.NewLine); + + #region ToBytes() Function + + //writer.WriteLine(" /// Serialize this packet to a byte arrayA byte array containing the serialized packet"); + writer.WriteLine(" public override byte[] ToBytes()" + Environment.NewLine + " {"); + + writer.Write(" int length = "); + if (packet.Frequency == PacketFrequency.Low) { writer.WriteLine("10;"); } + else if (packet.Frequency == PacketFrequency.Medium) { writer.WriteLine("8;"); } + else { writer.WriteLine("7;"); } + + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == 1) + { + // Single count block + writer.WriteLine(" length += " + sanitizedName + ".Length;"); + } + } + + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + writer.WriteLine(" length++;"); + writer.WriteLine(" for (int j = 0; j < " + sanitizedName + + ".Length; j++) { length += " + sanitizedName + "[j].Length; }"); + } + else if (block.Count > 1) + { + writer.WriteLine(" for (int j = 0; j < " + block.Count + + "; j++) { length += " + sanitizedName + "[j].Length; }"); + } + } + + writer.WriteLine(" if (Header.AckList != null && Header.AckList.Length > 0) { length += Header.AckList.Length * 4 + 1; }"); + writer.WriteLine(" byte[] bytes = new byte[length];"); + writer.WriteLine(" int i = 0;"); + writer.WriteLine(" Header.ToBytes(bytes, ref i);"); + foreach (MapBlock block in packet.Blocks) + { + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + // Variable count block + writer.WriteLine(" bytes[i++] = (byte)" + sanitizedName + ".Length;"); + writer.WriteLine(" for (int j = 0; j < " + sanitizedName + + ".Length; j++) { " + sanitizedName + "[j].ToBytes(bytes, ref i); }"); + } + else if (block.Count == 1) + { + writer.WriteLine(" " + sanitizedName + ".ToBytes(bytes, ref i);"); + } + else + { + // Multiple count block + writer.WriteLine(" for (int j = 0; j < " + block.Count + + "; j++) { " + sanitizedName + "[j].ToBytes(bytes, ref i); }"); + } + } + + writer.WriteLine(" if (Header.AckList != null && Header.AckList.Length > 0) { Header.AcksToBytes(bytes, ref i); }"); + writer.WriteLine(" return bytes;" + Environment.NewLine + " }" + Environment.NewLine); + + #endregion ToBytes() Function + + WriteToBytesMultiple(writer, packet); + + writer.WriteLine(" }" + Environment.NewLine); + } + + static void WriteToBytesMultiple(TextWriter writer, MapPacket packet) + { + writer.WriteLine( + " public override byte[][] ToBytesMultiple()" + Environment.NewLine + + " {"); + + // Check if there are any variable blocks + bool hasVariable = false; + bool cannotSplit = false; + foreach (MapBlock block in packet.Blocks) + { + if (block.Count == -1) + { + hasVariable = true; + } + else if (hasVariable) + { + // A fixed or single block showed up after a variable count block. + // Our automatic splitting algorithm won't work for this packet + cannotSplit = true; + break; + } + } + + if (hasVariable && !cannotSplit) + { + writer.WriteLine( + " System.Collections.Generic.List packets = new System.Collections.Generic.List();"); + writer.WriteLine( + " int i = 0;"); + writer.Write( + " int fixedLength = "); + if (packet.Frequency == PacketFrequency.Low) { writer.WriteLine("10;"); } + else if (packet.Frequency == PacketFrequency.Medium) { writer.WriteLine("8;"); } + else { writer.WriteLine("7;"); } + writer.WriteLine(); + + // ACK serialization + writer.WriteLine(" byte[] ackBytes = null;"); + writer.WriteLine(" int acksLength = 0;"); + writer.WriteLine(" if (Header.AckList != null && Header.AckList.Length > 0) {"); + writer.WriteLine(" Header.AppendedAcks = true;"); + writer.WriteLine(" ackBytes = new byte[Header.AckList.Length * 4 + 1];"); + writer.WriteLine(" Header.AcksToBytes(ackBytes, ref acksLength);"); + writer.WriteLine(" }"); + writer.WriteLine(); + + // Count fixed blocks + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == 1) + { + // Single count block + writer.WriteLine(" fixedLength += " + sanitizedName + ".Length;"); + } + else if (block.Count > 0) + { + // Fixed count block + writer.WriteLine(" for (int j = 0; j < " + block.Count + "; j++) { fixedLength += " + sanitizedName + "[j].Length; }"); + } + } + + // Serialize fixed blocks + writer.WriteLine( + " byte[] fixedBytes = new byte[fixedLength];"); + writer.WriteLine( + " Header.ToBytes(fixedBytes, ref i);"); + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == 1) + { + // Single count block + writer.WriteLine(" " + sanitizedName + ".ToBytes(fixedBytes, ref i);"); + } + else if (block.Count > 0) + { + // Fixed count block + writer.WriteLine(" for (int j = 0; j < " + block.Count + "; j++) { " + sanitizedName + "[j].ToBytes(fixedBytes, ref i); }"); + } + } + + int variableCountBlock = 0; + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + // Variable count block + ++variableCountBlock; + } + } + writer.WriteLine(" fixedLength += " + variableCountBlock + ";"); + writer.WriteLine(); + + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + // Variable count block + writer.WriteLine(" int " + sanitizedName + "Start = 0;"); + } + } + + writer.WriteLine(" do"); + writer.WriteLine(" {"); + + // Count how many variable blocks can go in this packet + writer.WriteLine(" int variableLength = 0;"); + + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + // Variable count block + writer.WriteLine(" int " + sanitizedName + "Count = 0;"); + } + } + writer.WriteLine(); + + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + // Variable count block + writer.WriteLine(" i = " + sanitizedName + "Start;"); + writer.WriteLine(" while (fixedLength + variableLength + acksLength < Packet.MTU && i < " + sanitizedName + ".Length) {"); + writer.WriteLine(" int blockLength = " + sanitizedName + "[i].Length;"); + writer.WriteLine(" if (fixedLength + variableLength + blockLength + acksLength <= MTU || i == " + sanitizedName + "Start) {"); + writer.WriteLine(" variableLength += blockLength;"); + writer.WriteLine(" ++" + sanitizedName + "Count;"); + writer.WriteLine(" }"); + writer.WriteLine(" else { break; }"); + writer.WriteLine(" ++i;"); + writer.WriteLine(" }"); + writer.WriteLine(); + } + } + + // Create the packet + writer.WriteLine(" byte[] packet = new byte[fixedLength + variableLength + acksLength];"); + writer.WriteLine(" int length = fixedBytes.Length;"); + writer.WriteLine(" Buffer.BlockCopy(fixedBytes, 0, packet, 0, length);"); + // Remove the appended ACKs flag from subsequent packets + writer.WriteLine(" if (packets.Count > 0) { packet[0] = (byte)(packet[0] & ~0x10); }"); + writer.WriteLine(); + + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + writer.WriteLine(" packet[length++] = (byte)" + sanitizedName + "Count;"); + writer.WriteLine(" for (i = " + sanitizedName + "Start; i < " + sanitizedName + "Start + " + + sanitizedName + "Count; i++) { " + sanitizedName + "[i].ToBytes(packet, ref length); }"); + writer.WriteLine(" " + sanitizedName + "Start += " + sanitizedName + "Count;"); + writer.WriteLine(); + } + } + + // ACK appending + writer.WriteLine(" if (acksLength > 0) {"); + writer.WriteLine(" Buffer.BlockCopy(ackBytes, 0, packet, length, acksLength);"); + writer.WriteLine(" acksLength = 0;"); + writer.WriteLine(" }"); + writer.WriteLine(); + + writer.WriteLine(" packets.Add(packet);"); + + writer.WriteLine(" } while ("); + bool first = true; + foreach (MapBlock block in packet.Blocks) + { + string sanitizedName; + if (block.Name == "Header") { sanitizedName = "_" + block.Name; } + else { sanitizedName = block.Name; } + + if (block.Count == -1) + { + if (first) first = false; + else writer.WriteLine(" ||"); + + // Variable count block + writer.Write(" " + sanitizedName + "Start < " + sanitizedName + ".Length"); + } + } + writer.WriteLine(");"); + writer.WriteLine(); + writer.WriteLine(" return packets.ToArray();"); + writer.WriteLine(" }"); + } + else + { + writer.WriteLine(" return new byte[][] { ToBytes() };"); + writer.WriteLine(" }"); + } + } + + static int Main(string[] args) + { + ProtocolManager protocol; + List unused = new List(); + TextWriter writer; + + try + { + if (args.Length != 4) + { + Console.WriteLine("Usage: [message_template.msg] [template.cs] [unusedpackets.txt] [_Packets_.cs]"); + return -1; + } + + writer = new StreamWriter(args[3]); + protocol = new ProtocolManager(args[0]); + + // Build a list of unused packets + using (StreamReader unusedReader = new StreamReader(args[2])) + { + while (unusedReader.Peek() >= 0) + { + unused.Add(unusedReader.ReadLine().Trim()); + } + } + + // Read in the template.cs file and write it to our output + TextReader reader = new StreamReader(args[1]); + writer.WriteLine(reader.ReadToEnd()); + reader.Close(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + return -2; + } + + + // Prune all of the unused packets out of the protocol + int i = 0; + foreach (MapPacket packet in protocol.LowMaps) + { + if (packet != null && unused.Contains(packet.Name)) + protocol.LowMaps[i] = null; + i++; + } + i = 0; + foreach (MapPacket packet in protocol.MediumMaps) + { + if (packet != null && unused.Contains(packet.Name)) + protocol.MediumMaps[i] = null; + i++; + } + i = 0; + foreach (MapPacket packet in protocol.HighMaps) + { + if (packet != null && unused.Contains(packet.Name)) + protocol.HighMaps[i] = null; + i++; + } + + + // Write the PacketType enum + writer.WriteLine(" public enum PacketType" + Environment.NewLine + " {" + Environment.NewLine + + " /// A generic value, not an actual packet type" + Environment.NewLine + + " Default,"); + foreach (MapPacket packet in protocol.LowMaps) + if (packet != null) + writer.WriteLine(" " + packet.Name + " = " + (0x10000 | packet.ID) + ","); + foreach (MapPacket packet in protocol.MediumMaps) + if (packet != null) + writer.WriteLine(" " + packet.Name + " = " + (0x20000 | packet.ID) + ","); + foreach (MapPacket packet in protocol.HighMaps) + if (packet != null) + writer.WriteLine(" " + packet.Name + " = " + (0x30000 | packet.ID) + ","); + writer.WriteLine(" }" + Environment.NewLine); + + // Write the base Packet class + writer.WriteLine( + " public abstract partial class Packet" + Environment.NewLine + " {" + Environment.NewLine + + " public const int MTU = 1200;" + Environment.NewLine + + Environment.NewLine + + " public Header Header;" + Environment.NewLine + + " public bool HasVariableBlocks;" + Environment.NewLine + + " public PacketType Type;" + Environment.NewLine + + " public abstract int Length { get; }" + Environment.NewLine + + " public abstract void FromBytes(byte[] bytes, ref int i, ref int packetEnd, byte[] zeroBuffer);" + Environment.NewLine + + " public abstract void FromBytes(Header header, byte[] bytes, ref int i, ref int packetEnd);" + Environment.NewLine + + " public abstract byte[] ToBytes();" + Environment.NewLine + + " public abstract byte[][] ToBytesMultiple();" + ); + writer.WriteLine(); + + // Write the Packet.GetType() function + writer.WriteLine( + " public static PacketType GetType(ushort id, PacketFrequency frequency)" + Environment.NewLine + + " {" + Environment.NewLine + + " switch (frequency)" + Environment.NewLine + + " {" + Environment.NewLine + + " case PacketFrequency.Low:" + Environment.NewLine + + " switch (id)" + Environment.NewLine + + " {"); + foreach (MapPacket packet in protocol.LowMaps) + if (packet != null) + writer.WriteLine(" case " + packet.ID + ": return PacketType." + packet.Name + ";"); + writer.WriteLine(" }" + Environment.NewLine + + " break;" + Environment.NewLine + + " case PacketFrequency.Medium:" + Environment.NewLine + + " switch (id)" + Environment.NewLine + " {"); + foreach (MapPacket packet in protocol.MediumMaps) + if (packet != null) + writer.WriteLine(" case " + packet.ID + ": return PacketType." + packet.Name + ";"); + writer.WriteLine(" }" + Environment.NewLine + + " break;" + Environment.NewLine + + " case PacketFrequency.High:" + Environment.NewLine + + " switch (id)" + Environment.NewLine + " {"); + foreach (MapPacket packet in protocol.HighMaps) + if (packet != null) + writer.WriteLine(" case " + packet.ID + ": return PacketType." + packet.Name + ";"); + writer.WriteLine(" }" + Environment.NewLine + + " break;" + Environment.NewLine + " }" + Environment.NewLine + Environment.NewLine + + " return PacketType.Default;" + Environment.NewLine + " }" + Environment.NewLine); + + // Write the Packet.BuildPacket(PacketType) function + writer.WriteLine(" public static Packet BuildPacket(PacketType type)"); + writer.WriteLine(" {"); + foreach (MapPacket packet in protocol.HighMaps) + if (packet != null) + writer.WriteLine(" if(type == PacketType." + packet.Name + ") return new " + packet.Name + "Packet();"); + foreach (MapPacket packet in protocol.MediumMaps) + if (packet != null) + writer.WriteLine(" if(type == PacketType." + packet.Name + ") return new " + packet.Name + "Packet();"); + foreach (MapPacket packet in protocol.LowMaps) + if (packet != null) + writer.WriteLine(" if(type == PacketType." + packet.Name + ") return new " + packet.Name + "Packet();"); + writer.WriteLine(" return null;" + Environment.NewLine); + writer.WriteLine(" }"); + + // Write the Packet.BuildPacket() function + writer.WriteLine(@" + public static Packet BuildPacket(byte[] packetBuffer, ref int packetEnd, byte[] zeroBuffer) + { + byte[] bytes; + int i = 0; + Header header = Header.BuildHeader(packetBuffer, ref i, ref packetEnd); + if (header.Zerocoded) + { + packetEnd = Helpers.ZeroDecode(packetBuffer, packetEnd + 1, zeroBuffer) - 1; + bytes = zeroBuffer; + } + else + { + bytes = packetBuffer; + } + Array.Clear(bytes, packetEnd + 1, bytes.Length - packetEnd - 1); + + switch (header.Frequency) + { + case PacketFrequency.Low: + switch (header.ID) + {"); + foreach (MapPacket packet in protocol.LowMaps) + if (packet != null) + writer.WriteLine(" case " + packet.ID + ": return new " + packet.Name + "Packet(header, bytes, ref i);"); + writer.WriteLine(@" + } + break; + case PacketFrequency.Medium: + switch (header.ID) + {"); + foreach (MapPacket packet in protocol.MediumMaps) + if (packet != null) + writer.WriteLine(" case " + packet.ID + ": return new " + packet.Name + "Packet(header, bytes, ref i);"); + writer.WriteLine(@" + } + break; + case PacketFrequency.High: + switch (header.ID) + {"); + foreach (MapPacket packet in protocol.HighMaps) + if (packet != null) + writer.WriteLine(" case " + packet.ID + ": return new " + packet.Name + "Packet(header, bytes, ref i);"); + writer.WriteLine(@" + } + break; + } + + throw new MalformedDataException(""Unknown packet ID "" + header.Frequency + "" "" + header.ID); + } + }"); + + // Write the packet classes + foreach (MapPacket packet in protocol.LowMaps) + if (packet != null) { WritePacketClass(writer, packet); } + foreach (MapPacket packet in protocol.MediumMaps) + if (packet != null) { WritePacketClass(writer, packet); } + foreach (MapPacket packet in protocol.HighMaps) + if (packet != null) { WritePacketClass(writer, packet); } + + + // Finish up + writer.WriteLine("}"); + writer.Close(); + return 0; + } + } +} diff --git a/Programs/mapgenerator/mapgenerator.csproj b/Programs/mapgenerator/mapgenerator.csproj new file mode 100644 index 00000000..e395c73f --- /dev/null +++ b/Programs/mapgenerator/mapgenerator.csproj @@ -0,0 +1,88 @@ + + + Local + 14.0.25123 + 2.0 + {2867B4B3-0000-0000-0000-000000000000} + Debug + + + + mapgenerator + JScript + Grid + IE50 + false + v4.0 + Exe + + mapgenerator + + + + + + + True + 285212672 + False + + + TRACE;DEBUG + + True + 4096 + False + ..\..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + + + TRACE + + False + 4096 + True + ..\..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + + System + False + + + + + + + Code + + + Code + + + + + + + + + + diff --git a/Programs/mapgenerator/template.cs b/Programs/mapgenerator/template.cs new file mode 100644 index 00000000..fb7f3649 --- /dev/null +++ b/Programs/mapgenerator/template.cs @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2006-2016, openmetaverse.co + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the Second Life Reverse Engineering Team nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Text; +using OpenMetaverse; + +namespace OpenMetaverse +{ + /// + /// + /// + public enum PacketFrequency : byte + { + /// + Low, + /// + Medium, + /// + High + } +} + +namespace OpenMetaverse.Packets +{ + /// + /// Thrown when a packet could not be successfully deserialized + /// + public class MalformedDataException : ApplicationException + { + /// + /// Default constructor + /// + public MalformedDataException() { } + + /// + /// Constructor that takes an additional error message + /// + /// An error message to attach to this exception + public MalformedDataException(string Message) + : base(Message) + { + this.Source = "Packet decoding"; + } + } + + /// + /// The header of a message template packet. Holds packet flags, sequence + /// number, packet ID, and any ACKs that will be appended at the end of + /// the packet + /// + public struct Header + { + public bool Reliable; + public bool Resent; + public bool Zerocoded; + public bool AppendedAcks; + public uint Sequence; + public ushort ID; + public PacketFrequency Frequency; + public uint[] AckList; + + public void ToBytes(byte[] bytes, ref int i) + { + byte flags = 0; + if (Reliable) flags |= Helpers.MSG_RELIABLE; + if (Resent) flags |= Helpers.MSG_RESENT; + if (Zerocoded) flags |= Helpers.MSG_ZEROCODED; + if (AppendedAcks) flags |= Helpers.MSG_APPENDED_ACKS; + + // Flags + bytes[i++] = flags; + + // Sequence number + Utils.UIntToBytesBig(Sequence, bytes, i); + i += 4; + + // Extra byte + bytes[i++] = 0; + + // Packet ID + switch (Frequency) + { + case PacketFrequency.High: + // 1 byte ID + bytes[i++] = (byte)ID; + break; + case PacketFrequency.Medium: + // 2 byte ID + bytes[i++] = 0xFF; + bytes[i++] = (byte)ID; + break; + case PacketFrequency.Low: + // 4 byte ID + bytes[i++] = 0xFF; + bytes[i++] = 0xFF; + Utils.UInt16ToBytesBig(ID, bytes, i); + i += 2; + break; + } + } + + public void FromBytes(byte[] bytes, ref int pos, ref int packetEnd) + { + this = BuildHeader(bytes, ref pos, ref packetEnd); + } + + /// + /// Convert the AckList to a byte array, used for packet serializing + /// + /// Reference to the target byte array + /// Beginning position to start writing to in the byte + /// array, will be updated with the ending position of the ACK list + public void AcksToBytes(byte[] bytes, ref int i) + { + foreach (uint ack in AckList) + { + Utils.UIntToBytesBig(ack, bytes, i); + i += 4; + } + if (AckList.Length > 0) { bytes[i++] = (byte)AckList.Length; } + } + + /// + /// + /// + /// + /// + /// + /// + public static Header BuildHeader(byte[] bytes, ref int pos, ref int packetEnd) + { + Header header; + byte flags = bytes[pos]; + + header.AppendedAcks = (flags & Helpers.MSG_APPENDED_ACKS) != 0; + header.Reliable = (flags & Helpers.MSG_RELIABLE) != 0; + header.Resent = (flags & Helpers.MSG_RESENT) != 0; + header.Zerocoded = (flags & Helpers.MSG_ZEROCODED) != 0; + header.Sequence = (uint)((bytes[pos + 1] << 24) + (bytes[pos + 2] << 16) + (bytes[pos + 3] << 8) + bytes[pos + 4]); + + // Set the frequency and packet ID number + if (bytes[pos + 6] == 0xFF) + { + if (bytes[pos + 7] == 0xFF) + { + header.Frequency = PacketFrequency.Low; + if (header.Zerocoded && bytes[pos + 8] == 0) + header.ID = bytes[pos + 10]; + else + header.ID = (ushort)((bytes[pos + 8] << 8) + bytes[pos + 9]); + + pos += 10; + } + else + { + header.Frequency = PacketFrequency.Medium; + header.ID = bytes[pos + 7]; + + pos += 8; + } + } + else + { + header.Frequency = PacketFrequency.High; + header.ID = bytes[pos + 6]; + + pos += 7; + } + + header.AckList = null; + CreateAckList(ref header, bytes, ref packetEnd); + + return header; + } + + /// + /// + /// + /// + /// + /// + static void CreateAckList(ref Header header, byte[] bytes, ref int packetEnd) + { + if (header.AppendedAcks) + { + int count = bytes[packetEnd--]; + header.AckList = new uint[count]; + + for (int i = 0; i < count; i++) + { + header.AckList[i] = (uint)( + (bytes[(packetEnd - i * 4) - 3] << 24) | + (bytes[(packetEnd - i * 4) - 2] << 16) | + (bytes[(packetEnd - i * 4) - 1] << 8) | + (bytes[(packetEnd - i * 4) ])); + } + + packetEnd -= (count * 4); + } + } + } + + /// + /// A block of data in a packet. Packets are composed of one or more blocks, + /// each block containing one or more fields + /// + public abstract class PacketBlock + { + /// Current length of the data in this packet + public abstract int Length { get; } + + /// + /// Create a block from a byte array + /// + /// Byte array containing the serialized block + /// Starting position of the block in the byte array. + /// This will point to the data after the end of the block when the + /// call returns + public abstract void FromBytes(byte[] bytes, ref int i); + + /// + /// Serialize this block into a byte array + /// + /// Byte array to serialize this block into + /// Starting position in the byte array to serialize to. + /// This will point to the position directly after the end of the + /// serialized block when the call returns + public abstract void ToBytes(byte[] bytes, ref int i); + } diff --git a/Programs/mapgenerator/unusedpackets.txt b/Programs/mapgenerator/unusedpackets.txt new file mode 100644 index 00000000..175199e8 --- /dev/null +++ b/Programs/mapgenerator/unusedpackets.txt @@ -0,0 +1,124 @@ +AddCircuitCode +RelayLogControl +NeighborList +SimulatorAssign +SpaceServerSimulatorTimeMessage +ClosestSimulator +AvatarTextureUpdate +SimulatorMapUpdate +SimulatorSetMap +SubscribeLoad +UnsubscribeLoad +SimulatorStart +SimulatorReady +SimulatorPresentAtLocation +SimulatorLoad +SimulatorShutdownRequest +RegionPresenceRequestByRegionID +RegionPresenceRequestByHandle +RegionPresenceResponse +RecordAgentPresence +EraseAgentPresence +AgentPresenceRequest +AgentPresenceResponse +UpdateSimulator +TrackAgentSession +ClearAgentSessions +LogDwellTime +FeatureDisabled +LogFailedMoneyTransaction +UserReportInternal +SetSimStatusInDatabase +SetSimPresenceInDatabase +AvatarPickerRequestBackend +DirFindQueryBackend +DirPlacesQueryBackend +DirClassifiedQueryBackend +DirPicksQueryBackend +DirLandQueryBackend +DirPopularQueryBackend +OnlineStatusRequest +OnlineStatusReply +GroupNoticeAdd +DataHomeLocationRequest +DataHomeLocationReply +SpaceLocationTeleportRequest +SpaceLocationTeleportReply +CompleteLure +AddModifyAbility +RemoveModifyAbility +NearestLandingRegionRequest +NearestLandingRegionReply +NearestLandingPointUpdated +TeleportLandingStatusChanged +SystemKickUser +AvatarPropertiesRequestBackend +RequestParcelTransfer +UpdateParcel +RemoveParcel +MergeParcel +LogParcelChanges +CheckParcelSales +ParcelSales +StartAuction +ConfirmAuctionStart +CompleteAuction +CancelAuction +CheckParcelAuctions +ParcelAuctions +ChatPass +EdgeDataPacket +SimStatus +PassObject +AtomicPassObject +KillChildAgents +LogLogin +DataServerLogout +TransferInventory +TransferInventoryAck +EventLocationRequest +EventLocationReply +MoneyTransferBackend +BulkMoneyTransfer +SetStartLocation +NetTest +SetCPURatio +SimCrashed +NameValuePair +RemoveNameValuePair +GetNameValuePair +UpdateAttachment +RemoveAttachment +EmailMessageRequest +EmailMessageReply +InternalScriptMail +ScriptDataRequest +ScriptDataReply +InviteGroupResponse +TallyVotes +LogTextMessage +StartExpungeProcess +StartExpungeProcessAck +StartParcelRename +StartParcelRenameAck +BulkParcelRename +ParcelRename +StartParcelRemove +BulkParcelRemove +RpcChannelRequest +RpcChannelReply +RpcScriptRequestInbound +RpcScriptRequestInboundForward +RpcScriptReplyInbound +MailTaskSimRequest +MailTaskSimReply +ScriptMailRegistration +NearestLandingRegionUpdated +KickUserAck +RequestInventoryAsset +InventoryAssetResponse +FormFriendship +CreateTrustedCircuit +DenyTrustedCircuit +RequestTrustedCircuit +SystemMessage \ No newline at end of file