#region ================== Namespaces using System; using System.Collections.Generic; using System.IO; using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Types; using CodeImp.DoomBuilder.Windows; #endregion namespace CodeImp.DoomBuilder.IO { internal class ClipboardStreamReader { #region ================== Variables private struct SidedefData { public int OffsetX; public int OffsetY; public int SectorID; public string HighTexture; public string MiddleTexture; public string LowTexture; public Dictionary Fields; public Dictionary Flags; } #endregion #region ================== Properties #endregion #region ================== Reading // This reads from a stream public bool Read(MapSet map, Stream stream) { BinaryReader reader = new BinaryReader(stream); //mxd. Sanity checks int numverts = reader.ReadInt32(); if(map.Vertices.Count + numverts >= General.Map.FormatInterface.MaxVertices) { General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste: resulting number of vertices (" + (map.Vertices.Count + numverts) + ") will exceed map format's maximum (" + General.Map.FormatInterface.MaxVertices + ")."); return false; } int numsectors = reader.ReadInt32(); if(map.Sectors.Count + numsectors >= General.Map.FormatInterface.MaxSectors) { General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste: resulting number of sectors (" + (map.Sectors.Count + numsectors) + ") will exceed map format's maximum (" + General.Map.FormatInterface.MaxSectors + ")."); return false; } int numlinedefs = reader.ReadInt32(); if(map.Linedefs.Count + numlinedefs >= General.Map.FormatInterface.MaxLinedefs) { General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste: resulting number of linedefs (" + (map.Linedefs.Count + numlinedefs) + ") will exceed map format's maximum (" + General.Map.FormatInterface.MaxLinedefs + ")."); return false; } int numthings = reader.ReadInt32(); if(map.Things.Count + numthings >= General.Map.FormatInterface.MaxThings) { General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste: resulting number of things (" + (map.Things.Count + numthings) + ") will exceed map format's maximum (" + General.Map.FormatInterface.MaxThings + ")."); return false; } // Read the map Dictionary vertexlink = ReadVertices(map, reader); Dictionary sectorlink = ReadSectors(map, reader); Dictionary sidedeflink = ReadSidedefs(reader); ReadLinedefs(map, reader, vertexlink, sectorlink, sidedeflink); ReadThings(map, reader); return true; } private static Dictionary ReadVertices(MapSet map, BinaryReader reader) { int count = reader.ReadInt32(); // Create lookup table Dictionary link = new Dictionary(count); // Go for all collections map.SetCapacity(map.Vertices.Count + count, 0, 0, 0, 0); for(int i = 0; i < count; i++) { double x = reader.ReadDouble(); double y = reader.ReadDouble(); double zc = reader.ReadDouble(); double zf = reader.ReadDouble(); // Create new item Dictionary fields = ReadCustomFields(reader); Vertex v = map.CreateVertex(new Vector2D(x, y)); if(v != null) { //zoffsets v.ZCeiling = zc; v.ZFloor = zf; // Add custom fields v.Fields.BeforeFieldsChange(); foreach(KeyValuePair group in fields) { v.Fields.Add(group.Key, group.Value); } // Add it to the lookup table link.Add(i, v); } } // Return lookup table return link; } private static Dictionary ReadSectors(MapSet map, BinaryReader reader) { int count = reader.ReadInt32(); // Create lookup table Dictionary link = new Dictionary(count); // Go for all collections map.SetCapacity(0, 0, 0, map.Sectors.Count + count, 0); for(int i = 0; i < count; i++) { int effect = reader.ReadInt32(); int hfloor = reader.ReadInt32(); int hceil = reader.ReadInt32(); int bright = reader.ReadInt32(); //mxd. Tags int numtags = reader.ReadInt32(); //mxd List tags = new List(numtags); //mxd for(int a = 0; a < numtags; a++) tags.Add(reader.ReadInt32()); //mxd string tfloor = ReadString(reader); string tceil = ReadString(reader); //mxd. Slopes double foffset = reader.ReadDouble(); Vector3D fslope = new Vector3D(reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble()); double coffset = reader.ReadDouble(); Vector3D cslope = new Vector3D(reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble()); //flags Dictionary stringflags = new Dictionary(StringComparer.Ordinal); int numFlags = reader.ReadInt32(); for(int f = 0; f < numFlags; f++) stringflags.Add(ReadString(reader), reader.ReadBoolean()); //add missing flags foreach(KeyValuePair flag in General.Map.Config.SectorFlags) { if(stringflags.ContainsKey(flag.Key)) continue; stringflags.Add(flag.Key, false); } foreach(KeyValuePair flag in General.Map.Config.CeilingPortalFlags) { if(stringflags.ContainsKey(flag.Key)) continue; stringflags.Add(flag.Key, false); } foreach(KeyValuePair flag in General.Map.Config.FloorPortalFlags) { if(stringflags.ContainsKey(flag.Key)) continue; stringflags.Add(flag.Key, false); } // Create new item Dictionary fields = ReadCustomFields(reader); Sector s = map.CreateSector(); if(s != null) { s.Update(hfloor, hceil, tfloor, tceil, effect, stringflags, tags, bright, foffset, fslope, coffset, cslope); // Add custom fields s.Fields.BeforeFieldsChange(); foreach(KeyValuePair group in fields) { s.Fields.Add(group.Key, group.Value); } // Add it to the lookup table link.Add(i, s); } } // Return lookup table return link; } // This reads the linedefs and sidedefs private static void ReadLinedefs(MapSet map, BinaryReader reader, Dictionary vertexlink, Dictionary sectorlink, Dictionary sidedeflink) { int count = reader.ReadInt32(); // Go for all lines map.SetCapacity(0, map.Linedefs.Count + count, map.Sidedefs.Count + sidedeflink.Count, 0, 0); for(int i = 0; i < count; i++) { int[] args = new int[Linedef.NUM_ARGS]; int v1 = reader.ReadInt32(); int v2 = reader.ReadInt32(); int s1 = reader.ReadInt32(); int s2 = reader.ReadInt32(); int special = reader.ReadInt32(); for(int a = 0; a < args.Length; a++) args[a] = reader.ReadInt32(); int numtags = reader.ReadInt32(); //mxd List tags = new List(numtags); //mxd for(int a = 0; a < numtags; a++) tags.Add(reader.ReadInt32()); //mxd //flags Dictionary stringflags = new Dictionary(StringComparer.Ordinal); int numFlags = reader.ReadInt32(); for(int f = 0; f < numFlags; f++) stringflags.Add(ReadString(reader), reader.ReadBoolean()); //add missing flags foreach(KeyValuePair flag in General.Map.Config.LinedefFlags) { if(stringflags.ContainsKey(flag.Key)) continue; stringflags.Add(flag.Key, false); } //add missing activations foreach(LinedefActivateInfo activate in General.Map.Config.LinedefActivates) { if(stringflags.ContainsKey(activate.Key)) continue; stringflags.Add(activate.Key, false); } // Read custom fields Dictionary fields = ReadCustomFields(reader); // Check if not zero-length if(Vector2D.ManhattanDistance(vertexlink[v1].Position, vertexlink[v2].Position) > 0.0001f) { // Create new linedef Linedef l = map.CreateLinedef(vertexlink[v1], vertexlink[v2]); if(l != null) { l.Update(stringflags, 0, tags, special, args); l.UpdateCache(); // Add custom fields l.Fields.BeforeFieldsChange(); foreach(KeyValuePair group in fields) { l.Fields.Add(group.Key, group.Value); } // Connect sidedefs to the line if(s1 > -1) { if(s1 < sidedeflink.Count) AddSidedef(map, sidedeflink[s1], l, true, sectorlink); else General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid front sidedef " + s1 + ". Sidedef has been removed."); } if(s2 > -1) { if(s2 < sidedeflink.Count) AddSidedef(map, sidedeflink[s2], l, false, sectorlink); else General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid back sidedef " + s1 + ". Sidedef has been removed."); } } } else { General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " is zero-length. Linedef has been removed."); } } } private static void AddSidedef(MapSet map, SidedefData data, Linedef ld, bool front, Dictionary sectorlink) { // Create sidedef if(sectorlink.ContainsKey(data.SectorID)) { Sidedef s = map.CreateSidedef(ld, front, sectorlink[data.SectorID]); if(s != null) { s.Update(data.OffsetX, data.OffsetY, data.HighTexture, data.MiddleTexture, data.LowTexture, data.Flags); // Add custom fields foreach(KeyValuePair group in data.Fields) { s.Fields.Add(group.Key, group.Value); } } } else { General.ErrorLogger.Add(ErrorType.Warning, "Sidedef references invalid sector " + data.SectorID + ". Sidedef has been removed."); } } private static Dictionary ReadSidedefs(BinaryReader reader) { Dictionary sidedeflink = new Dictionary(); int count = reader.ReadInt32(); for(int i = 0; i < count; i++) { SidedefData data = new SidedefData(); data.OffsetX = reader.ReadInt32(); data.OffsetY = reader.ReadInt32(); data.SectorID = reader.ReadInt32(); data.HighTexture = ReadString(reader); data.MiddleTexture = ReadString(reader); data.LowTexture = ReadString(reader); //flags data.Flags = new Dictionary(StringComparer.Ordinal); int numFlags = reader.ReadInt32(); for(int f = 0; f < numFlags; f++) data.Flags.Add(ReadString(reader), reader.ReadBoolean()); //add missing flags foreach(KeyValuePair flag in General.Map.Config.SidedefFlags) { if(data.Flags.ContainsKey(flag.Key)) continue; data.Flags.Add(flag.Key, false); } //custom fields data.Fields = ReadCustomFields(reader); sidedeflink.Add(i, data); } return sidedeflink; } private static void ReadThings(MapSet map, BinaryReader reader) { int count = reader.ReadInt32(); // Go for all collections map.SetCapacity(0, 0, 0, 0, map.Things.Count + count); for(int i = 0; i < count; i++) { int[] args = new int[Thing.NUM_ARGS]; int tag = reader.ReadInt32(); double x = reader.ReadDouble(); double y = reader.ReadDouble(); double height = reader.ReadDouble(); int angledeg = reader.ReadInt32(); int pitch = reader.ReadInt32(); //mxd int roll = reader.ReadInt32(); //mxd double scaleX = reader.ReadDouble(); //mxd double scaleY = reader.ReadDouble(); //mxd int type = reader.ReadInt32(); int special = reader.ReadInt32(); for(int a = 0; a < args.Length; a++) args[a] = reader.ReadInt32(); //flags Dictionary stringflags = new Dictionary(StringComparer.Ordinal); int numFlags = reader.ReadInt32(); for(int f = 0; f < numFlags; f++) stringflags.Add(ReadString(reader), reader.ReadBoolean()); //add missing flags foreach(KeyValuePair flag in General.Map.Config.ThingFlags) { if(stringflags.ContainsKey(flag.Key)) continue; stringflags.Add(flag.Key, false); } // Create new item Dictionary fields = ReadCustomFields(reader); Thing t = map.CreateThing(); if(t != null) { t.Update(type, x, y, height, angledeg, pitch, roll, scaleX, scaleY, stringflags, tag, special, args); // Add custom fields t.Fields.BeforeFieldsChange(); foreach(KeyValuePair group in fields) { t.Fields.Add(group.Key, group.Value); } } } } private static Dictionary ReadCustomFields(BinaryReader reader) { Dictionary fields = new Dictionary(StringComparer.Ordinal); int fieldscount = reader.ReadInt32(); for(int f = 0; f < fieldscount; f++) { string name = ReadString(reader); UniversalType type = (UniversalType)reader.ReadInt32(); UniversalType valueType = (UniversalType)reader.ReadInt32(); switch(valueType) { case UniversalType.Float: fields.Add(name, new UniValue(type, reader.ReadDouble())); break; case UniversalType.Boolean: fields.Add(name, new UniValue(type, reader.ReadBoolean())); break; case UniversalType.Integer: fields.Add(name, new UniValue(type, reader.ReadInt32())); break; case UniversalType.String: fields.Add(name, new UniValue(type, ReadString(reader))); break; default: //WOLOLO! ERRORS! throw new Exception("Got unknown value type while reading custom fields from clipboard data! Field \"" + name + "\", type \"" + type + "\", primitive type \"" + valueType + "\""); } } return fields; } private static string ReadString(BinaryReader reader) { int len = reader.ReadInt32(); if(len == 0) return string.Empty; char[] chars = new char[len]; for(int i = 0; i < len; ++i) chars[i] = reader.ReadChar(); return new string(chars); } #endregion } }