From 91e1fbbd530a54162e6c6964d552cbd2c3b2151f Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Thu, 28 May 2015 13:45:01 +0000
Subject: [PATCH] Internal: added SNDSEQ parser. Sector Edit Form, UDMF: Sound
 Sequence selector is now a drop-down populated by values parsed from SNDSEQ.
 Documentation: updated "(G)ZDoom text lumps support" page. Updated
 udmf_zdoom.txt.

---
 Documents/udmf_zdoom.txt                      |  5 +
 Help/gzdb/text_lumps.html                     |  3 +
 Source/Core/Builder.csproj                    |  1 +
 Source/Core/Controls/ActionSelectorControl.cs |  8 +-
 Source/Core/Data/DataManager.cs               | 26 ++++++
 Source/Core/Data/DataReader.cs                |  3 +
 Source/Core/Data/PK3StructuredReader.cs       | 28 ++++++
 Source/Core/Data/WADReader.cs                 | 11 +++
 .../Windows/SectorEditFormUDMF.Designer.cs    | 29 +++---
 Source/Core/Windows/SectorEditFormUDMF.cs     |  5 +-
 Source/Core/ZDoom/SndSeqParser.cs             | 91 +++++++++++++++++++
 11 files changed, 192 insertions(+), 18 deletions(-)
 create mode 100644 Source/Core/ZDoom/SndSeqParser.cs

diff --git a/Documents/udmf_zdoom.txt b/Documents/udmf_zdoom.txt
index cbb5b902c..6541e9a02 100644
--- a/Documents/udmf_zdoom.txt
+++ b/Documents/udmf_zdoom.txt
@@ -119,6 +119,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
       blockhitscan = <bool>;      // Line blocks hitscan attacks
       locknumber = <int>;         // Line special is locked
       arg0str = <string>;         // Alternate string-based version of arg0
+	  moreids = <string>;         // Additional line IDs, specified as a space separated list of numbers (e.g. "2 666 1003 4505")
 
       transparent   = <bool>; // true = line is a Strife transparent line (alpha 0.25)
 
@@ -201,6 +202,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
                                       // sound sequence thing in the sector will override this property.
       hidden = <bool>;                // if true this sector will not be drawn on the textured automap.
       waterzone = <bool>;             // Sector is under water and swimmable
+	  moreids = <string>;             // Additional sector IDs/tags, specified as a space separated list of numbers (e.g. "2 666 1003 4505")
       
       * Note about dropactors
 
@@ -370,6 +372,9 @@ Added transparent line property (to be folded back to core UDMF standard), and h
 Added plane equations for sector slopes. (Please read carefully to ensure proper use!)
 Changed language describing the DIALOGUE lump to mention USDF as an option.
 
+1.25 19.04.2015
+Added 'moreids' for linedefs and sectors.
+
 ===============================================================================
 EOF
 ===============================================================================
diff --git a/Help/gzdb/text_lumps.html b/Help/gzdb/text_lumps.html
index dbc2c51bf..767349f46 100644
--- a/Help/gzdb/text_lumps.html
+++ b/Help/gzdb/text_lumps.html
@@ -50,6 +50,9 @@
 	<a name="reverbs" id="reverbs"></a>
     <h2>REVERBS:</h2>
     Sound environment definitions are loaded and can be used in the <a href="features/classic_modes/mode_soundenvironment.html">Sound Environment Mode</a>.
+    
+    <h2>SNDSEQ:</h2>
+    Sound Sequence and Sound Sequence Group definitions are loaded and can be selected in the "Sound sequence" drop-down of the Edit Sector window (UDMF only). 
          
     <a name="modeldef" id="modeldef"></a>
     <h2>MODELDEF:</h2>
diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 20b66c4ee..de35a94f6 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -953,6 +953,7 @@
     <Compile Include="ZDoom\ActorStructure.cs" />
     <Compile Include="ZDoom\PatchStructure.cs" />
     <Compile Include="ZDoom\ReverbsParser.cs" />
+    <Compile Include="ZDoom\SndSeqParser.cs" />
     <Compile Include="ZDoom\StateGoto.cs" />
     <Compile Include="ZDoom\TexturesParser.cs" />
     <Compile Include="ZDoom\TextureStructure.cs" />
diff --git a/Source/Core/Controls/ActionSelectorControl.cs b/Source/Core/Controls/ActionSelectorControl.cs
index 48dcb45e1..6d8f68265 100644
--- a/Source/Core/Controls/ActionSelectorControl.cs
+++ b/Source/Core/Controls/ActionSelectorControl.cs
@@ -169,7 +169,6 @@ namespace CodeImp.DoomBuilder.Controls
 						backbrush = new SolidBrush(SystemColors.Window);
 					}
 				}
-				
 			}
 
 			// Draw item
@@ -177,8 +176,11 @@ namespace CodeImp.DoomBuilder.Controls
 			e.Graphics.DrawString(displayname, list.Font, displaybrush, e.Bounds.X, e.Bounds.Y);
 
 			//mxd. Dispose brushes
-			backbrush.Dispose();
-			displaybrush.Dispose();
+			if(!this.DesignMode)
+			{
+				backbrush.Dispose();
+				displaybrush.Dispose();
+			}
 		}
 
 		// List closed
diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs
index 56c666252..6cfd5a3ac 100644
--- a/Source/Core/Data/DataManager.cs
+++ b/Source/Core/Data/DataManager.cs
@@ -70,6 +70,7 @@ namespace CodeImp.DoomBuilder.Data
 		private MapInfo mapinfo;
 		private Dictionary<string, KeyValuePair<int, int>> reverbs; //<name, <arg1, arg2> 
 		private Dictionary<long, GlowingFlatData> glowingflats; // Texture name hash, Glowing Flat Data
+		private List<string> soundsequences;
 		
 		// Background loading
 		private Queue<ImageData> imageque;
@@ -115,6 +116,7 @@ namespace CodeImp.DoomBuilder.Data
 		public MapInfo MapInfo { get { return mapinfo; } }
 		public Dictionary<string, KeyValuePair<int, int>> Reverbs { get { return reverbs; } }
 		public Dictionary<long, GlowingFlatData> GlowingFlats { get { return glowingflats; } }
+		public List<string> SoundSequences { get { return soundsequences; } }
 
 		public Playpal Palette { get { return palette; } }
 		public PreviewManager Previews { get { return previews; } }
@@ -161,6 +163,7 @@ namespace CodeImp.DoomBuilder.Data
 			gldefsentries = new Dictionary<int, DynamicLightData>();
 			reverbs = new Dictionary<string, KeyValuePair<int, int>>();
 			glowingflats = new Dictionary<long, GlowingFlatData>();
+			soundsequences = new List<string>();
 
 			// Load special images
 			missingtexture3d = new ResourceImage("CodeImp.DoomBuilder.Resources.MissingTexture3D.png");
@@ -331,6 +334,7 @@ namespace CodeImp.DoomBuilder.Data
 
 			//mxd. Load more stuff
 			LoadReverbs();
+			LoadSndSeq();
 			LoadVoxels();
 			Dictionary<string, List<int>> actorsbyclass = CreateActorsByClassList();
 			LoadModeldefs(actorsbyclass);
@@ -1929,6 +1933,28 @@ namespace CodeImp.DoomBuilder.Data
 			reverbs = parser.GetReverbs();
 		}
 
+		//mxd. This loads SNDSEQ
+		private void LoadSndSeq()
+		{
+			SndSeqParser parser = new SndSeqParser();
+			soundsequences.Clear();
+
+			foreach(DataReader dr in containers) 
+			{
+				currentreader = dr;
+				List<Stream> streams = dr.GetSndSeqData();
+
+				// Parse the data
+				foreach (Stream s in streams)
+				{
+					if(s != null) parser.Parse(s, "SNDSEQ");
+				}
+			}
+
+			currentreader = null;
+			soundsequences = parser.GetSoundSequences();
+		}
+
 		//mxd
 		internal MemoryStream LoadFile(string name) 
 		{
diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs
index c30c5e643..57c8b7011 100644
--- a/Source/Core/Data/DataReader.cs
+++ b/Source/Core/Data/DataReader.cs
@@ -176,6 +176,9 @@ namespace CodeImp.DoomBuilder.Data
 		//mxd
 		public virtual KeyValuePair<string, Stream> GetVoxeldefData() { return new KeyValuePair<string,Stream>(); }
 
+		//mxd. When implemented, this returns the SndSeq lump
+		public virtual List<Stream> GetSndSeqData() { return new List<Stream>(); }
+
 		//mxd
 		internal virtual MemoryStream LoadFile(string name) { return null; }
 		internal virtual bool FileExists(string filename) { return false; }
diff --git a/Source/Core/Data/PK3StructuredReader.cs b/Source/Core/Data/PK3StructuredReader.cs
index 420046867..cb935865a 100644
--- a/Source/Core/Data/PK3StructuredReader.cs
+++ b/Source/Core/Data/PK3StructuredReader.cs
@@ -616,6 +616,34 @@ namespace CodeImp.DoomBuilder.Data
 
 		#endregion
 
+		#region ================== SndSeq
+
+		public override List<Stream> GetSndSeqData() 
+		{
+			// Error when suspended
+			if(issuspended) throw new Exception("Data reader is suspended");
+
+			List<Stream> streams = new List<Stream>();
+
+			// Get from wads first
+			//TODO: is this the correct order?..
+			foreach(WADReader wr in wads) 
+			{
+				streams.AddRange(wr.GetSndSeqData());
+			}
+
+			// Then from our own files
+			string foundfile = FindFirstFile("sndseq", false);
+			if(!string.IsNullOrEmpty(foundfile) && FileExists(foundfile))
+			{
+				streams.Add(LoadFile(foundfile));
+			}
+
+			return streams;
+		}
+
+		#endregion
+
 		#region ================== Methods
 
 		// This loads the images in this directory
diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs
index 6d97fe52e..cfefe4b31 100644
--- a/Source/Core/Data/WADReader.cs
+++ b/Source/Core/Data/WADReader.cs
@@ -928,6 +928,17 @@ namespace CodeImp.DoomBuilder.Data
 			return result;
 		}
 
+		//mxd
+		public override List<Stream> GetSndSeqData() 
+		{
+			if(issuspended) throw new Exception("Data reader is suspended");
+
+			List<Stream> result = new List<Stream>();
+			Lump lump = file.FindLump("SNDSEQ");
+			if(lump != null) result.Add(lump.Stream);
+			return result;
+		}
+
 		//mxd
 		internal override MemoryStream LoadFile(string name) 
 		{
diff --git a/Source/Core/Windows/SectorEditFormUDMF.Designer.cs b/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
index c63044e89..8aeebd66d 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
+++ b/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
@@ -46,7 +46,6 @@
 			this.lightColor = new CodeImp.DoomBuilder.GZBuilder.Controls.ColorFieldsControl();
 			this.brightness = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.desaturation = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
-			this.soundsequence = new System.Windows.Forms.TextBox();
 			this.gravity = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.browseeffect = new System.Windows.Forms.Button();
 			this.effect = new CodeImp.DoomBuilder.Controls.ActionSelectorControl();
@@ -104,6 +103,7 @@
 			this.cancel = new System.Windows.Forms.Button();
 			this.apply = new System.Windows.Forms.Button();
 			this.tooltip = new System.Windows.Forms.ToolTip(this.components);
+			this.soundsequence = new System.Windows.Forms.ComboBox();
 			groupaction = new System.Windows.Forms.GroupBox();
 			groupeffect = new System.Windows.Forms.GroupBox();
 			label14 = new System.Windows.Forms.Label();
@@ -153,6 +153,7 @@
 			// 
 			groupeffect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
 						| System.Windows.Forms.AnchorStyles.Right)));
+			groupeffect.Controls.Add(this.soundsequence);
 			groupeffect.Controls.Add(this.resetsoundsequence);
 			groupeffect.Controls.Add(this.fadeColor);
 			groupeffect.Controls.Add(this.lightColor);
@@ -161,7 +162,6 @@
 			groupeffect.Controls.Add(label14);
 			groupeffect.Controls.Add(label9);
 			groupeffect.Controls.Add(label13);
-			groupeffect.Controls.Add(this.soundsequence);
 			groupeffect.Controls.Add(this.gravity);
 			groupeffect.Controls.Add(label2);
 			groupeffect.Controls.Add(this.browseeffect);
@@ -269,15 +269,6 @@
 			label13.Text = "Desaturation:";
 			label13.TextAlign = System.Drawing.ContentAlignment.TopRight;
 			// 
-			// soundsequence
-			// 
-			this.soundsequence.Location = new System.Drawing.Point(125, 55);
-			this.soundsequence.Name = "soundsequence";
-			this.soundsequence.Size = new System.Drawing.Size(325, 20);
-			this.soundsequence.TabIndex = 2;
-			this.soundsequence.TextChanged += new System.EventHandler(this.soundsequence_TextChanged);
-			this.soundsequence.MouseDown += new System.Windows.Forms.MouseEventHandler(this.soundsequence_MouseDown);
-			// 
 			// gravity
 			// 
 			this.gravity.AllowDecimal = true;
@@ -585,7 +576,7 @@
 			// 
 			// floorAngleControl
 			// 
-			this.floorAngleControl.Angle = -360;
+			this.floorAngleControl.Angle = 0;
 			this.floorAngleControl.AngleOffset = 90;
 			this.floorAngleControl.Location = new System.Drawing.Point(6, 132);
 			this.floorAngleControl.Name = "floorAngleControl";
@@ -819,7 +810,7 @@
 			// 
 			// ceilAngleControl
 			// 
-			this.ceilAngleControl.Angle = -360;
+			this.ceilAngleControl.Angle = 0;
 			this.ceilAngleControl.AngleOffset = 90;
 			this.ceilAngleControl.Location = new System.Drawing.Point(6, 132);
 			this.ceilAngleControl.Name = "ceilAngleControl";
@@ -1111,6 +1102,16 @@
 			this.tooltip.InitialDelay = 10;
 			this.tooltip.ReshowDelay = 100;
 			// 
+			// soundsequence
+			// 
+			this.soundsequence.FormattingEnabled = true;
+			this.soundsequence.Location = new System.Drawing.Point(125, 54);
+			this.soundsequence.Name = "soundsequence";
+			this.soundsequence.Size = new System.Drawing.Size(325, 21);
+			this.soundsequence.TabIndex = 32;
+			this.soundsequence.MouseDown += new System.Windows.Forms.MouseEventHandler(this.soundsequence_MouseDown);
+			this.soundsequence.TextChanged += new System.EventHandler(this.soundsequence_TextChanged);
+			// 
 			// SectorEditFormUDMF
 			// 
 			this.AcceptButton = this.apply;
@@ -1201,7 +1202,6 @@
 		private System.Windows.Forms.GroupBox groupBox3;
 		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox gravity;
 		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox desaturation;
-		private System.Windows.Forms.TextBox soundsequence;
 		private CodeImp.DoomBuilder.GZBuilder.Controls.ColorFieldsControl fadeColor;
 		private CodeImp.DoomBuilder.GZBuilder.Controls.ColorFieldsControl lightColor;
 		private CodeImp.DoomBuilder.Controls.CheckboxArrayControl flags;
@@ -1219,5 +1219,6 @@
 		private System.Windows.Forms.Label labelCeilOffsets;
 		private System.Windows.Forms.Label labelCeilScale;
 		private System.Windows.Forms.Button resetsoundsequence;
+		private System.Windows.Forms.ComboBox soundsequence;
 	}
 }
\ No newline at end of file
diff --git a/Source/Core/Windows/SectorEditFormUDMF.cs b/Source/Core/Windows/SectorEditFormUDMF.cs
index cfd2c90fa..2f4d44c79 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.cs
+++ b/Source/Core/Windows/SectorEditFormUDMF.cs
@@ -205,6 +205,10 @@ namespace CodeImp.DoomBuilder.Windows
 			effect.GeneralizedOptions = General.Map.Config.GenEffectOptions; //mxd
 			effect.AddInfo(General.Map.Config.SortedSectorEffects.ToArray());
 
+			// Fill sound sequences list
+			soundsequence.Items.Add(NO_SOUND_SEQUENCE);
+			soundsequence.Items.AddRange(General.Map.Data.SoundSequences.ToArray());
+
 			// Initialize custom fields editor
 			fieldslist.Setup("sector");
 
@@ -806,7 +810,6 @@ namespace CodeImp.DoomBuilder.Windows
 
 		private void soundsequence_TextChanged(object sender, EventArgs e) 
 		{
-			soundsequence.ForeColor = (soundsequence.Text == NO_SOUND_SEQUENCE ? SystemColors.GrayText : SystemColors.WindowText);
 			resetsoundsequence.Enabled = (soundsequence.Text != NO_SOUND_SEQUENCE);
 		}
 
diff --git a/Source/Core/ZDoom/SndSeqParser.cs b/Source/Core/ZDoom/SndSeqParser.cs
new file mode 100644
index 000000000..791914522
--- /dev/null
+++ b/Source/Core/ZDoom/SndSeqParser.cs
@@ -0,0 +1,91 @@
+#region ================== Namespaces
+
+using System.Collections.Generic;
+using System.IO;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.ZDoom
+{
+	internal sealed class SndSeqParser : ZDTextParser
+	{
+		#region ================== Variables
+
+		private readonly List<string> sequences;
+		private readonly List<string> sequencegroups;
+		private readonly HashSet<string> seqencenames;
+
+		#endregion
+
+		#region ================== Constructor
+
+		public SndSeqParser()
+		{
+			specialtokens = "";
+			sequences = new List<string>();
+			sequencegroups = new List<string>();
+			seqencenames = new HashSet<string>();
+		}
+
+		#endregion
+
+		#region ================== Parsing
+
+		public override bool Parse(Stream stream, string sourcefilename)
+		{
+			base.Parse(stream, sourcefilename);
+			
+			char[] dots = new[] { ':' };
+			char[] brace = new[] { '[' };
+
+			// Continue until at the end of the stream
+			while(SkipWhitespace(true))
+			{
+				string token = ReadToken();
+
+				if(!string.IsNullOrEmpty(token))
+				{
+					// Sound sequence definition
+					if(token.StartsWith(":"))
+					{
+						string val = token.TrimStart(dots);
+						if(!string.IsNullOrEmpty(val) && !seqencenames.Contains(val.ToUpper()))
+						{
+							sequences.Add(val);
+							seqencenames.Add(val.ToUpper());
+						}
+					}
+					// Group definition
+					else if(token.StartsWith("["))
+					{
+						string val = token.TrimStart(brace);
+						if(!string.IsNullOrEmpty(val) && !seqencenames.Contains(val.ToUpper()))
+						{
+							sequencegroups.Add(val);
+							seqencenames.Add(val.ToUpper());
+						}
+					} 
+				}
+			}
+
+			return true;
+		}
+
+		internal List<string> GetSoundSequences() 
+		{
+			List<string> result = new List<string>(sequencegroups.Count + sequences.Count);
+			
+			// Add to the collection
+			sequencegroups.Sort();
+			result.AddRange(sequencegroups);
+
+			sequences.Sort();
+			result.AddRange(sequences);
+
+			// Return the collection
+			return result;
+		}
+
+		#endregion
+	}
+}
\ No newline at end of file
-- 
GitLab