From 80e56310a31715c5656f3b767e0539d2f38e3b84 Mon Sep 17 00:00:00 2001 From: biwa <6475593+biwa@users.noreply.github.com> Date: Sun, 3 Oct 2021 21:04:38 +0200 Subject: [PATCH] UDMF reader/writer: fixed a bug where unknown top level fields and blocks were not preserved. Fixes #627 --- Source/Core/IO/UniversalStreamReader.cs | 22 +++++++++++++++++ Source/Core/IO/UniversalStreamWriter.cs | 32 +++++++++++++++++++++++-- Source/Core/Map/MapSet.cs | 28 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/Source/Core/IO/UniversalStreamReader.cs b/Source/Core/IO/UniversalStreamReader.cs index 3566065f..e31fe56c 100755 --- a/Source/Core/IO/UniversalStreamReader.cs +++ b/Source/Core/IO/UniversalStreamReader.cs @@ -163,6 +163,9 @@ namespace CodeImp.DoomBuilder.IO Dictionary sectorlink = ReadSectors(map, textmap); ReadLinedefs(map, textmap, vertexlink, sectorlink); ReadThings(map, textmap); + + // Read all the stuff we don't know, but have to preserve + map.UnknownUDMFData = GetUnknownCollections(textmap.Root, new List() { "namespace", "vertex", "sector", "linedef", "sidedef", "thing" }); } // This reads the things @@ -629,6 +632,25 @@ namespace CodeImp.DoomBuilder.IO return list; } + /// + /// Get unknown fields and blocks from an universal collection. + /// + /// Universal collection to get the fields and blocks from + /// List of fields and block that are known and can be ignored + /// + private static List GetUnknownCollections(UniversalCollection collection, List knowncollections) + { + List list = new List(); + + // Make list + foreach (UniversalEntry e in collection) + { + if (!knowncollections.Contains(e.Key)) list.Add(e); + } + + return list; + } + #endregion } } diff --git a/Source/Core/IO/UniversalStreamWriter.cs b/Source/Core/IO/UniversalStreamWriter.cs index d020294a..d6e06eb4 100755 --- a/Source/Core/IO/UniversalStreamWriter.cs +++ b/Source/Core/IO/UniversalStreamWriter.cs @@ -114,7 +114,7 @@ namespace CodeImp.DoomBuilder.IO // writenamespace may be null to omit writing the namespace to the stream public void Write(MapSet map, Stream stream, string writenamespace) { - Write(map.Vertices, map.Linedefs, map.Sidedefs, map.Sectors, map.Things, stream, writenamespace); + Write(map.Vertices, map.Linedefs, map.Sidedefs, map.Sectors, map.Things, map.UnknownUDMFData, stream, writenamespace); } // This writes the structures to a stream @@ -123,13 +123,16 @@ namespace CodeImp.DoomBuilder.IO // If there are missing sidedefs, their reference will be removed from the linedefs. public void Write(ICollection vertices, ICollection linedefs, ICollection sidedefs, ICollection sectors, - ICollection things, Stream stream, string writenamespace) + ICollection things, ICollection unknowndata, Stream stream, string writenamespace) { UniversalParser textmap = new UniversalParser(); // Begin with fields that must be at the top if(writenamespace != null) textmap.Root.Add("namespace", writenamespace); + // Dump unknown fields at the top + WriteUnknownData(unknowndata, textmap); + Dictionary vertexids = new Dictionary(vertices.Count); //mxd Dictionary sidedefids = new Dictionary(sidedefs.Count); //mxd Dictionary sectorids = new Dictionary(sectors.Count); //mxd @@ -362,6 +365,31 @@ namespace CodeImp.DoomBuilder.IO } } + /// + /// This writes UDMF data UDB doesn't know about to the map. + /// + /// Collection of universal entries. + /// The map + private void WriteUnknownData(ICollection data, UniversalParser textmap) + { + foreach (UniversalEntry e in data) + { + if (e.Value is UniversalCollection) + { + UniversalCollection coll = new UniversalCollection(); + + foreach (UniversalEntry ie in (UniversalCollection)e.Value) + coll.Add(ie.Key, ie.Value); + + textmap.Root.Add(e.Key, coll); + } + else + { + textmap.Root.Add(e); + } + } + } + // This adds custom fields from a map element to a collection private void AddCustomFields(MapElement element, string elementname, UniversalCollection collection) { diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs index 62213815..0edd2b0f 100755 --- a/Source/Core/Map/MapSet.cs +++ b/Source/Core/Map/MapSet.cs @@ -94,6 +94,9 @@ namespace CodeImp.DoomBuilder.Map private LinkedList sel_sectors; private LinkedList sel_things; private SelectionType sel_type; + + // Unknown UDMF data that needs to be preserved + private List unknownudmfdata; // Statics private static long emptylongname; @@ -164,6 +167,8 @@ namespace CodeImp.DoomBuilder.Map internal bool AutoRemove { get { return autoremove; } set { autoremove = value; } } + internal List UnknownUDMFData { get { return unknownudmfdata; } set { unknownudmfdata = value; } } + #endregion #region ================== Constructor / Disposer @@ -184,6 +189,7 @@ namespace CodeImp.DoomBuilder.Map indexholes = new List(); lastsectorindex = 0; autoremove = true; + unknownudmfdata = new List(); // We have no destructor GC.SuppressFinalize(this); @@ -205,6 +211,7 @@ namespace CodeImp.DoomBuilder.Map indexholes = new List(); lastsectorindex = 0; autoremove = true; + unknownudmfdata = new List(); // Deserialize Deserialize(stream); @@ -387,6 +394,27 @@ namespace CodeImp.DoomBuilder.Map // Remove clone references foreach(Vertex v in vertices) v.Clone = null; foreach(Sector s in sectors) s.Clone = null; + + // Copy unknown UDMF data + newset.UnknownUDMFData = new List(); + foreach(UniversalEntry e in unknownudmfdata) + { + if(e.Value is UniversalCollection) + { + // The UniversalEntry value is a collection, so we have to copy all sub-elements + UniversalCollection uc = new UniversalCollection(); + + foreach(UniversalEntry ie in (UniversalCollection)e.Value) + uc.Add(ie.Key, ie.Value); + + newset.UnknownUDMFData.Add(new UniversalEntry(e.Key, uc)); + } + else + { + // Just a normal UniversalEntry + newset.UnknownUDMFData.Add(new UniversalEntry(e.Key, e.Value)); + } + } // Return the new set newset.EndAddRemove();