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