From b9eba267db2d8827f0400fa01a753504e638f0c5 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Fri, 18 Dec 2015 10:16:53 +0000
Subject: [PATCH] Added ANIMDEFS CAMERATEXTURE support. Some internal changes
 in text parsers logic.

---
 Source/Core/Builder.csproj                    |   2 +
 Source/Core/Controls/ScriptDocumentTab.cs     |   8 +-
 Source/Core/Controls/ScriptFileDocumentTab.cs |   4 +-
 Source/Core/Data/CameraTextureImage.cs        | 120 ++++++++++++++
 Source/Core/Data/DataManager.cs               | 139 ++++++++++++-----
 Source/Core/Data/DataReader.cs                |  43 ++---
 Source/Core/Data/PK3StructuredReader.cs       |  59 +++++--
 Source/Core/Data/WADReader.cs                 |  25 ++-
 Source/Core/GZBuilder/GZDoom/AcsParserSE.cs   |  22 +--
 .../Core/GZBuilder/GZDoom/DecorateParserSE.cs |   4 +-
 Source/Core/GZBuilder/GZDoom/GldefsParser.cs  |   8 +-
 Source/Core/GZBuilder/GZDoom/MapinfoParser.cs |  18 +--
 .../Core/GZBuilder/GZDoom/ModeldefParser.cs   |   4 +-
 .../Core/GZBuilder/GZDoom/ModeldefParserSE.cs |   4 +-
 .../GZBuilder/GZDoom/ScriptTypeParserSE.cs    |   6 +-
 Source/Core/General/MapManager.cs             |   4 +-
 Source/Core/ZDoom/AnimdefsParser.cs           | 147 ++++++++++++++++++
 Source/Core/ZDoom/DecorateParser.cs           |   2 +-
 Source/Core/ZDoom/ReverbsParser.cs            |   4 +-
 Source/Core/ZDoom/SndSeqParser.cs             |   4 +-
 Source/Core/ZDoom/TexturesParser.cs           |   4 +-
 Source/Core/ZDoom/VoxeldefParser.cs           |   4 +-
 Source/Core/ZDoom/ZDTextParser.cs             |  42 +++--
 23 files changed, 523 insertions(+), 154 deletions(-)
 create mode 100644 Source/Core/Data/CameraTextureImage.cs
 create mode 100644 Source/Core/ZDoom/AnimdefsParser.cs

diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 338f3cbef..0a8f10560 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -792,6 +792,7 @@
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Data\BitmapImage.cs" />
+    <Compile Include="Data\CameraTextureImage.cs" />
     <Compile Include="Data\ColorImage.cs" />
     <Compile Include="Data\ColormapImage.cs" />
     <Compile Include="Data\HighResImage.cs" />
@@ -996,6 +997,7 @@
       <DependentUpon>VertexEditForm.cs</DependentUpon>
     </Compile>
     <Compile Include="ZDoom\ActorStructure.cs" />
+    <Compile Include="ZDoom\AnimdefsParser.cs" />
     <Compile Include="ZDoom\PatchStructure.cs" />
     <Compile Include="ZDoom\ReverbsParser.cs" />
     <Compile Include="ZDoom\SndSeqParser.cs" />
diff --git a/Source/Core/Controls/ScriptDocumentTab.cs b/Source/Core/Controls/ScriptDocumentTab.cs
index 3536a2c44..98acbd0a5 100644
--- a/Source/Core/Controls/ScriptDocumentTab.cs
+++ b/Source/Core/Controls/ScriptDocumentTab.cs
@@ -426,7 +426,7 @@ namespace CodeImp.DoomBuilder.Controls
 			navigator.Items.Clear();
 
 			DecorateParserSE parser = new DecorateParserSE();
-			if(parser.Parse(stream, "DECORATE"))
+			if(parser.Parse(stream, "DECORATE", false))
 			{
 				navigator.Items.AddRange(parser.Actors.ToArray());
 			}
@@ -444,7 +444,7 @@ namespace CodeImp.DoomBuilder.Controls
 			navigator.Items.Clear();
 
 			ModeldefParserSE parser = new ModeldefParserSE();
-			if(parser.Parse(stream, "MODELDEF"))
+			if(parser.Parse(stream, "MODELDEF", false))
 			{
 				navigator.Items.AddRange(parser.Models.ToArray());
 			}
@@ -462,7 +462,7 @@ namespace CodeImp.DoomBuilder.Controls
 			navigator.Items.Clear();
 
 			AcsParserSE parser = new AcsParserSE { AddArgumentsToScriptNames = true, IsMapScriptsLump = this is ScriptLumpDocumentTab };
-			if(parser.Parse(stream, "SCRIPTS"))
+			if(parser.Parse(stream, "SCRIPTS", false))
 			{
 				navigator.Items.AddRange(parser.NamedScripts.ToArray());
 				navigator.Items.AddRange(parser.NumberedScripts.ToArray());
@@ -479,7 +479,7 @@ namespace CodeImp.DoomBuilder.Controls
 		internal ScriptType VerifyScriptType() 
 		{
 			ScriptTypeParserSE parser = new ScriptTypeParserSE();
-			if(parser.Parse(new MemoryStream(editor.GetText()), config.Description)) 
+			if(parser.Parse(new MemoryStream(editor.GetText()), config.Description, false)) 
 			{
 				if(parser.ScriptType != ScriptType.UNKNOWN && config.ScriptType != parser.ScriptType)
 					return parser.ScriptType;
diff --git a/Source/Core/Controls/ScriptFileDocumentTab.cs b/Source/Core/Controls/ScriptFileDocumentTab.cs
index ad2a85fc4..f9e094c24 100644
--- a/Source/Core/Controls/ScriptFileDocumentTab.cs
+++ b/Source/Core/Controls/ScriptFileDocumentTab.cs
@@ -172,10 +172,10 @@ namespace CodeImp.DoomBuilder.Controls
 			}
 
 			// Preprocess the file
-			AcsParserSE parser = new AcsParserSE { OnInclude = (se, path) => se.Parse(General.Map.Data.LoadFile(path), path, true, true) };
+			AcsParserSE parser = new AcsParserSE { OnInclude = (se, path) => se.Parse(General.Map.Data.LoadFile(path), path, true, true, false) };
 			using(FileStream stream = File.OpenRead(filepathname))
 			{
-				if(!parser.Parse(stream, filepathname, scriptconfig.Compiler.Files, true, false))
+				if(!parser.Parse(stream, filepathname, scriptconfig.Compiler.Files, true, false, false))
 				{
 					// Check for errors
 					if(parser.HasError)
diff --git a/Source/Core/Data/CameraTextureImage.cs b/Source/Core/Data/CameraTextureImage.cs
new file mode 100644
index 000000000..2008276e3
--- /dev/null
+++ b/Source/Core/Data/CameraTextureImage.cs
@@ -0,0 +1,120 @@
+#region ================== Namespaces
+
+using System;
+using System.Drawing;
+using System.IO;
+using CodeImp.DoomBuilder.ZDoom;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.Data
+{
+	public class CameraTextureImage : ImageData
+	{
+		#region ================== Constructor / Disposer
+
+		// Constructor
+		public CameraTextureImage(CameraTextureData data)
+		{
+			// Initialize
+			this.UseColorCorrection = false;
+			this.worldpanning = data.WorldPanning;
+			SetName(data.Name);
+
+			// Get width and height from image
+			this.width = data.Width;
+			this.height = data.Height;
+			scale.x = data.ScaleX;
+			scale.y = data.ScaleY;
+
+			// We have no destructor
+			GC.SuppressFinalize(this);
+		}
+
+		#endregion
+
+		#region ================== Methods
+
+		// This loads the image
+		protected override void LocalLoadImage()
+		{
+			// Leave when already loaded
+			if(this.IsImageLoaded) return;
+			
+			lock(this)
+			{
+				bitmap = new Bitmap(width, height);
+
+				int w = Math.Max(2, Math.Min(width, height) / 24); // line width
+				int o = w / 2; // line center offset
+				int l = w * 3; // line length
+				name = name.ToUpperInvariant();
+
+				using(Graphics g = Graphics.FromImage(bitmap))
+				{
+					// Fill bg
+					g.FillRectangle(Brushes.Black, 0, 0, width, height);
+
+					// Draw corners
+					Color color = General.Colors.BrightColors[General.Random(0, General.Colors.BrightColors.Length - 1)].ToColor();
+					using(var pen = new Pen(color, w))
+					{
+						g.DrawLines(pen, new[] { new Point(l, o), new Point(o, o), new Point(o, l) }); // TL
+						g.DrawLines(pen, new[] { new Point(width - l, o), new Point(width - o, o), new Point(width - o, l) }); // TR
+						g.DrawLines(pen, new[] { new Point(l, height - o), new Point(o, height - o), new Point(o, height - l) }); // BL
+						g.DrawLines(pen, new[] { new Point(width - l, height - o), new Point(width - o, height - o), new Point(width - o, height - l) }); // BR
+					}
+
+					// Calculate required font size
+					const string rec = "\u25CFREC";
+					float targetwidth = Math.Max(l * 2, 22);
+					SizeF fontsize = g.MeasureString(rec, General.MainWindow.Font);
+					float scaleratio = Math.Min(targetwidth / fontsize.Height, targetwidth / fontsize.Width);
+
+					// Draw "REC" text
+					using(Font font = new Font(General.MainWindow.Font.FontFamily, General.MainWindow.Font.Size * scaleratio))
+					{
+						using(var brush = new SolidBrush(Color.Red))
+						{
+							g.DrawString(rec, font, brush, new RectangleF(l / 2, l / 2 - w / 2, fontsize.Width * scaleratio, fontsize.Height * scaleratio));
+						}
+					}
+
+					// Calculate required font size
+					targetwidth = Math.Min(width, height);
+					targetwidth -= targetwidth / 6;
+					fontsize = g.MeasureString(name, General.MainWindow.Font);
+					scaleratio = Math.Min(targetwidth / fontsize.Height, targetwidth / fontsize.Width);
+
+					// Draw texture name
+					using(Font font = new Font(General.MainWindow.Font.FontFamily, General.MainWindow.Font.Size * scaleratio))
+					{
+						using(var brush = new SolidBrush(color))
+						{
+							StringFormat sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
+							g.DrawString(name, font, brush, new RectangleF(0, 0, width, height), sf);
+						}
+					}
+				}
+
+				// Pass on to base
+				base.LocalLoadImage();
+			}
+		}
+
+		//mxd
+		protected override void SetName(string name)
+		{
+			if(!General.Map.Config.UseLongTextureNames)
+			{
+				if(name.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH) name = name.Substring(0, DataManager.CLASIC_IMAGE_NAME_LENGTH);
+				name = name.ToUpperInvariant();
+			}
+
+			base.SetName(name);
+			this.virtualname = "[CAMERA TEXTURES]" + Path.AltDirectorySeparatorChar + name;
+		}
+
+		#endregion
+	}
+}
diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs
index 4dd39e32b..24d7b1ee0 100644
--- a/Source/Core/Data/DataManager.cs
+++ b/Source/Core/Data/DataManager.cs
@@ -21,7 +21,6 @@ using System.Collections.Generic;
 using System.IO;
 using System.Threading;
 using System.Windows.Forms;
-using CodeImp.DoomBuilder.Compilers;
 using CodeImp.DoomBuilder.Config;
 using CodeImp.DoomBuilder.GZBuilder.Data;
 using CodeImp.DoomBuilder.GZBuilder.GZDoom;
@@ -369,7 +368,7 @@ namespace CodeImp.DoomBuilder.Data
 			LoadVoxels();
 			Dictionary<string, List<int>> actorsbyclass = CreateActorsByClassList();
 			LoadModeldefs(actorsbyclass);
-			foreach (Thing t in General.Map.Map.Things) t.UpdateCache();
+			foreach(Thing t in General.Map.Map.Things) t.UpdateCache();
 			General.MainWindow.DisplayReady();
 			
 			// Process colormaps (we just put them in as textures)
@@ -451,6 +450,9 @@ namespace CodeImp.DoomBuilder.Data
 
 			//mxd. Should be done after loading textures...
 			LoadGldefs(actorsbyclass);
+
+			//mxd. Create camera textures. Should be done after loading textures.
+			LoadAnimdefs();
 			
 			// Sort names
 			texturenames.Sort();
@@ -954,7 +956,7 @@ namespace CodeImp.DoomBuilder.Data
 							nametranslation.Remove(longshortname);
 							nametranslation.Add(longshortname, img.LongName);
 						} 
-						else if (img is TextureImage && nametranslation.ContainsKey(img.LongName))
+						else if(img is TextureImage && nametranslation.ContainsKey(img.LongName))
 						{
 							nametranslation.Remove(img.LongName);
 						}
@@ -1672,7 +1674,14 @@ namespace CodeImp.DoomBuilder.Data
 			foreach(KeyValuePair<string, Stream> group in decostreams)
 			{
 				// Parse this data
-				parser.Parse(group.Value, Path.Combine(currentreader.Location.location, group.Key));
+				parser.Parse(group.Value, Path.Combine(currentreader.Location.location, group.Key), false);
+
+				//mxd. DECORATE lumps are interdepandable. Can't carry on...
+				if(parser.HasError)
+				{
+					decorate.LogError();
+					return;
+				}
 			}
 		}
 		
@@ -1741,7 +1750,7 @@ namespace CodeImp.DoomBuilder.Data
 					group.Value.Dispose();
 			}
 
-			// Bail out when not supported by currect game configuration
+			// Bail out when not supported by current game configuration
 			if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
 
 			General.MainWindow.DisplayStatus(StatusType.Busy, "Reloading model definitions...");
@@ -1764,7 +1773,7 @@ namespace CodeImp.DoomBuilder.Data
 		//mxd
 		public void ReloadGldefs() 
 		{
-			// Bail out when not supported by currect game configuration
+			// Bail out when not supported by current game configuration
 			if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
 			
 			General.MainWindow.DisplayStatus(StatusType.Busy, "Reloading GLDEFS...");
@@ -1806,7 +1815,7 @@ namespace CodeImp.DoomBuilder.Data
 				foreach(KeyValuePair<string, Stream> group in streams) 
 				{
 					// Parse the data
-					if(parser.Parse(group.Value, currentreader.Location.location + "\\" + group.Key)) 
+					if(parser.Parse(group.Value, currentreader.Location.location + "\\" + group.Key, true)) 
 					{
 						foreach(KeyValuePair<string, ModelData> g in parser.Entries) 
 						{
@@ -1823,11 +1832,7 @@ namespace CodeImp.DoomBuilder.Data
 					}
 
 					// Modeldefs are independable, so parsing fail in one file should not affect the others
-					if(parser.HasError)
-					{
-						parser.LogError();
-						parser.ClearError();
-					}
+					if(parser.HasError) parser.LogError();
 				}
 			}
 
@@ -1849,7 +1854,7 @@ namespace CodeImp.DoomBuilder.Data
 		//mxd
 		private void LoadVoxels() 
 		{
-			// Bail out when not supported by currect game configuration
+			// Bail out when not supported by current game configuration
 			if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
 			
 			//Get names of all voxel models, which can be used "as is"
@@ -1859,7 +1864,7 @@ namespace CodeImp.DoomBuilder.Data
 			{
 				currentreader = dr;
 
-				string[] result = dr.GetVoxelNames();
+				IEnumerable<string> result = dr.GetVoxelNames();
 				if(result == null) continue;
 
 				foreach(string s in result) 
@@ -1902,7 +1907,7 @@ namespace CodeImp.DoomBuilder.Data
 				KeyValuePair<string, Stream> group = dr.GetVoxeldefData();
 				if(group.Value != null) 
 				{
-					if(parser.Parse(group.Value, group.Key))
+					if(parser.Parse(group.Value, group.Key, true))
 					{
 						foreach(KeyValuePair<string, ModelData> entry in parser.Entries)
 						{
@@ -1918,11 +1923,7 @@ namespace CodeImp.DoomBuilder.Data
 					}
 
 					// Report errors?
-					if(parser.HasError)
-					{
-						parser.LogError();
-						parser.ClearError();
-					}
+					if(parser.HasError) parser.LogError();
 				}
 			}
 
@@ -1963,7 +1964,7 @@ namespace CodeImp.DoomBuilder.Data
 
 				foreach(KeyValuePair<string, Stream> group in streams)
 				{
-					parser.Parse(group.Value, group.Key);
+					parser.Parse(group.Value, group.Key, false);
 					
 					// Gldefs can be interdependable. Can't carry on
 					if(parser.HasError)
@@ -2006,7 +2007,7 @@ namespace CodeImp.DoomBuilder.Data
 				foreach(KeyValuePair<string, Stream> group in streams) 
 				{
 					// Parse the data
-					parser.Parse(group.Value, Path.Combine(currentreader.Location.location, group.Key), General.Map.Options.LevelName);
+					parser.Parse(group.Value, Path.Combine(currentreader.Location.location, group.Key), General.Map.Options.LevelName, false);
 
 					//MAPINFO lumps are interdependable. Can't carry on...
 					if(parser.HasError)
@@ -2032,11 +2033,10 @@ namespace CodeImp.DoomBuilder.Data
 			mapinfo = parser.MapInfo ?? new MapInfo();
 		}
 
-		private void ParseFromLocation(ZDTextParser parser, string location)
+		private void ParseFromLocation(ZDTextParser parser, string location, bool clearerrors)
 		{
 			if(currentreader.IsSuspended) throw new Exception("Data reader is suspended");
-			Stream s = currentreader.LoadFile(location);
-			if(s != null) parser.Parse(s, location);
+			parser.Parse(currentreader.LoadFile(location), location, clearerrors);
 		}
 
 		//mxd. This loads REVERBS
@@ -2044,7 +2044,7 @@ namespace CodeImp.DoomBuilder.Data
 		{
 			reverbs.Clear();
 
-			// Bail out when not supported by currect game configuration
+			// Bail out when not supported by current game configuration
 			if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
 
 			ReverbsParser parser = new ReverbsParser();
@@ -2055,14 +2055,10 @@ namespace CodeImp.DoomBuilder.Data
 				foreach(KeyValuePair<string, Stream> group in streams) 
 				{
 					// Parse the data
-					parser.Parse(group.Value, group.Key);
+					parser.Parse(group.Value, group.Key, true);
 
 					// Report errors?
-					if(parser.HasError)
-					{
-						parser.LogError();
-						parser.ClearError();
-					}
+					if(parser.HasError) parser.LogError();
 				}
 			}
 
@@ -2075,31 +2071,94 @@ namespace CodeImp.DoomBuilder.Data
 		{
 			soundsequences.Clear();
 
-			// Bail out when not supported by currect game configuration
+			// Bail out when not supported by current game configuration
 			if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
 
 			SndSeqParser parser = new SndSeqParser();
 			foreach(DataReader dr in containers) 
 			{
 				currentreader = dr;
-				List<Stream> streams = dr.GetSndSeqData();
+				Dictionary<string, Stream> streams = dr.GetSndSeqData();
 
 				// Parse the data
-				foreach(Stream s in streams)
+				foreach(KeyValuePair<string, Stream> group in streams)
 				{
-					if(s != null) parser.Parse(s, "SNDSEQ");
+					parser.Parse(group.Value, group.Key, true);
 
 					// Report errors?
-					if(parser.HasError)
+					if(parser.HasError) parser.LogError();
+				}
+			}
+
+			currentreader = null;
+			soundsequences = parser.GetSoundSequences();
+		}
+
+		//mxd. This loads cameratextures from ANIMDEFS
+		private void LoadAnimdefs()
+		{
+			// Bail out when not supported by current game configuration
+			if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return;
+
+			AnimdefsParser parser = new AnimdefsParser();
+			foreach(DataReader dr in containers)
+			{
+				currentreader = dr;
+				Dictionary<string, Stream> streams = dr.GetAnimdefsData();
+
+				// Parse the data
+				foreach(KeyValuePair<string, Stream> group in streams)
+				{
+					parser.Parse(group.Value, group.Key, true);
+
+					// Report errors?
+					if(parser.HasError) parser.LogError();
+
+					// Create images
+					foreach(var g in parser.CameraTextures)
 					{
-						parser.LogError();
-						parser.ClearError();
+						// Grab a local copy
+						CameraTextureData data = g.Value;
+
+						// Apply texture size override?
+						if(!data.FitTexture)
+						{
+							long longname = Lump.MakeLongName(data.Name);
+
+							if(textures.ContainsKey(longname))
+							{
+								data.ScaleX = (float)textures[longname].Width / data.Width;
+								data.ScaleY = (float)textures[longname].Height / data.Height;
+							}
+							else if(flats.ContainsKey(longname))
+							{
+								data.ScaleX = (float)flats[longname].Width / data.Width;
+								data.ScaleY = (float)flats[longname].Height / data.Height;
+							}
+						}
+
+						// Create texture
+						CameraTextureImage camteximage = new CameraTextureImage(data);
+
+						// Add to flats and textures
+						texturenames.Add(camteximage.Name);
+						flatnames.Add(camteximage.Name);
+
+						//TODO: Do cameratextures override stuff like this?..
+						textures[camteximage.LongName] = camteximage;
+						flats[camteximage.LongName] = camteximage;
+
+						// Add to preview manager
+						previews.AddImage(camteximage);
+
+						// Add to container's texture set
+						currentreader.TextureSet.AddFlat(camteximage);
+						currentreader.TextureSet.AddTexture(camteximage);
 					}
 				}
 			}
 
 			currentreader = null;
-			soundsequences = parser.GetSoundSequences();
 		}
 
 		//mxd
diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs
index 57c8b7011..d8364e38b 100644
--- a/Source/Core/Data/DataReader.cs
+++ b/Source/Core/Data/DataReader.cs
@@ -152,36 +152,39 @@ namespace CodeImp.DoomBuilder.Data
 
 		#region ================== Decorate, Modeldef, Mapinfo, Gldefs, etc...
 
-		// When implemented, this returns the decorate lump
-		public virtual Dictionary<string, Stream> GetDecorateData(string pname) { return new Dictionary<string, Stream>(); }
+		// When implemented, this returns the DECORATE lump
+		public abstract Dictionary<string, Stream> GetDecorateData(string pname); // { return new Dictionary<string, Stream>(); }
 
-		//mxd. When implemented, this returns the Modeldef lump
-		public virtual Dictionary<string, Stream> GetModeldefData() { return new Dictionary<string, Stream>(); }
+		//mxd. When implemented, this returns the MODELDEF lump
+		public abstract Dictionary<string, Stream> GetModeldefData(); // { return new Dictionary<string, Stream>(); }
 
-		//mxd. When implemented, this returns the Mapinfo lump
-		public virtual Dictionary<string, Stream> GetMapinfoData() { return new Dictionary<string, Stream>(); }
+		//mxd. When implemented, this returns the MAPINFO lump
+		public abstract Dictionary<string, Stream> GetMapinfoData(); // { return new Dictionary<string, Stream>(); }
 
-		//mxd. When implemented, this returns the Gldefs lump
-		public virtual Dictionary<string, Stream> GetGldefsData(GameType gameType) { return new Dictionary<string, Stream>(); }
+		//mxd. When implemented, this returns the GLDEFS lump
+		public abstract Dictionary<string, Stream> GetGldefsData(GameType gametype); // { return new Dictionary<string, Stream>(); }
 
-		//mxd. When implemented, this returns the Reverbs lump
-		public virtual Dictionary<string, Stream> GetReverbsData() { return new Dictionary<string, Stream>(); }
+		//mxd. When implemented, this returns the REVERBS lump
+		public abstract Dictionary<string, Stream> GetReverbsData(); // { return new Dictionary<string, Stream>(); }
 
-		//mxd. When implemented, this returns the list of voxel model names
-		public virtual string[] GetVoxelNames() { return null; }
+		//mxd. When implemented, this returns the VOXELDEF lump
+		public abstract KeyValuePair<string, Stream> GetVoxeldefData(); // { return new KeyValuePair<string, Stream>(); }
 
-		//mxd. When implemented, this returns the voxel lump
-		public virtual Stream GetVoxelData(string name) { return null; }
+		//mxd. When implemented, this returns the SNDSEQ lump
+		public abstract Dictionary<string, Stream> GetSndSeqData(); // { return new Dictionary<string, Stream>(); }
 
-		//mxd
-		public virtual KeyValuePair<string, Stream> GetVoxeldefData() { return new KeyValuePair<string,Stream>(); }
+		//mxd. When implemented, this returns the ANIMDEFS lump
+		public abstract Dictionary<string, Stream> GetAnimdefsData();
 
-		//mxd. When implemented, this returns the SndSeq lump
-		public virtual List<Stream> GetSndSeqData() { return new List<Stream>(); }
+		//mxd. When implemented, this returns the list of voxel model names
+		public abstract IEnumerable<string> GetVoxelNames(); // { return null; }
+
+		//mxd. When implemented, this returns the voxel lump
+		public abstract Stream GetVoxelData(string name); // { return null; }
 
 		//mxd
-		internal virtual MemoryStream LoadFile(string name) { return null; }
-		internal virtual bool FileExists(string filename) { return false; }
+		internal abstract MemoryStream LoadFile(string name);// { return null; }
+		internal abstract bool FileExists(string filename);// { return false; }
 
 		#endregion
 	}
diff --git a/Source/Core/Data/PK3StructuredReader.cs b/Source/Core/Data/PK3StructuredReader.cs
index cb935865a..0c013857b 100644
--- a/Source/Core/Data/PK3StructuredReader.cs
+++ b/Source/Core/Data/PK3StructuredReader.cs
@@ -409,7 +409,7 @@ namespace CodeImp.DoomBuilder.Data
 
 		#endregion
 
-		#region ================== Decorate
+		#region ================== DECORATE
 
 		// This finds and returns a sprite stream
 		public override Dictionary<string, Stream> GetDecorateData(string pname)
@@ -461,7 +461,7 @@ namespace CodeImp.DoomBuilder.Data
 
 		#endregion
 
-		#region ================== Modeldef (mxd)
+		#region ================== MODELDEF (mxd)
 
 		//mxd
 		public override Dictionary<string, Stream> GetModeldefData() 
@@ -484,10 +484,10 @@ namespace CodeImp.DoomBuilder.Data
 
 		#endregion 
 
-		#region ================== Voxeldef (mxd)
+		#region ================== VOXELDEF (mxd)
 
 		//mxd. This returns the list of voxels, which can be used without VOXELDEF definition
-		public override string[] GetVoxelNames() 
+		public override IEnumerable<string> GetVoxelNames() 
 		{
 			// Error when suspended
 			if(issuspended) throw new Exception("Data reader is suspended");
@@ -557,7 +557,7 @@ namespace CodeImp.DoomBuilder.Data
 		public override Dictionary<string, Stream> GetGldefsData(GameType gametype) 
 		{
 			// Error when suspended
-			if (issuspended) throw new Exception("Data reader is suspended");
+			if(issuspended) throw new Exception("Data reader is suspended");
 
 			Dictionary<string, Stream> streams = new Dictionary<string, Stream>(StringComparer.Ordinal);
 
@@ -565,10 +565,10 @@ namespace CodeImp.DoomBuilder.Data
 			string[] files = GetAllFiles("", false);
 
 			//try to load game specific GLDEFS first
-			if (gametype != GameType.UNKNOWN) 
+			if(gametype != GameType.UNKNOWN) 
 			{
 				string lumpname = Gldefs.GLDEFS_LUMPS_PER_GAME[(int)gametype];
-				foreach (string s in files) 
+				foreach(string s in files) 
 				{
 					if(Path.GetFileNameWithoutExtension(s).ToUpperInvariant() == lumpname)
 						streams.Add(s, LoadFile(s));
@@ -576,7 +576,7 @@ namespace CodeImp.DoomBuilder.Data
 			}
 
 			// Can be several entries
-			foreach (string s in files)
+			foreach(string s in files)
 			{
 				if(Path.GetFileNameWithoutExtension(s).ToUpperInvariant().StartsWith("GLDEFS"))
 					streams.Add(s, LoadFile(s));
@@ -587,7 +587,7 @@ namespace CodeImp.DoomBuilder.Data
 
 		#endregion
 
-		#region ================== Reverbs
+		#region ================== REVERBS (mxd)
 
 		public override Dictionary<string, Stream> GetReverbsData() 
 		{
@@ -616,27 +616,58 @@ namespace CodeImp.DoomBuilder.Data
 
 		#endregion
 
-		#region ================== SndSeq
+		#region ================== SNDSEQ (mxd)
 
-		public override List<Stream> GetSndSeqData() 
+		public override Dictionary<string, Stream> GetSndSeqData() 
 		{
 			// Error when suspended
 			if(issuspended) throw new Exception("Data reader is suspended");
 
-			List<Stream> streams = new List<Stream>();
+			Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
 
 			// Get from wads first
 			//TODO: is this the correct order?..
 			foreach(WADReader wr in wads) 
 			{
-				streams.AddRange(wr.GetSndSeqData());
+				Dictionary<string, Stream> wadstreams = wr.GetSndSeqData();
+				foreach(KeyValuePair<string, Stream> pair in wadstreams) streams.Add(pair.Key, pair.Value);
 			}
 
 			// Then from our own files
 			string foundfile = FindFirstFile("sndseq", false);
 			if(!string.IsNullOrEmpty(foundfile) && FileExists(foundfile))
 			{
-				streams.Add(LoadFile(foundfile));
+				streams.Add(foundfile, LoadFile(foundfile));
+			}
+
+			return streams;
+		}
+
+		#endregion
+
+		#region ================== ANIMDEFS (mxd)
+
+		public override Dictionary<string, Stream> GetAnimdefsData()
+		{
+			// Error when suspended
+			if(issuspended) throw new Exception("Data reader is suspended");
+
+			Dictionary<string, Stream> streams = new Dictionary<string, Stream>();
+
+			// Get from wads first
+			//TODO: is this the correct order?..
+			foreach(WADReader wr in wads)
+			{
+				Dictionary<string, Stream> wadstreams = wr.GetAnimdefsData();
+				foreach(KeyValuePair<string, Stream> pair in wadstreams)
+					streams.Add(pair.Key, pair.Value);
+			}
+
+			// Then from our own files
+			string foundfile = FindFirstFile("animdefs", false);
+			if(!string.IsNullOrEmpty(foundfile) && FileExists(foundfile))
+			{
+				streams.Add(foundfile, LoadFile(foundfile));
 			}
 
 			return streams;
diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs
index 7c4b5d45d..939b7abd0 100644
--- a/Source/Core/Data/WADReader.cs
+++ b/Source/Core/Data/WADReader.cs
@@ -407,7 +407,7 @@ namespace CodeImp.DoomBuilder.Data
 		{
 			// Parse the data
 			TexturesParser parser = new TexturesParser();
-			parser.Parse(stream, filename);
+			parser.Parse(stream, filename, false);
 			if(parser.HasError) parser.LogError(); //mxd
 
 			// Make the textures
@@ -647,7 +647,7 @@ namespace CodeImp.DoomBuilder.Data
 		{
 			// Parse the data
 			TexturesParser parser = new TexturesParser();
-			parser.Parse(stream, filename);
+			parser.Parse(stream, filename, false);
 			if(parser.HasError) parser.LogError(); //mxd
 
 			// Make the textures
@@ -710,7 +710,7 @@ namespace CodeImp.DoomBuilder.Data
 		{
 			// Parse the data
 			TexturesParser parser = new TexturesParser();
-			parser.Parse(stream, filename);
+			parser.Parse(stream, filename, false);
 			if(parser.HasError) parser.LogError(); //mxd
 			
 			// Make the textures
@@ -763,7 +763,7 @@ namespace CodeImp.DoomBuilder.Data
 		#region ================== Voxels (mxd)
 
 		//mxd. This returns the list of voxels, which can be used without VOXELDEF definition
-		public override string[] GetVoxelNames() 
+		public override IEnumerable<string> GetVoxelNames() 
 		{
 			// Error when suspended
 			if(issuspended) throw new Exception("Data reader is suspended");
@@ -908,13 +908,24 @@ namespace CodeImp.DoomBuilder.Data
 		}
 
 		//mxd
-		public override List<Stream> GetSndSeqData() 
+		public override Dictionary<string, Stream> GetSndSeqData() 
 		{
 			if(issuspended) throw new Exception("Data reader is suspended");
 
-			List<Stream> result = new List<Stream>();
+			Dictionary<string, Stream> result = new Dictionary<string, Stream>();
 			Lump lump = file.FindLump("SNDSEQ");
-			if(lump != null) result.Add(lump.Stream);
+			if(lump != null) result.Add(Path.Combine(location.location, "SNDSEQ"), lump.Stream);
+			return result;
+		}
+
+		//mxd
+		public override Dictionary<string, Stream> GetAnimdefsData()
+		{
+			if(issuspended) throw new Exception("Data reader is suspended");
+
+			Dictionary<string, Stream> result = new Dictionary<string, Stream>();
+			Lump lump = file.FindLump("ANIMDEFS");
+			if(lump != null) result.Add(Path.Combine(location.location, "ANIMDEFS"), lump.Stream);
 			return result;
 		}
 
diff --git a/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs b/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs
index 91f6522fb..0d65ee8dc 100644
--- a/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs
+++ b/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs
@@ -42,18 +42,23 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 			specialtokens += "(,)";
 		}
 
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) 
 		{
-			return Parse(stream, sourcefilename, new List<string>(), false, false);
+			return Parse(stream, sourcefilename, new List<string>(), false, false, clearerrors);
 		}
 
-		public bool Parse(Stream stream, string sourcefilename, bool processincludes, bool isinclude)
+		public bool Parse(Stream stream, string sourcefilename, bool processincludes, bool isinclude, bool clearerrors)
 		{
-			return Parse(stream, sourcefilename, includestoskip, processincludes, isinclude);
+			return Parse(stream, sourcefilename, includestoskip, processincludes, isinclude, clearerrors);
 		}
 
-		public bool Parse(Stream stream, string sourcefilename, List<string> configincludes, bool processincludes, bool isinclude) 
+		public bool Parse(Stream stream, string sourcefilename, List<string> configincludes, bool processincludes, bool isinclude, bool clearerrors) 
 		{
+			parsedlumps.Add(sourcefilename);
+			if(isinclude && !includes.Contains(sourcefilename)) includes.Add(sourcefilename);
+			includestoskip = configincludes;
+			int bracelevel = 0;
+
 			// Integrity check
 			if(stream == null || stream.Length == 0)
 			{
@@ -61,12 +66,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 				return false;
 			}
 
-			base.Parse(stream, sourcefilename);
-
-			parsedlumps.Add(sourcefilename);
-			if(isinclude && !includes.Contains(sourcefilename)) includes.Add(sourcefilename);
-			includestoskip = configincludes;
-			int bracelevel = 0;
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Keep local data
 			Stream localstream = datastream;
diff --git a/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs b/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs
index 3ff98a1a8..65c3d7e21 100644
--- a/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs
+++ b/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs
@@ -17,9 +17,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 			actors = new List<ScriptItem>();
 		}
 
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) 
 		{
-			base.Parse(stream, sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Continue until at the end of the stream
 			while(SkipWhitespace(true)) 
diff --git a/Source/Core/GZBuilder/GZDoom/GldefsParser.cs b/Source/Core/GZBuilder/GZDoom/GldefsParser.cs
index d8f10a48d..25064516b 100644
--- a/Source/Core/GZBuilder/GZDoom/GldefsParser.cs
+++ b/Source/Core/GZBuilder/GZDoom/GldefsParser.cs
@@ -39,7 +39,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 
 		#region ================== Delegates
 
-		public delegate void IncludeDelegate(GldefsParser parser, string includefile);
+		public delegate void IncludeDelegate(GldefsParser parser, string includefile, bool clearerrors);
 		public IncludeDelegate OnInclude;
 
 		#endregion
@@ -79,10 +79,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 
 		#region ================== Parsing
 
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) 
 		{
-			base.Parse(stream, sourcefilename);
 			parsedlumps.Add(sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Keep local data
 			Stream localstream = datastream;
@@ -652,7 +652,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 						}
 
 						// Callback to parse this file
-						if(OnInclude != null) OnInclude(this, includelump);
+						if(OnInclude != null) OnInclude(this, includelump, clearerrors);
 
 						// Set our buffers back to continue parsing
 						datastream = localstream;
diff --git a/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs b/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs
index e361b4b17..3988f869a 100644
--- a/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs
+++ b/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs
@@ -17,7 +17,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 	{
 		#region ================== Delegates
 
-		public delegate void IncludeDelegate(MapinfoParser parser, string includefile);
+		public delegate void IncludeDelegate(MapinfoParser parser, string includefile, bool clearerror);
 		public IncludeDelegate OnInclude;
 
 		#endregion
@@ -59,17 +59,17 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 		#region ================== Parsing
 
 
-		override public bool Parse(Stream stream, string sourcefilename)
+		override public bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
 			if(string.IsNullOrEmpty(mapname)) throw new NotSupportedException("MapName is required!");
-			return Parse(stream, sourcefilename, mapname);
+			return Parse(stream, sourcefilename, mapname, clearerrors);
 		}
 
-		public bool Parse(Stream stream, string sourcefilename, string mapname) 
+		public bool Parse(Stream stream, string sourcefilename, string mapname, bool clearerrors) 
 		{
-			base.Parse(stream, sourcefilename);
 			this.mapname = mapname.ToLowerInvariant();
 			parsedlumps.Add(sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			while(SkipWhitespace(true)) 
 			{
@@ -77,7 +77,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 				if(!string.IsNullOrEmpty(token)) 
 				{
 					token = token.ToLowerInvariant();
-					if(ParseBlock(token)) break;
+					if(ParseBlock(token, clearerrors)) break;
 				}
 			}
 
@@ -93,7 +93,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 		}
 
 		//returns true if parsing is finished
-		private bool ParseBlock(string token) 
+		private bool ParseBlock(string token, bool clearerrors) 
 		{
 			// Keep local data
 			Stream localstream = datastream;
@@ -333,7 +333,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 //block end
 					else if(token == "}") 
 					{
-						return ParseBlock(token);
+						return ParseBlock(token, clearerrors);
 					}
 //child block
 					else if(token == "{")
@@ -362,7 +362,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 				{
 					// Callback to parse this file
 					if(OnInclude != null)
-						OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
+						OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), clearerrors);
 
 					// Set our buffers back to continue parsing
 					datastream = localstream;
diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs
index 954d16bf7..1cddc3e5c 100644
--- a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs
+++ b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs
@@ -17,10 +17,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 		}
 
 		//should be called after all decorate actors are parsed 
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
-			base.Parse(stream, sourcefilename);
 			entries = new Dictionary<string, ModelData>(StringComparer.Ordinal);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Continue until at the end of the stream
 			while(SkipWhitespace(true)) 
diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs
index 13d3995fa..c0454a939 100644
--- a/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs
+++ b/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs
@@ -21,9 +21,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 			models = new List<ScriptItem>();
 		}
 
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) 
 		{
-			base.Parse(stream, sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Continue until at the end of the stream
 			while(SkipWhitespace(true)) 
diff --git a/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs b/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs
index e018b9039..1928108bc 100644
--- a/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs
+++ b/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs
@@ -19,12 +19,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
 			scriptType = ScriptType.UNKNOWN;
 		}
 		
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) 
 		{
-			base.Parse(stream, sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Continue until at the end of the stream
-			while (SkipWhitespace(true)) 
+			while(SkipWhitespace(true)) 
 			{
 				string token = ReadToken();
 
diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs
index 058e98f1e..1861544a0 100644
--- a/Source/Core/General/MapManager.cs
+++ b/Source/Core/General/MapManager.cs
@@ -1928,10 +1928,10 @@ namespace CodeImp.DoomBuilder
 					if(stream != null && stream.Length > 0 && scriptconfig != null && scriptconfig.Compiler != null)
 					{
 						// Get script names
-						AcsParserSE parser = new AcsParserSE { OnInclude = (se, path) => se.Parse(General.Map.Data.LoadFile(path), path, true, true) };
+						AcsParserSE parser = new AcsParserSE { OnInclude = (se, path) => se.Parse(General.Map.Data.LoadFile(path), path, true, true, false) };
 
 						//INFO: CompileLump() prepends lumpname with "?" to distinguish between temporary files and files compiled in place
-						if(parser.Parse(stream, "?SCRIPTS", scriptconfig.Compiler.Files, true, false))
+						if(parser.Parse(stream, "?SCRIPTS", scriptconfig.Compiler.Files, true, false, false))
 						{
 							// Add them to arrays
 							namedscriptslist.AddRange(parser.NamedScripts);
diff --git a/Source/Core/ZDoom/AnimdefsParser.cs b/Source/Core/ZDoom/AnimdefsParser.cs
new file mode 100644
index 000000000..ff9941afe
--- /dev/null
+++ b/Source/Core/ZDoom/AnimdefsParser.cs
@@ -0,0 +1,147 @@
+#region ================== Namespaces
+
+using System.Collections.Generic;
+using System.IO;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.ZDoom
+{
+	public struct CameraTextureData
+	{
+		public string Name;
+		public int Width;
+		public int Height;
+		public float ScaleX;
+		public float ScaleY;
+		public bool WorldPanning;
+		public bool FitTexture;
+	}
+	
+	//mxd. Currently this only parses cameratextures
+	internal sealed class AnimdefsParser : ZDTextParser
+	{
+		#region ================== Variables
+
+		private readonly Dictionary<string, CameraTextureData> cameratextures;
+
+		#endregion
+
+		#region ================== Properties
+
+		public Dictionary<string, CameraTextureData> CameraTextures { get { return cameratextures; } }
+
+		#endregion
+
+		#region ================== Constructor
+
+		internal AnimdefsParser()
+		{
+			cameratextures = new Dictionary<string, CameraTextureData>();
+		}
+
+		#endregion
+
+		#region ================== Parsing
+
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
+		{
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
+
+			// Continue until at the end of the stream
+			while(SkipWhitespace(true))
+			{
+				string token = ReadToken();
+				if(string.IsNullOrEmpty(token) || string.Compare(token, "CAMERATEXTURE", true) != 0) continue;
+
+				// Texture name
+				string texturename = StripTokenQuotes(ReadToken(false));
+				if(string.IsNullOrEmpty(texturename))
+				{
+					ReportError("Expected camera texture name");
+					break;
+				}
+
+				// Width
+				int width = -1;
+				SkipWhitespace(true);
+				if(!ReadSignedInt(ref width) || width < 1)
+				{
+					ReportError("Expected camera texture width");
+					break;
+				}
+
+				// Height
+				int height = -1;
+				SkipWhitespace(true);
+				if(!ReadSignedInt(ref height) || height < 1)
+				{
+					ReportError("Expected camera texture height");
+					break;
+				}
+
+				// "Fit" keyword?
+				bool worldpanning = false;
+				bool fit = false;
+				float scalex = 1.0f;
+				float scaley = 1.0f;
+
+				if(NextTokenIs("fit", false))
+				{
+					fit = true;
+					int fitwidth = width;
+					int fitheight = height;
+					
+					// Fit width
+					SkipWhitespace(true);
+					if(!ReadSignedInt(ref fitwidth) || fitwidth < 1)
+					{
+						ReportError("Expected camera texture fit width");
+						break;
+					}
+
+					// Fit height
+					SkipWhitespace(true);
+					if(!ReadSignedInt(ref fitheight) || fitheight < 1)
+					{
+						ReportError("Expected camera texture fit height");
+						break;
+					}
+
+					// Update scale
+					scalex = (float)fitwidth / width;
+					scaley = (float)fitheight / height;
+
+					// WorldPanning
+					worldpanning = NextTokenIs("worldpanning", false);
+				}
+				else if(NextTokenIs("worldpanning", false))
+				{
+					worldpanning = true;
+				}
+
+				// Check results
+				if(cameratextures.ContainsKey(texturename.ToUpperInvariant()))
+				{
+					ReportError("Camera texture '" + texturename + "' is defined more than once");
+					break;
+				}
+
+				// Store results
+				texturename = texturename.ToUpperInvariant();
+				cameratextures[texturename] = new CameraTextureData { Name = texturename, Width = width, Height = height, 
+																	  ScaleX = scalex, ScaleY = scaley, 
+																	  WorldPanning = worldpanning, FitTexture = fit };
+			}
+
+			return true;
+		}
+
+		protected override string GetLanguageType()
+		{
+			return "ANIMDEFS";
+		}
+
+		#endregion
+	}
+}
diff --git a/Source/Core/ZDoom/DecorateParser.cs b/Source/Core/ZDoom/DecorateParser.cs
index c7215d084..d5ad82a86 100644
--- a/Source/Core/ZDoom/DecorateParser.cs
+++ b/Source/Core/ZDoom/DecorateParser.cs
@@ -104,7 +104,7 @@ namespace CodeImp.DoomBuilder.ZDoom
 		// Returns false on errors
 		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
-			base.Parse(stream, sourcefilename, clearerrors);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 			
 			// Keep local data
 			Stream localstream = datastream;
diff --git a/Source/Core/ZDoom/ReverbsParser.cs b/Source/Core/ZDoom/ReverbsParser.cs
index 5186a0de7..c2f72af19 100644
--- a/Source/Core/ZDoom/ReverbsParser.cs
+++ b/Source/Core/ZDoom/ReverbsParser.cs
@@ -20,9 +20,9 @@ namespace CodeImp.DoomBuilder.ZDoom
 			combinedargs = new List<int>();
 		}
 		
-		public override bool Parse(Stream stream, string sourcefilename)
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
-			base.Parse(stream, sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			// Continue until at the end of the stream
 			while(SkipWhitespace(true)) 
diff --git a/Source/Core/ZDoom/SndSeqParser.cs b/Source/Core/ZDoom/SndSeqParser.cs
index 3459884bd..613baed74 100644
--- a/Source/Core/ZDoom/SndSeqParser.cs
+++ b/Source/Core/ZDoom/SndSeqParser.cs
@@ -31,9 +31,9 @@ namespace CodeImp.DoomBuilder.ZDoom
 
 		#region ================== Parsing
 
-		public override bool Parse(Stream stream, string sourcefilename)
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
-			base.Parse(stream, sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 			
 			char[] dots = new[] { ':' };
 			char[] brace = new[] { '[' };
diff --git a/Source/Core/ZDoom/TexturesParser.cs b/Source/Core/ZDoom/TexturesParser.cs
index 7f62efe5e..5574fde79 100644
--- a/Source/Core/ZDoom/TexturesParser.cs
+++ b/Source/Core/ZDoom/TexturesParser.cs
@@ -73,9 +73,9 @@ namespace CodeImp.DoomBuilder.ZDoom
 
 		// This parses the given stream
 		// Returns false on errors
-		public override bool Parse(Stream stream, string sourcefilename)
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
-			base.Parse(stream, sourcefilename);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 
 			//mxd. Make vitrual path from filename
 			string virtualpath = sourcefilename.Substring(8).TrimStart(pathtrimchars);
diff --git a/Source/Core/ZDoom/VoxeldefParser.cs b/Source/Core/ZDoom/VoxeldefParser.cs
index 89a46d42f..99efd89fb 100644
--- a/Source/Core/ZDoom/VoxeldefParser.cs
+++ b/Source/Core/ZDoom/VoxeldefParser.cs
@@ -16,10 +16,10 @@ namespace CodeImp.DoomBuilder.ZDoom
 		private Dictionary<string, ModelData> entries; //sprite name, entry
 		internal Dictionary<string, ModelData> Entries { get { return entries; } }
 
-		public override bool Parse(Stream stream, string sourcefilename) 
+		public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) 
 		{
-			base.Parse(stream, sourcefilename);
 			entries = new Dictionary<string, ModelData>(StringComparer.Ordinal);
+			if(!base.Parse(stream, sourcefilename, clearerrors)) return false;
 			string prevToken = string.Empty;
 
 			List<string> spriteNames = new List<string>();
diff --git a/Source/Core/ZDoom/ZDTextParser.cs b/Source/Core/ZDoom/ZDTextParser.cs
index b95331b68..62f18438c 100644
--- a/Source/Core/ZDoom/ZDTextParser.cs
+++ b/Source/Core/ZDoom/ZDTextParser.cs
@@ -75,23 +75,17 @@ namespace CodeImp.DoomBuilder.ZDoom
 
 		#region ================== Parsing
 
-		// This parses the given decorate stream
-		// Returns false on errors
-		public virtual bool Parse(Stream stream, string sourcefilename)
-		{
-			return Parse(stream, sourcefilename, false);
-		}
-
-		// This parses the given decorate stream (mxd)
-		// Returns false on errors
+		//mxd. This parses the given decorate stream. Returns false on errors
 		public virtual bool Parse(Stream stream, string sourcefilename, bool clearerrors)
 		{
-			// Clear error status (mxd)
-			if(clearerrors) 
+			//mxd. Clear error status?
+			if(clearerrors) ClearError();
+
+			//mxd. Integrity check
+			if(stream == null || stream.Length == 0)
 			{
-				errordesc = null;
-				errorsource = null;
-				errorline = -1;
+				ReportError("Unable to load '" + sourcefilename + "'!");
+				return false;
 			}
 			
 			datastream = stream;
@@ -425,7 +419,7 @@ namespace CodeImp.DoomBuilder.ZDoom
 			if(!SkipWhitespace(true)) return false;
 			string token = ReadToken();
 
-			if(token != expectedtoken) 
+			if(string.Compare(token, expectedtoken, true) != 0)
 			{
 				if(reporterror) ReportError("expected '" + expectedtoken + "', but got '" + token + "'");
 
@@ -438,34 +432,36 @@ namespace CodeImp.DoomBuilder.ZDoom
 		}
 
 		//mxd
+		protected internal bool ReadSignedFloat(ref float value) { return ReadSignedFloat(StripTokenQuotes(ReadToken(false)), ref value); }
 		protected internal bool ReadSignedFloat(string token, ref float value) 
 		{
 			int sign = 1;
-			if (token == "-") 
+			if(token == "-") 
 			{
 				sign = -1;
-				token = StripTokenQuotes(ReadToken());
+				token = StripTokenQuotes(ReadToken(false));
 			}
 
 			float val;
 			bool success = float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val);
-			if (success) value = val * sign;
+			if(success) value = val * sign;
 			return success;
 		}
 
 		//mxd
+		protected internal bool ReadSignedInt(ref int value) { return ReadSignedInt(StripTokenQuotes(ReadToken(false)), ref value); }
 		protected internal bool ReadSignedInt(string token, ref int value) 
 		{
 			int sign = 1;
-			if (token == "-") 
+			if(token == "-") 
 			{
 				sign = -1;
-				token = StripTokenQuotes(ReadToken());
+				token = StripTokenQuotes(ReadToken(false));
 			}
 
 			int val;
 			bool success = int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out val);
-			if (success) value = val * sign;
+			if(success) value = val * sign;
 			return success;
 		}
 		
@@ -497,11 +493,11 @@ namespace CodeImp.DoomBuilder.ZDoom
 		}
 
 		//mxd
-		internal void ClearError()
+		protected void ClearError()
 		{
-			errorline = 0;
 			errordesc = null;
 			errorsource = null;
+			errorline = CompilerError.NO_LINE_NUMBER;
 		}
 
 		//mxd 
-- 
GitLab