diff --git a/Build/Compilers/BCC/bcc.cfg b/Build/Compilers/BCC/bcc.cfg index f9f1161e..41f747bc 100644 --- a/Build/Compilers/BCC/bcc.cfg +++ b/Build/Compilers/BCC/bcc.cfg @@ -9,5 +9,7 @@ compilers { interface = "AccCompiler"; program = "bcc.exe"; + zcommon = "zcommon.acs"; + std = "std.acs"; } } diff --git a/Build/Compilers/Hexen/acc.cfg b/Build/Compilers/Hexen/acc.cfg index eddbd7dc..d20ad661 100644 --- a/Build/Compilers/Hexen/acc.cfg +++ b/Build/Compilers/Hexen/acc.cfg @@ -9,5 +9,9 @@ compilers { interface = "AccCompiler"; program = "acc.exe"; + zcommon = "common.acs"; + zdefs = "defs.acs"; + zspecial = "specials.acs"; + zwvars = "wvars.acs"; } } diff --git a/Build/Compilers/ZDoom/acc.cfg b/Build/Compilers/ZDoom/acc.cfg index c001fc7c..b3e10030 100644 --- a/Build/Compilers/ZDoom/acc.cfg +++ b/Build/Compilers/ZDoom/acc.cfg @@ -9,5 +9,9 @@ compilers { interface = "AccCompiler"; program = "acc.exe"; + zcommon = "zcommon.acs"; + zdefs = "zdefs.acs"; + zspecial = "zspecial.acs"; + zwvars = "zwvars.acs"; } } diff --git a/Build/Compilers/Zandronum/acc.cfg b/Build/Compilers/Zandronum/acc.cfg index 869a826c..13eddf2e 100644 --- a/Build/Compilers/Zandronum/acc.cfg +++ b/Build/Compilers/Zandronum/acc.cfg @@ -9,5 +9,9 @@ compilers { interface = "AccCompiler"; program = "acc.exe"; + zcommon = "zcommon.acs"; + zdefs = "zdefs.acs"; + zspecial = "zspecial.acs"; + zwvars = "zwvars.acs"; } } diff --git a/Build/SharpCompress.3.5.dll b/Build/SharpCompress.3.5.dll index 103de782..56828e26 100644 Binary files a/Build/SharpCompress.3.5.dll and b/Build/SharpCompress.3.5.dll differ diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index 6cf6e8e6..338f3cbe 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -533,7 +533,9 @@ - + + False + @@ -860,7 +862,6 @@ - diff --git a/Source/Core/Compilers/AccCompiler.cs b/Source/Core/Compilers/AccCompiler.cs index 7e314956..add7b874 100644 --- a/Source/Core/Compilers/AccCompiler.cs +++ b/Source/Core/Compilers/AccCompiler.cs @@ -17,7 +17,6 @@ #region ================== Namespaces using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using CodeImp.DoomBuilder.Config; @@ -44,7 +43,7 @@ namespace CodeImp.DoomBuilder.Compilers #region ================== Constructor // Constructor - public AccCompiler(CompilerInfo info) : base(info) + public AccCompiler(CompilerInfo info) : base(info, false) { } diff --git a/Source/Core/Compilers/Compiler.cs b/Source/Core/Compilers/Compiler.cs index 648f13b8..2dc37fc6 100644 --- a/Source/Core/Compilers/Compiler.cs +++ b/Source/Core/Compilers/Compiler.cs @@ -71,7 +71,7 @@ namespace CodeImp.DoomBuilder.Compilers #region ================== Constructor / Disposer // Constructor - protected Compiler(CompilerInfo info) + protected Compiler(CompilerInfo info, bool copyrequiredfiles) { // Initialize this.info = info; @@ -83,10 +83,15 @@ namespace CodeImp.DoomBuilder.Compilers // Create temporary directory tempdir = Directory.CreateDirectory(General.MakeTempDirname()); workingdir = tempdir.FullName; - - // Copy required files to the temp directory - General.WriteLogLine("Copying required files for compiler..."); - CopyRequiredFiles(); + + //mxd. ACC compiler itself is not copied to tempdir anymore, so we don't need to move it's include files + //but we still need tempdir to compile SCRIPTS lump. + if(copyrequiredfiles) + { + // Copy required files to the temp directory + General.WriteLogLine("Copying required files for compiler..."); + CopyRequiredFiles(); + } } // Disposer diff --git a/Source/Core/Compilers/NodesCompiler.cs b/Source/Core/Compilers/NodesCompiler.cs index 049ffa74..5bf25d52 100644 --- a/Source/Core/Compilers/NodesCompiler.cs +++ b/Source/Core/Compilers/NodesCompiler.cs @@ -43,7 +43,7 @@ namespace CodeImp.DoomBuilder.Compilers #region ================== Constructor / Disposer // Constructor - public NodesCompiler(CompilerInfo info) : base(info) + public NodesCompiler(CompilerInfo info) : base(info, true) { // Initialize diff --git a/Source/Core/Config/ConfigurationInfo.cs b/Source/Core/Config/ConfigurationInfo.cs index 64c04f71..45867b26 100644 --- a/Source/Core/Config/ConfigurationInfo.cs +++ b/Source/Core/Config/ConfigurationInfo.cs @@ -454,13 +454,14 @@ namespace CodeImp.DoomBuilder.Config { // Copy the things filters from game configuration foreach(ThingsFilter f in gameconfig.ThingsFilters) - { thingsfilters.Add(new ThingsFilter(f)); - } } - //mxd. Validate filters - foreach(ThingsFilter f in thingsfilters) f.Validate(); + //mxd. Validate filters. Do it only for currently used ConfigInfo + if(General.Map != null && General.Map.ConfigSettings == this) + { + foreach(ThingsFilter f in thingsfilters) f.Validate(); + } } // Go for all available editing modes diff --git a/Source/Core/Controls/ScriptDocumentTab.cs b/Source/Core/Controls/ScriptDocumentTab.cs index 85fde7e1..3536a2c4 100644 --- a/Source/Core/Controls/ScriptDocumentTab.cs +++ b/Source/Core/Controls/ScriptDocumentTab.cs @@ -410,6 +410,7 @@ namespace CodeImp.DoomBuilder.Controls } // Put some text in the navigator (but don't actually trigger selection event) + navigator.Enabled = (navigator.Items.Count > 0); if(navigator.Items.Count > 0) { preventchanges = true; @@ -421,32 +422,43 @@ namespace CodeImp.DoomBuilder.Controls //mxd private void UpdateNavigatorDecorate(MemoryStream stream) { - if (stream == null) return; - + if(stream == null) return; navigator.Items.Clear(); DecorateParserSE parser = new DecorateParserSE(); - parser.Parse(stream, "DECORATE"); - navigator.Items.AddRange(parser.Actors.ToArray()); + if(parser.Parse(stream, "DECORATE")) + { + navigator.Items.AddRange(parser.Actors.ToArray()); + } + + if(parser.HasError) + { + panel.ShowErrors(new List { new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine) }); + } } //mxd private void UpdateNavigatorModeldef(MemoryStream stream) { - if (stream == null) return; - + if(stream == null) return; navigator.Items.Clear(); ModeldefParserSE parser = new ModeldefParserSE(); - parser.Parse(stream, "MODELDEF"); - navigator.Items.AddRange(parser.Models.ToArray()); + if(parser.Parse(stream, "MODELDEF")) + { + navigator.Items.AddRange(parser.Models.ToArray()); + } + + if(parser.HasError) + { + panel.ShowErrors(new List { new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine) }); + } } //mxd private void UpdateNavigatorAcs(MemoryStream stream) { - if (stream == null) return; - + if(stream == null) return; navigator.Items.Clear(); AcsParserSE parser = new AcsParserSE { AddArgumentsToScriptNames = true, IsMapScriptsLump = this is ScriptLumpDocumentTab }; @@ -456,17 +468,28 @@ namespace CodeImp.DoomBuilder.Controls navigator.Items.AddRange(parser.NumberedScripts.ToArray()); navigator.Items.AddRange(parser.Functions.ToArray()); } + + if(parser.HasError) + { + panel.ShowErrors(new List { new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine) }); + } } //mxd 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)) { - if (parser.ScriptType != ScriptType.UNKNOWN && config.ScriptType != parser.ScriptType) + if(parser.ScriptType != ScriptType.UNKNOWN && config.ScriptType != parser.ScriptType) return parser.ScriptType; } + + if(parser.HasError) + { + panel.ShowErrors(new List { new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine) }); + } + return ScriptType.UNKNOWN; } diff --git a/Source/Core/Controls/ScriptEditorControl.cs b/Source/Core/Controls/ScriptEditorControl.cs index 7d337d26..bec26f45 100644 --- a/Source/Core/Controls/ScriptEditorControl.cs +++ b/Source/Core/Controls/ScriptEditorControl.cs @@ -250,8 +250,6 @@ namespace CodeImp.DoomBuilder.Controls // This sets up the script editor with a script configuration public void SetupStyles(ScriptConfiguration config) { - Stream lexersdata; - StreamReader lexersreader; Configuration lexercfg = new Configuration(); // Make collections @@ -259,7 +257,7 @@ namespace CodeImp.DoomBuilder.Controls SortedList autocompletelist = new SortedList(StringComparer.Ordinal); // Keep script configuration - if(scriptconfig != config) scriptconfig = config; + scriptconfig = config; // Find a resource named Lexers.cfg string[] resnames = General.ThisAssembly.GetManifestResourceNames(); @@ -269,8 +267,8 @@ namespace CodeImp.DoomBuilder.Controls if(rn.EndsWith(LEXERS_RESOURCE, StringComparison.InvariantCultureIgnoreCase)) { // Get a stream from the resource - lexersdata = General.ThisAssembly.GetManifestResourceStream(rn); - lexersreader = new StreamReader(lexersdata, Encoding.ASCII); + Stream lexersdata = General.ThisAssembly.GetManifestResourceStream(rn); + StreamReader lexersreader = new StreamReader(lexersdata, Encoding.ASCII); // Load configuration from stream lexercfg.InputConfiguration(lexersreader.ReadToEnd()); @@ -426,8 +424,10 @@ namespace CodeImp.DoomBuilder.Controls functionbar.Visible = (scriptconfig.FunctionRegEx.Length > 0); // Rearrange the layout + bool ischanged = changed; //mxd. Don't want the "changed" status to change when changing text styles scriptedit.ClearDocumentStyle(); scriptedit.SetText(scriptedit.GetText(scriptedit.TextSize)); + changed = ischanged; //mxd this.PerformLayout(); } diff --git a/Source/Core/Controls/ScriptFileDocumentTab.cs b/Source/Core/Controls/ScriptFileDocumentTab.cs index 82e99b49..ad2a85fc 100644 --- a/Source/Core/Controls/ScriptFileDocumentTab.cs +++ b/Source/Core/Controls/ScriptFileDocumentTab.cs @@ -175,23 +175,24 @@ namespace CodeImp.DoomBuilder.Controls AcsParserSE parser = new AcsParserSE { OnInclude = (se, path) => se.Parse(General.Map.Data.LoadFile(path), path, true, true) }; using(FileStream stream = File.OpenRead(filepathname)) { - if(!parser.Parse(stream, inputfile, scriptconfig.Compiler.Files, true, false)) + if(!parser.Parse(stream, filepathname, scriptconfig.Compiler.Files, true, false)) { // Check for errors if(parser.HasError) { errors.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine)); panel.ShowErrors(errors); - compiler.Dispose(); - return; } + + compiler.Dispose(); + return; } } - // Only works for libraries + //mxd. Only works for libraries if(!parser.IsLibrary) { - errors.Add(new CompilerError("External ACS files can only be compiled as libraries!", inputfile)); + errors.Add(new CompilerError("External ACS files can only be compiled as libraries!", filepathname)); panel.ShowErrors(errors); compiler.Dispose(); return; @@ -349,6 +350,10 @@ namespace CodeImp.DoomBuilder.Controls string ext = (config.Extensions.Length > 0 ? "." + config.Extensions[0] : ""); SetTitle("Untitled" + ext); } + else + { + UpdateTitle(); //mxd + } //mxd base.ChangeScriptConfig(newconfig); diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs index 19d439fd..4dd39e32 100644 --- a/Source/Core/Data/DataManager.cs +++ b/Source/Core/Data/DataManager.cs @@ -1404,9 +1404,8 @@ namespace CodeImp.DoomBuilder.Data char[] catsplitter = new[] {Path.AltDirectorySeparatorChar}; //mxd // Create new parser - decorate = new DecorateParser(); - decorate.OnInclude = LoadDecorateFromLocation; - + decorate = new DecorateParser { OnInclude = LoadDecorateFromLocation }; + // Only load these when the game configuration supports the use of decorate if(!string.IsNullOrEmpty(General.Map.Config.DecorateGames)) { @@ -1423,12 +1422,11 @@ namespace CodeImp.DoomBuilder.Data group.Value.Seek(0, SeekOrigin.Begin); decorate.Parse(group.Value, group.Key, true); - // Check for errors + //mxd. DECORATE lumps are interdepandable. Can't carry on... if(decorate.HasError) { - General.ErrorLogger.Add(ErrorType.Error, "DECORATE error in '" + decorate.ErrorSource - + (decorate.ErrorLine != CompilerError.NO_LINE_NUMBER ? "', line " + decorate.ErrorLine : "'") + ". " + decorate.ErrorDescription + "."); - break; + decorate.LogError(); //mxd + return counter; } } } @@ -1823,6 +1821,13 @@ 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(); + } } } @@ -1895,19 +1900,29 @@ namespace CodeImp.DoomBuilder.Data currentreader = dr; KeyValuePair group = dr.GetVoxeldefData(); - if(group.Value != null && parser.Parse(group.Value, group.Key)) + if(group.Value != null) { - foreach(KeyValuePair entry in parser.Entries) + if(parser.Parse(group.Value, group.Key)) { - foreach(KeyValuePair> sc in sprites) + foreach(KeyValuePair entry in parser.Entries) { - if(sc.Key.Contains(entry.Key)) + foreach(KeyValuePair> sc in sprites) { - foreach(int id in sc.Value) modeldefentries[id] = entry.Value; - processed.Add(entry.Key, false); + if(sc.Key.Contains(entry.Key)) + { + foreach(int id in sc.Value) modeldefentries[id] = entry.Value; + processed.Add(entry.Key, false); + } } } } + + // Report errors? + if(parser.HasError) + { + parser.LogError(); + parser.ClearError(); + } } } @@ -1939,7 +1954,7 @@ namespace CodeImp.DoomBuilder.Data GldefsParser parser = new GldefsParser { OnInclude = ParseFromLocation }; - //load gldefs from resources + // Load gldefs from resources foreach(DataReader dr in containers) { currentreader = dr; @@ -1947,7 +1962,16 @@ namespace CodeImp.DoomBuilder.Data Dictionary streams = dr.GetGldefsData(General.Map.Config.GameType); foreach(KeyValuePair group in streams) + { parser.Parse(group.Value, group.Key); + + // Gldefs can be interdependable. Can't carry on + if(parser.HasError) + { + parser.LogError(); + return; + } + } } //create gldefsEntries dictionary @@ -1957,12 +1981,7 @@ namespace CodeImp.DoomBuilder.Data if(actorsByClass.ContainsKey(e.Key) && parser.LightsByName.ContainsKey(e.Value)) { foreach(int i in actorsByClass[e.Key]) - { - if(gldefsentries.ContainsKey(i)) - gldefsentries[i] = parser.LightsByName[e.Value]; - else - gldefsentries.Add(i, parser.LightsByName[e.Value]); - } + gldefsentries[i] = parser.LightsByName[e.Value]; } else if(!decorate.AllActorsByClass.ContainsKey(e.Key)) { @@ -1978,7 +1997,7 @@ namespace CodeImp.DoomBuilder.Data private void LoadMapInfo(out Dictionary spawnnums, out Dictionary doomednums) { MapinfoParser parser = new MapinfoParser { OnInclude = ParseFromLocation }; - + foreach(DataReader dr in containers) { currentreader = dr; @@ -1987,7 +2006,20 @@ namespace CodeImp.DoomBuilder.Data foreach(KeyValuePair 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); + + //MAPINFO lumps are interdependable. Can't carry on... + if(parser.HasError) + { + parser.LogError(); + + // No nulls allowed! + spawnnums = new Dictionary(); + doomednums = new Dictionary(); + mapinfo = new MapInfo(); + + return; + } } } @@ -2024,6 +2056,13 @@ namespace CodeImp.DoomBuilder.Data { // Parse the data parser.Parse(group.Value, group.Key); + + // Report errors? + if(parser.HasError) + { + parser.LogError(); + parser.ClearError(); + } } } @@ -2049,6 +2088,13 @@ namespace CodeImp.DoomBuilder.Data foreach(Stream s in streams) { if(s != null) parser.Parse(s, "SNDSEQ"); + + // Report errors? + if(parser.HasError) + { + parser.LogError(); + parser.ClearError(); + } } } diff --git a/Source/Core/Data/PK3Reader.cs b/Source/Core/Data/PK3Reader.cs index b94db119..5888ed6d 100644 --- a/Source/Core/Data/PK3Reader.cs +++ b/Source/Core/Data/PK3Reader.cs @@ -71,7 +71,7 @@ namespace CodeImp.DoomBuilder.Data IReader reader = archive.ExtractAllEntries(); while(reader.MoveToNextEntry()) { - if(reader.Entry.IsDirectory) continue; + if(reader.Entry.IsDirectory || !CheckInvalidPathChars(reader.Entry.Key)) continue; MemoryStream s = new MemoryStream(); reader.WriteEntryTo(s); @@ -83,8 +83,8 @@ namespace CodeImp.DoomBuilder.Data { foreach(IArchiveEntry entry in archive.Entries) { - if(entry.IsDirectory) continue; - fileentries.Add(new DirectoryFileEntry(entry.Key)); + if(!entry.IsDirectory && CheckInvalidPathChars(entry.Key)) + fileentries.Add(new DirectoryFileEntry(entry.Key)); } } @@ -415,18 +415,18 @@ namespace CodeImp.DoomBuilder.Data string fn = filename.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); //mxd //mxd. This works waaaaaay faster with 7z archive - if (archivetype == ArchiveType.SevenZip) + if(archivetype == ArchiveType.SevenZip) { fn = fn.ToLowerInvariant(); if (sevenzipentries.ContainsKey(fn)) filedata = new MemoryStream(sevenzipentries[fn]); } else { - lock (this) + lock(this) { UpdateArchive(true); - foreach (var entry in archive.Entries) + foreach(var entry in archive.Entries) { if(entry.IsDirectory) continue; @@ -444,7 +444,7 @@ namespace CodeImp.DoomBuilder.Data } // Nothing found? - if (filedata == null) + if(filedata == null) { //mxd General.ErrorLogger.Add(ErrorType.Error, "Cannot find the file '" + filename + "' in archive '" + location.location + "'."); @@ -466,7 +466,31 @@ namespace CodeImp.DoomBuilder.Data filedata.Dispose(); return tempfile; } - + + //mxd. This replicates System.IO.Path.CheckInvalidPathChars() internal function + private bool CheckInvalidPathChars(string path) + { + foreach(char c in path) + { + int num = c; + switch(num) + { + case 34: + case 60: + case 62: + case 124: + General.ErrorLogger.Add(ErrorType.Error, "Error in \"" + location.location + "\": unsupported character \"" + c + "\" in path \"" + path + "\". File loading was skipped."); + return false; + + default: + if(num >= 32) continue; + else goto case 34; + } + } + + return true; + } + #endregion } } diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs index cfefe4b3..7c4b5d45 100644 --- a/Source/Core/Data/WADReader.cs +++ b/Source/Core/Data/WADReader.cs @@ -408,21 +408,14 @@ namespace CodeImp.DoomBuilder.Data // Parse the data TexturesParser parser = new TexturesParser(); parser.Parse(stream, filename); + if(parser.HasError) parser.LogError(); //mxd // Make the textures foreach(TextureStructure t in parser.Textures) { - if(t.Name.Length > 0) - { - // Add the texture - ImageData img = t.MakeImage(); - images.Add(img); - } - else - { - // Can't load image without name - General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed texture from \"" + filename + "\". Please consider giving names to your resources."); - } + // Add the texture + ImageData img = t.MakeImage(); + images.Add(img); } } @@ -655,21 +648,14 @@ namespace CodeImp.DoomBuilder.Data // Parse the data TexturesParser parser = new TexturesParser(); parser.Parse(stream, filename); + if(parser.HasError) parser.LogError(); //mxd // Make the textures foreach(TextureStructure t in parser.Flats) { - if(t.Name.Length > 0) - { - // Add the texture - ImageData img = t.MakeImage(); - images.Add(img); - } - else - { - // Can't load image without name - General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed flat from \"" + filename + "\". Please consider giving names to your resources."); - } + // Add the texture + ImageData img = t.MakeImage(); + images.Add(img); } } @@ -725,21 +711,14 @@ namespace CodeImp.DoomBuilder.Data // Parse the data TexturesParser parser = new TexturesParser(); parser.Parse(stream, filename); + if(parser.HasError) parser.LogError(); //mxd // Make the textures foreach(TextureStructure t in parser.Sprites) { - if(t.Name.Length > 0) - { - // Add the sprite - ImageData img = t.MakeImage(); - images.Add(img); - } - else - { - // Can't load image without name - General.ErrorLogger.Add(ErrorType.Error, "Can't load an unnamed sprite from \"" + filename + "\". Please consider giving names to your resources."); - } + // Add the sprite + ImageData img = t.MakeImage(); + images.Add(img); } } diff --git a/Source/Core/Editing/ThingsFilter.cs b/Source/Core/Editing/ThingsFilter.cs index 75926524..d8a2e553 100644 --- a/Source/Core/Editing/ThingsFilter.cs +++ b/Source/Core/Editing/ThingsFilter.cs @@ -236,36 +236,6 @@ namespace CodeImp.DoomBuilder.Editing for(int i = 0; i < Thing.NUM_ARGS; i++) thingargs[i] = -1; } if(!General.Map.FormatInterface.HasCustomFields) customfields.Clear(); - - //mxd. We don't want to keep unknown flags (like flags from different map format) - if(General.Map.Config != null && General.Map.Config.ThingFlags != null) - { - List unknownfields = new List(); - foreach(String s in forbiddenfields) - { - if(!General.Map.Config.ThingFlags.ContainsKey(s)) - unknownfields.Add(s); - } - - if(unknownfields.Count > 0) - { - foreach(String s in unknownfields) - forbiddenfields.Remove(s); - } - - unknownfields = new List(); - foreach(String s in requiredfields) - { - if(!General.Map.Config.ThingFlags.ContainsKey(s)) - unknownfields.Add(s); - } - - if(unknownfields.Count > 0) - { - foreach(String s in unknownfields) - requiredfields.Remove(s); - } - } } } @@ -274,6 +244,32 @@ namespace CodeImp.DoomBuilder.Editing { AdjustForMapFormat(); + //mxd. We don't want to keep unknown flags (like flags from different map format) + if(General.Map.Config != null && General.Map.Config.ThingFlags != null) + { + List unknownfields = new List(); + foreach(String s in forbiddenfields) + { + if(!General.Map.Config.ThingFlags.ContainsKey(s)) unknownfields.Add(s); + } + + if(unknownfields.Count > 0) + { + foreach(String s in unknownfields) forbiddenfields.Remove(s); + } + + unknownfields.Clear(); + foreach(String s in requiredfields) + { + if(!General.Map.Config.ThingFlags.ContainsKey(s)) unknownfields.Add(s); + } + + if(unknownfields.Count > 0) + { + foreach(String s in unknownfields) requiredfields.Remove(s); + } + } + //Integrity check if(!IsValid()) General.ErrorLogger.Add(ErrorType.Warning, "Things filter '" + name + "' has invalid properties. Configure the thing filter to fix this!"); @@ -298,8 +294,7 @@ namespace CodeImp.DoomBuilder.Editing /// public bool IsThingVisible(Thing t) { - if (t.IsDisposed) return false; //mxd - return thingsvisiblestate[t]; + return (!t.IsDisposed && thingsvisiblestate[t]); } // This writes the filter to configuration diff --git a/Source/Core/GZBuilder/Data/ModelData.cs b/Source/Core/GZBuilder/Data/ModelData.cs index d99eb9e2..dde94fbb 100644 --- a/Source/Core/GZBuilder/Data/ModelData.cs +++ b/Source/Core/GZBuilder/Data/ModelData.cs @@ -12,6 +12,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data { internal sealed class ModelData { + #region ================== Constants + + public static readonly string[] SUPPORTED_TEXTURE_EXTENSIONS = { ".jpg", ".tga", ".png", ".dds", ".pcx" }; + + #endregion + #region ================== Variables private ModelLoadState loadstate; diff --git a/Source/Core/GZBuilder/Data/TextureData.cs b/Source/Core/GZBuilder/Data/TextureData.cs deleted file mode 100644 index c4a6f505..00000000 --- a/Source/Core/GZBuilder/Data/TextureData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace CodeImp.DoomBuilder.GZBuilder.Data { - public struct TextureData { - public const string INVALID_TEXTURE = "**invalid_texture**"; - public static string[] SUPPORTED_TEXTURE_EXTENSIONS = { ".jpg", ".tga", ".png", ".dds", ".pcx" }; - } -} diff --git a/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs b/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs index 3d2db7d2..91f6522f 100644 --- a/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs +++ b/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs @@ -63,8 +63,6 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom base.Parse(stream, sourcefilename); - // Already parsed this? - if(parsedlumps.Contains(sourcefilename)) return false; parsedlumps.Add(sourcefilename); if(isinclude && !includes.Contains(sourcefilename)) includes.Add(sourcefilename); includestoskip = configincludes; @@ -180,46 +178,68 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom case "#library": if(IsMapScriptsLump) { - ReportError("Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": SCRIPTS lump can not be compiled as library!"); + ReportError("SCRIPTS lump can not be compiled as a library"); return false; } SkipWhitespace(true); - libraryname = ReadToken(); + libraryname = ReadToken(false); // Don't skip newline - if(string.IsNullOrEmpty(libraryname) || !libraryname.StartsWith("\"") || !libraryname.EndsWith("\"")) + if(!libraryname.StartsWith("\"") || !libraryname.EndsWith("\"")) { - ReportError("Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": invalid #library directive!"); + ReportError("#library name should be quoted"); return false; } libraryname = StripTokenQuotes(libraryname); + + if(string.IsNullOrEmpty(libraryname)) + { + ReportError("Expected library name"); + return false; + } + break; default: if(processincludes && (token == "#include" || token == "#import")) { SkipWhitespace(true); - string includelump = StripTokenQuotes(ReadToken()).ToLowerInvariant(); + string includelump = ReadToken(false); // Don't skip newline - if(!string.IsNullOrEmpty(includelump)) + if(!includelump.StartsWith("\"") || !includelump.EndsWith("\"")) { - string includename = Path.GetFileName(includelump); - if(includestoskip.Contains(includename) || includes.Contains(includename)) continue; - - // Callback to parse this file - if(OnInclude != null) OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); - - // Set our buffers back to continue parsing - datastream = localstream; - datareader = localreader; - sourcename = localsourcename; - } - else - { - ReportError("Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": got #include directive without include path!"); + ReportError(token + " filename should be quoted"); return false; } + + includelump = StripTokenQuotes(includelump).ToLowerInvariant(); + + if(string.IsNullOrEmpty(includelump)) + { + ReportError("Expected file name to " + token); + return false; + } + + string includename = Path.GetFileName(includelump); + + // Compiler files? + if(includestoskip.Contains(includename)) continue; + + // Already parsed this? + if(includes.Contains(includename)) + { + ReportError("already parsed '" + includename + "'. Check your #include directives"); + return false; + } + + // Callback to parse this file + if(OnInclude != null) OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); + + // Set our buffers back to continue parsing + datastream = localstream; + datareader = localreader; + sourcename = localsourcename; } break; } @@ -276,5 +296,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return "(void)"; } + + protected override string GetLanguageType() + { + return "ACS"; + } } } \ No newline at end of file diff --git a/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs b/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs index afc80a6b..3ff98a1a 100644 --- a/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs +++ b/Source/Core/GZBuilder/GZDoom/DecorateParserSE.cs @@ -4,6 +4,7 @@ using CodeImp.DoomBuilder.ZDoom; using CodeImp.DoomBuilder.GZBuilder.Data; //mxd. Decorate parser used to create ScriptItems for use in script editor's navigator +//Should be able to parse actor definitions even from invalid DECORATE and should never fail parsing namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { internal sealed class DecorateParserSE : ZDTextParser @@ -21,38 +22,35 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom base.Parse(stream, sourcefilename); // Continue until at the end of the stream - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { string token = ReadToken(); + if(string.IsNullOrEmpty(token) || token.ToUpperInvariant() != "ACTOR") continue; - if (!string.IsNullOrEmpty(token)) + SkipWhitespace(true); + int startpos = (int)stream.Position; + + List definition = new List(); + + do { - token = token.ToLowerInvariant(); + token = ReadToken(false); // Don't skip newline + if(string.IsNullOrEmpty(token) || token == "{" || token == "}") break; + definition.Add(token); + } while(SkipWhitespace(false)); // Don't skip newline - if (token == "actor") - { - SkipWhitespace(true); - int startPos = (int)stream.Position; - - List definition = new List(); - - do - { - token = ReadToken(); - if(string.IsNullOrEmpty(token) || token == "{") break; - definition.Add(token); - } while(SkipWhitespace(true)); - - string name = ""; - foreach (string s in definition) name += s + " "; - actors.Add(new ScriptItem(name.TrimEnd(), startPos, false)); - } - } + string name = string.Join(" ", definition.ToArray()); + if(!string.IsNullOrEmpty(name)) actors.Add(new ScriptItem(name, startpos, false)); } - //sort nodes + // Sort nodes actors.Sort(ScriptItem.SortByName); return true; } + + protected override string GetLanguageType() + { + return "DECORATE"; + } } } diff --git a/Source/Core/GZBuilder/GZDoom/GldefsParser.cs b/Source/Core/GZBuilder/GZDoom/GldefsParser.cs index 075a11bb..d8f10a48 100644 --- a/Source/Core/GZBuilder/GZDoom/GldefsParser.cs +++ b/Source/Core/GZBuilder/GZDoom/GldefsParser.cs @@ -16,7 +16,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { internal sealed class GldefsParser : ZDTextParser { - #region ================== Structs + #region ================== Constants private const int DEFAULT_GLOW_HEIGHT = 64; @@ -82,12 +82,6 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom public override bool Parse(Stream stream, string sourcefilename) { base.Parse(stream, sourcefilename); - - if (parsedlumps.IndexOf(sourcefilename) != -1) - { - General.ErrorLogger.Add(ErrorType.Error, "already parsed '" + sourcefilename + "'. Check your #include directives!"); - return false; - } parsedlumps.Add(sourcefilename); // Keep local data @@ -96,18 +90,17 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom BinaryReader localreader = datareader; // Continue until at the end of the stream - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { string token = ReadToken(); - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { token = StripTokenQuotes(token.ToLowerInvariant()); //Quotes can be anywhere! ANYWHERE!!! And GZDoom will still parse data correctly //got light structure - if (token == GldefsLightType.POINT || token == GldefsLightType.PULSE || token == GldefsLightType.FLICKER + if(token == GldefsLightType.POINT || token == GldefsLightType.PULSE || token == GldefsLightType.FLICKER || token == GldefsLightType.FLICKER2 || token == GldefsLightType.SECTOR) { - bool gotErrors = false; string lightType = token; DynamicLightData light = new DynamicLightData(); @@ -117,41 +110,39 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom SkipWhitespace(true); string lightName = StripTokenQuotes(ReadToken()).ToLowerInvariant(); - if (!string.IsNullOrEmpty(lightName)) + if(!string.IsNullOrEmpty(lightName)) { //now find opening brace if(!NextTokenIs("{")) continue; //read gldefs light structure - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { token = ReadToken(); - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { token = token.ToLowerInvariant(); //color - if (token == "color") + if(token == "color") { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Red)) + if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Red)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Red Color value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Red color value, but got '" + token + "'"); + return false; } SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Green)) + if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Green)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Green Color value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Green color value, but got '" + token + "'"); + return false; } SkipWhitespace(true); @@ -160,128 +151,118 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Blue)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Blue Color value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Blue color value, but got '" + token + "'"); + return false; } //size } - else if (token == "size") + else if(token == "size") { - if (lightType != GldefsLightType.SECTOR) + if(lightType != GldefsLightType.SECTOR) { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out light.PrimaryRadius)) + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out light.PrimaryRadius)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Size value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Size value, but got '" + token + "'"); + return false; } light.PrimaryRadius *= 2; } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": '" + token + "' is not valid property for " + lightType + "."); - gotErrors = true; - break; + ReportError("'" + token + "' is not valid property for " + lightType + "."); + return false; } //offset } - else if (token == "offset") + else if(token == "offset") { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!ReadSignedFloat(token, ref light.Offset.X)) + if(!ReadSignedFloat(token, ref light.Offset.X)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Offset X value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Offset X value, but got '" + token + "'"); + return false; } SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!ReadSignedFloat(token, ref light.Offset.Z)) + if(!ReadSignedFloat(token, ref light.Offset.Z)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Offset Y value, but got '" + token + "'"); - gotErrors = true; - break; + ReportError("expected Offset Y value, but got '" + token + "'"); + return false; } SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!ReadSignedFloat(token, ref light.Offset.Y)) + if(!ReadSignedFloat(token, ref light.Offset.Y)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Offset Z value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Offset Z value, but got '" + token + "'"); + return false; } //subtractive } - else if (token == "subtractive") + else if(token == "subtractive") { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); int i; - if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Subtractive value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Subtractive value, but got '" + token + "'"); + return false; } light.Subtractive = i == 1; //dontlightself } - else if (token == "dontlightself") + else if(token == "dontlightself") { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); int i; - if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Dontlightself value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Dontlightself value, but got '" + token + "'"); + return false; } light.DontLightSelf = (i == 1); //interval } - else if (token == "interval") + else if(token == "interval") { - if (lightType == GldefsLightType.PULSE || lightType == GldefsLightType.FLICKER2) + if(lightType == GldefsLightType.PULSE || lightType == GldefsLightType.FLICKER2) { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); float interval; - if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out interval)) + if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out interval)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Interval value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Interval value, but got '" + token + "'"); + return false; } - if(interval == 0) - General.ErrorLogger.Add(ErrorType.Warning, "Warning in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": Interval value should be greater than zero."); + if(interval == 0) LogWarning("Interval value should be greater than zero"); //I wrote logic for dynamic lights animation first, so here I modify gldefs settings to fit in existing logic - if (lightType == GldefsLightType.PULSE) + if(lightType == GldefsLightType.PULSE) { light.Interval = (int)(interval * 35); //measured in tics (35 per second) in PointLightPulse, measured in seconds in gldefs' PulseLight } @@ -292,50 +273,46 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": '" + token + "' is not valid property for " + lightType + "."); - gotErrors = true; - break; + ReportError("'" + token + "' is not valid property for " + lightType); + return false; } //secondarysize } - else if (token == "secondarysize") + else if(token == "secondarysize") { - if (lightType == GldefsLightType.PULSE || lightType == GldefsLightType.FLICKER || lightType == GldefsLightType.FLICKER2) + if(lightType == GldefsLightType.PULSE || lightType == GldefsLightType.FLICKER || lightType == GldefsLightType.FLICKER2) { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out light.SecondaryRadius)) + if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out light.SecondaryRadius)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected SecondarySize value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected SecondarySize value, but got '" + token + "'"); + return false; } light.SecondaryRadius *= 2; } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": '" + token + "' is not valid property for " + lightType + "."); - gotErrors = true; - break; + ReportError("'" + token + "' is not valid property for " + lightType); + return false; } //chance } - else if (token == "chance") + else if(token == "chance") { - if (lightType == GldefsLightType.FLICKER) + if(lightType == GldefsLightType.FLICKER) { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); float chance; - if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out chance)) + if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out chance)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Chance value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Chance value, but got '" + token + "'"); + return false; } //transforming from 0.0 .. 1.0 to 0 .. 359 to fit in existing logic @@ -343,33 +320,30 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": '" + token + "' is not valid property for " + lightType + "."); - gotErrors = true; - break; + ReportError("'" + token + "' is not valid property for " + lightType); + return false; } //scale } - else if (token == "scale") + else if(token == "scale") { - if (lightType == GldefsLightType.SECTOR) + if(lightType == GldefsLightType.SECTOR) { SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); float scale; - if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out scale)) + if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out scale)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected Scale value, but got '" + token + "'."); - gotErrors = true; - break; + ReportError("expected Scale value, but got '" + token + "'"); + return false; } - if (scale > 1.0f) + if(scale > 1.0f) { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": scale must be in 0.0 - 1.0 range, but is " + scale + "."); - gotErrors = true; - break; + ReportError("scale must be in 0.0 - 1.0 range, but is " + scale); + return false; } //sector light doesn't have animation, so we will store it's size in Interval @@ -378,64 +352,59 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": '" + token + "' is not valid property for " + lightType + "."); - gotErrors = true; - break; + ReportError("'" + token + "' is not valid property for " + lightType); + return false; } } //end of structure - else if (token == "}") + else if(token == "}") { - if (!gotErrors) + bool skip = false; + + //general checks + if(light.Color.Red == 0.0f && light.Color.Green == 0.0f && light.Color.Blue == 0.0f) { - //general checks - if (light.Color.Red == 0.0f && light.Color.Green == 0.0f && light.Color.Blue == 0.0f) - { - General.ErrorLogger.Add(ErrorType.Warning, "'" + sourcefilename + "', line " + GetCurrentLineNumber() + ": light Color is " + light.Color.Red + "," + light.Color.Green + "," + light.Color.Blue + ". It won't be shown in GZDoom!"); - gotErrors = true; - } + LogWarning("'" + lightName + "' light Color is " + light.Color.Red + "," + light.Color.Green + "," + light.Color.Blue + ". It won't be shown in GZDoom"); + skip = true; + } - //light-type specific checks - if (light.Type == DynamicLightType.NORMAL && light.PrimaryRadius == 0) - { - General.ErrorLogger.Add(ErrorType.Warning, "'" + sourcefilename + "', line " + GetCurrentLineNumber() + ": light Size is 0. It won't be shown in GZDoom!"); - gotErrors = true; - } + //light-type specific checks + if(light.Type == DynamicLightType.NORMAL && light.PrimaryRadius == 0) + { + LogWarning("'" + lightName + "' light Size is 0. It won't be shown in GZDoom"); + skip = true; + } - if (light.Type == DynamicLightType.FLICKER || light.Type == DynamicLightType.PULSE || light.Type == DynamicLightType.RANDOM) + if(light.Type == DynamicLightType.FLICKER || light.Type == DynamicLightType.PULSE || light.Type == DynamicLightType.RANDOM) + { + if(light.PrimaryRadius == 0 && light.SecondaryRadius == 0) { - if (light.PrimaryRadius == 0 && light.SecondaryRadius == 0) - { - General.ErrorLogger.Add(ErrorType.Warning, "'" + sourcefilename + "', line " + GetCurrentLineNumber() + ": 'Size' and 'SecondarySize' are 0. This light won't be shown in GZDoom!"); - gotErrors = true; - } - } - - //offset it slightly to avoid shading glitches - if (light.Offset.Z == 0.0f) light.Offset.Z = 0.1f; - - if (!gotErrors) - { - if (lightsByName.ContainsKey(lightName)) - lightsByName[lightName] = light; - else - lightsByName.Add(lightName, light); + LogWarning("'" + lightName + "' light Size and SecondarySize are 0. This light won't be shown in GZDoom"); + skip = true; } } - break; //break out of this parsing loop + + //offset it slightly to avoid shading glitches + if(light.Offset.Z == 0.0f) light.Offset.Z = 0.1f; + + // Add to the collection? + if(!skip) lightsByName[lightName] = light; + + //break out of this parsing loop + break; } } } } } - else if (token == "object") + else if(token == "object") { SkipWhitespace(true); //read object class string objectClass = StripTokenQuotes(ReadToken()).ToLowerInvariant(); - if (!string.IsNullOrEmpty(objectClass)) + if(!string.IsNullOrEmpty(objectClass)) { //now find opening brace if(!NextTokenIs("{")) continue; @@ -445,10 +414,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom bool foundFrame = false; //read frames structure - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { token = ReadToken(); - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { token = StripTokenQuotes(token).ToLowerInvariant(); if(!foundLight && !foundFrame && token == "frame") @@ -464,29 +433,26 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom SkipWhitespace(true); token = ReadToken().ToLowerInvariant(); //should be light name - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { - if (lightsByName.ContainsKey(token)) + if(lightsByName.ContainsKey(token)) { - if (objects.ContainsKey(objectClass)) - objects[objectClass] = token; - else - objects.Add(objectClass, token); + objects[objectClass] = token; foundLight = true; } else { - General.ErrorLogger.Add(ErrorType.Warning, "Light declaration not found for light '" + token + "' ('" + sourcefilename + "', line " + GetCurrentLineNumber()+")."); + LogWarning("Light declaration not found for light '" + token + "'"); } } } - else if (token == "{") //continue in this loop until object structure ends + else if(token == "{") //continue in this loop until object structure ends { bracesCount++; } - else if (token == "}") + else if(token == "}") { - if (--bracesCount < 1) break; //This was Cave Johnson. And we are done here. + if(--bracesCount < 1) break; //This was Cave Johnson. And we are done here. } } } @@ -549,12 +515,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom int color; int glowheight = DEFAULT_GLOW_HEIGHT; bool subtractivetexture = (token == "subtexture"); - string texturename = StripTokenQuotes(ReadToken()); + string texturename = StripTokenQuotes(ReadToken(false)); if(string.IsNullOrEmpty(texturename)) { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected a texture name."); - break; + ReportError("expected " + token + " name"); + return false; } // Now we should find a comma @@ -574,8 +540,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected glow color value, but got '" + token + "'."); - break; + ReportError("expected glow color value, but got '" + token + "'"); + return false; } } @@ -632,8 +598,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!fullblack && !fullbright) { string expectedflags = (subtractivetexture ? "'fullbright'" : "'fullbright' or 'fullblack'"); - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected " + expectedflags + " flag, but got '" + token + "'."); - break; + ReportError("expected " + expectedflags + " flag, but got '" + token + "'"); + return false; } // Add glow data @@ -655,25 +621,43 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(string.IsNullOrEmpty(token) || token == "}") break; } } - else if (token == "#include") + else if(token == "#include") { SkipWhitespace(true); - string includeLump = StripTokenQuotes(ReadToken()).ToLowerInvariant(); - if (!string.IsNullOrEmpty(includeLump)) - { - // Callback to parse this file - if (OnInclude != null) - OnInclude(this, includeLump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); + string includelump = ReadToken(false); // Don't skip newline - // Set our buffers back to continue parsing - datastream = localstream; - datareader = localreader; - sourcename = localsourcename; - } - else + // Sanity checks + if(!includelump.StartsWith("\"") || !includelump.EndsWith("\"")) { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": got #include directive with missing or incorrect path: '" + includeLump + "'."); + ReportError("#include filename should be quoted"); + return false; } + + includelump = StripTokenQuotes(includelump).ToLowerInvariant(); + + // More sanity checks + if(string.IsNullOrEmpty(includelump)) + { + ReportError("Expected file name to include"); + return false; + } + + includelump = includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + // Already parsed? + if(parsedlumps.IndexOf(includelump) != -1) + { + ReportError("already parsed '" + includelump + "'. Check your #include directives"); + return false; + } + + // Callback to parse this file + if(OnInclude != null) OnInclude(this, includelump); + + // Set our buffers back to continue parsing + datastream = localstream; + datareader = localreader; + sourcename = localsourcename; } else if(token == "$gzdb_skip") //mxd { @@ -685,21 +669,21 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom string token2; do { - if (!SkipWhitespace(true)) break; + if(!SkipWhitespace(true)) break; token2 = ReadToken(); - if (string.IsNullOrEmpty(token2)) break; + if(string.IsNullOrEmpty(token2)) break; } while (token2 != "{"); int scopelevel = 1; do { - if (!SkipWhitespace(true)) break; + if(!SkipWhitespace(true)) break; token2 = ReadToken(); if(string.IsNullOrEmpty(token2)) break; - if (token2 == "{") scopelevel++; - if (token2 == "}") scopelevel--; + if(token2 == "{") scopelevel++; + if(token2 == "}") scopelevel--; } - while (scopelevel > 0); + while(scopelevel > 0); } } } @@ -716,6 +700,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom parsedlumps.Clear(); } + protected override string GetLanguageType() + { + return "GLDEFS"; + } + #endregion } } \ No newline at end of file diff --git a/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs b/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs index 93c8f195..e361b4b1 100644 --- a/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs +++ b/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs @@ -28,6 +28,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom private string mapname; private readonly Dictionary spawnnums; private readonly Dictionary doomednums; // > + private readonly HashSet parsedlumps; #endregion @@ -50,6 +51,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom mapinfo = new MapInfo(); spawnnums = new Dictionary(); doomednums = new Dictionary(); + parsedlumps = new HashSet(); } #endregion @@ -67,6 +69,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { base.Parse(stream, sourcefilename); this.mapname = mapname.ToLowerInvariant(); + parsedlumps.Add(sourcefilename); while(SkipWhitespace(true)) { @@ -118,30 +121,30 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom int bracelevel = 0; //search for required keys - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { token = ReadToken().ToLowerInvariant(); //sky1 or sky2 - if (token == "sky1" || token == "sky2") + if(token == "sky1" || token == "sky2") { string skyType = token; SkipWhitespace(true); token = StripTokenQuotes(ReadToken()).ToLowerInvariant(); //new format - if (token == "=") + if(token == "=") { SkipWhitespace(true); //should be sky texture name token = StripTokenQuotes(ReadToken()); bool gotComma = (token.IndexOf(",") != -1); - if (gotComma) token = token.Replace(",", ""); + if(gotComma) token = token.Replace(",", ""); string skyTexture = StripTokenQuotes(token).ToLowerInvariant(); - if (!string.IsNullOrEmpty(skyTexture)) + if(!string.IsNullOrEmpty(skyTexture)) { - if (skyType == "sky1") + if(skyType == "sky1") mapinfo.Sky1 = skyTexture; else mapinfo.Sky2 = skyTexture; @@ -150,25 +153,24 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom SkipWhitespace(true); token = StripTokenQuotes(ReadToken()); - if (!gotComma && token == ",") + if(!gotComma && token == ",") { gotComma = true; SkipWhitespace(true); token = ReadToken(); } - if (gotComma) + if(gotComma) { float scrollSpeed = 0; - if (!ReadSignedFloat(token, ref scrollSpeed)) + if(!ReadSignedFloat(token, ref scrollSpeed)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Warning, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected " + skyType + " scroll speed value, but got '" + token + "'"); - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again - continue; + ReportError("expected " + skyType + " scroll speed value, but got '" + token + "'"); + return false; } - if (skyType == "sky1") + if(skyType == "sky1") mapinfo.Sky1ScrollSpeed = scrollSpeed; else mapinfo.Sky2ScrollSpeed = scrollSpeed; @@ -180,17 +182,17 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected " + skyType + " texture name."); + ReportError("expected " + skyType + " texture name"); + return false; } } //old format else { //token should be sky1/2 name - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { - if (skyType == "sky1") + if(skyType == "sky1") mapinfo.Sky1 = token; else mapinfo.Sky2 = token; @@ -200,14 +202,14 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = StripTokenQuotes(ReadToken()); float scrollSpeed = 0; - if (!ReadSignedFloat(token, ref scrollSpeed)) + if(!ReadSignedFloat(token, ref scrollSpeed)) { // Not numeric! datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again continue; } - if (skyType == "sky1") + if(skyType == "sky1") mapinfo.Sky1ScrollSpeed = scrollSpeed; else mapinfo.Sky2ScrollSpeed = scrollSpeed; @@ -215,20 +217,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected " + skyType + " texture name."); + ReportError("expected " + skyType + " texture name"); + return false; } } } //fade or outsidefog - else if (token == "fade" || token == "outsidefog") + else if(token == "fade" || token == "outsidefog") { string fadeType = token; SkipWhitespace(true); token = StripTokenQuotes(ReadToken()).ToLowerInvariant(); //new format? - if (token == "=") + if(token == "=") { SkipWhitespace(true); token = ReadToken(); @@ -249,18 +251,18 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else //...or not { - General.ErrorLogger.Add(ErrorType.Error, "Failed to parse " + fadeType + " value from string '" + colorVal + "' in '" + sourcename + "' at line " + GetCurrentLineNumber()); - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again + ReportError("failed to parse " + fadeType + " value from string '" + colorVal + "'"); + return false; } } else { - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected " + fadeType + " color value."); - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again + ReportError("expected " + fadeType + " color value"); + return false; } } //vertwallshade or horizwallshade - else if (token == "vertwallshade" || token == "horizwallshade") + else if(token == "vertwallshade" || token == "horizwallshade") { string shadeType = token; SkipWhitespace(true); @@ -277,9 +279,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!ReadSignedInt(token, ref val)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected " + shadeType + " value, but got '" + token + "'"); - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again - continue; + ReportError("expected " + shadeType + " value, but got '" + token + "'"); + return false; } if(shadeType == "vertwallshade") @@ -305,23 +306,22 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected " + densityType + " value, but got '" + token + "'"); - datastream.Seek(-token.Length - 1, SeekOrigin.Current); //step back and try parsing this token again - continue; + ReportError("expected " + densityType + " value, but got '" + token + "'"); + return false; } - if (densityType == "fogdensity") + if(densityType == "fogdensity") mapinfo.FogDensity = (int)(1024 * (256.0f / val)); else mapinfo.OutsideFogDensity = (int)(1024 * (256.0f / val)); } //doublesky - else if (token == "doublesky") + else if(token == "doublesky") { mapinfo.DoubleSky = true; } //evenlighting - else if (token == "evenlighting") + else if(token == "evenlighting") { mapinfo.EvenLighting = true; } @@ -331,7 +331,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom mapinfo.SmoothLighting = true; } //block end - else if (token == "}") + else if(token == "}") { return ParseBlock(token); } @@ -356,12 +356,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom else if(token == "include") { SkipWhitespace(true); - string includeLump = StripTokenQuotes(ReadToken()).ToLowerInvariant(); - if(!string.IsNullOrEmpty(includeLump)) + string includelump = StripTokenQuotes(ReadToken(false)).ToLowerInvariant(); // Don't skip newline + + if(!string.IsNullOrEmpty(includelump)) { // Callback to parse this file if(OnInclude != null) - OnInclude(this, includeLump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); + OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); // Set our buffers back to continue parsing datastream = localstream; @@ -370,32 +371,33 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": got #include directive with missing or incorrect path: '" + includeLump + "'."); + ReportError("got include directive with missing or incorrect path: '" + includelump + "'"); + return false; } } else if(token == "gameinfo") { - if(!NextTokenIs("{")) return true; // Finished with this file + if(!NextTokenIs("{")) return false; // Finished with this file while(SkipWhitespace(true)) { token = ReadToken(); if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber() + ": failed to find the end of GameInfo block"); - return true; // Finished with this file + ReportError("failed to find the end of GameInfo block"); + return false; // Finished with this file } if(token == "}") break; if(token == "skyflatname") { - if(!NextTokenIs("=")) return true; // Finished with this file + if(!NextTokenIs("=")) return false; // Finished with this file SkipWhitespace(true); string skyflatname = StripTokenQuotes(ReadToken()); if(string.IsNullOrEmpty(skyflatname)) { - General.ErrorLogger.Add(ErrorType.Error, "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber() + ": unable to get SkyFlatName value"); - return true; // Finished with this file + ReportError("unable to get SkyFlatName value"); + return false; // Finished with this file } General.Map.Config.SkyFlatName = skyflatname.ToUpperInvariant(); @@ -404,15 +406,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else if(token == "doomednums") { - if(!NextTokenIs("{")) return true; // Finished with this file + if(!NextTokenIs("{")) return false; // Finished with this file while(SkipWhitespace(true)) { token = ReadToken(); if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber() + ": failed to find the end of DoomEdNums block"); - return true; // Finished with this file + ReportError("failed to find the end of DoomEdNums block"); + return false; // Finished with this file } if(token == "}") break; @@ -421,24 +423,24 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out id)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected DoomEdNums entry number, but got '" + token + "'"); - return true; // Finished with this file + ReportError("expected DoomEdNums entry number, but got '" + token + "'"); + return false; // Finished with this file } // Then "=" - if(!NextTokenIs("=")) return true; // Finished with this file + if(!NextTokenIs("=")) return false; // Finished with this file // Then actor class SkipWhitespace(false); string classname = StripTokenQuotes(ReadToken()); if(string.IsNullOrEmpty(classname)) { - General.ErrorLogger.Add(ErrorType.Error, "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber() + ": unable to get DoomEdNums entry class definition"); - return true; // Finished with this file + ReportError("unable to get DoomEdNums entry class definition"); + return false; // Finished with this file } // Possible special and args. We'll skip them - for (int i = 0; i < 6; i++) + for(int i = 0; i < 6; i++) { if(!NextTokenIs(",", false)) break; @@ -452,15 +454,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else if(token == "spawnnums") { - if(!NextTokenIs("{")) return true; // Finished with this file + if(!NextTokenIs("{")) return false; // Finished with this file - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { token = ReadToken(); if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber() + ": failed to find the end of SpawnNums block"); - return true; // Finished with this file + ReportError("failed to find the end of SpawnNums block"); + return false; // Finished with this file } if(token == "}") break; @@ -469,20 +471,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out id)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected SpawnNums number, but got '" + token + "'"); - return true; // Finished with this file + ReportError("expected SpawnNums number, but got '" + token + "'"); + return false; // Finished with this file } // Then "=" - if(!NextTokenIs("=")) return true; // Finished with this file + if(!NextTokenIs("=")) return false; // Finished with this file // Then actor class SkipWhitespace(false); token = StripTokenQuotes(ReadToken()); if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Error while parisng '" + sourcename + "' at line " + GetCurrentLineNumber() + ": unable to get SpawnNums entry class definition"); - return true; + ReportError("unable to get SpawnNums entry class definition"); + return false; } // Add to collection @@ -503,11 +505,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom private static bool GetColor(string name, ref Color4 color) { - if (name == "black") return true; + if(name == "black") return true; //probably it's a hex color (like FFCC11)? int ci; - if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ci)) + if(int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ci)) { color = new Color4(ci) {Alpha = 1.0f}; return true; @@ -515,7 +517,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom //probably it's a color name? Color c = Color.FromName(name); //should be similar to C++ color name detection, I suppose - if (c.IsKnownColor) + if(c.IsKnownColor) { color = new Color4(c); return true; @@ -523,6 +525,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return false; } + protected override string GetLanguageType() + { + return "MAPINFO"; + } + #endregion } } diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs index 65e619d3..954d16bf 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs @@ -10,7 +10,6 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { private Dictionary entries; //classname, entry internal Dictionary Entries { get { return entries; } } - internal string Source { get { return sourcename; } } internal ModeldefParser() { @@ -24,66 +23,88 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom entries = new Dictionary(StringComparer.Ordinal); // Continue until at the end of the stream - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { string token = ReadToken(); - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { token = StripTokenQuotes(token).ToLowerInvariant(); - if (token == "model") //model structure start + if(token == "model") //model structure start { //find classname SkipWhitespace(true); - string className = StripTokenQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)).ToLowerInvariant(); + string displayclassname = StripTokenQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)); + string classname = displayclassname.ToLowerInvariant(); - if(!string.IsNullOrEmpty(className) && !entries.ContainsKey(className)) + if(!string.IsNullOrEmpty(classname) && !entries.ContainsKey(classname)) { //now find opening brace SkipWhitespace(true); token = ReadToken(); - if (token != "{") + if(token != "{") { - General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '"+sourcefilename+"' at line "+GetCurrentLineNumber()+": expected '{', but got '" + token + "'"); + General.ErrorLogger.Add(ErrorType.Error, "Unexpected token found in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": expected '{', but got '" + token + "'"); continue; //something wrong with modeldef declaration, continue to next one } ModeldefStructure mds = new ModeldefStructure(); - ModelData mde = mds.Parse(this); - if (mde != null) entries.Add(className, mde); + if(mds.Parse(this, displayclassname) && mds.ModelData != null) + { + entries.Add(classname, mds.ModelData); + } + + if(HasError) + { + LogError(); + ClearError(); + } + + // Skip untill current structure end + if(!mds.ParsingFinished) + { + while(SkipWhitespace(true)) + { + token = ReadToken(); + if(string.IsNullOrEmpty(token) || token == "}") break; + } + } } } else { // Unknown structure! string token2; - if (token != "{") + if(token != "{") { do { - if (!SkipWhitespace(true)) break; + if(!SkipWhitespace(true)) break; token2 = ReadToken(); - if (string.IsNullOrEmpty(token2)) break; + if(string.IsNullOrEmpty(token2)) break; } - while (token2 != "{"); + while(token2 != "{"); } int scopelevel = 1; do { - if (!SkipWhitespace(true)) break; + if(!SkipWhitespace(true)) break; token2 = ReadToken(); - if (string.IsNullOrEmpty(token2)) break; - if (token2 == "{") scopelevel++; - if (token2 == "}") scopelevel--; + if(string.IsNullOrEmpty(token2)) break; + if(token2 == "{") scopelevel++; + if(token2 == "}") scopelevel--; } - while (scopelevel > 0); + while(scopelevel > 0); } - } - } return entries.Count > 0; } + + protected override string GetLanguageType() + { + return "MODELDEF"; + } } } diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs index e591e453..13d3995f 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefParserSE.cs @@ -1,13 +1,14 @@ #region ================== Namespaces -using System.IO; using System.Collections.Generic; -using CodeImp.DoomBuilder.ZDoom; +using System.IO; using CodeImp.DoomBuilder.GZBuilder.Data; +using CodeImp.DoomBuilder.ZDoom; #endregion //mxd. Modeldef parser used to create ScriptItems for use in script editor's navigator +//Should be parse model definitions even from invalid MODELDEF and should never fail parsing namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { internal sealed class ModeldefParserSE : ZDTextParser @@ -25,34 +26,39 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom base.Parse(stream, sourcefilename); // Continue until at the end of the stream - while (SkipWhitespace(true)) + while(SkipWhitespace(true)) { string token = ReadToken(); + if(string.IsNullOrEmpty(token) || token.ToUpperInvariant() != "MODEL") continue; - if (!string.IsNullOrEmpty(token)) + SkipWhitespace(true); + int startpos = (int)stream.Position; + string modelname = ReadToken(); + + SkipWhitespace(true); + token = ReadToken(); //this should be "{" + + if(token == "{") { - token = token.ToUpperInvariant(); + ScriptItem i = new ScriptItem(modelname, startpos, false); + models.Add(i); + } - if(token == "MODEL") - { - SkipWhitespace(true); - int startPos = (int)stream.Position; - string modelName = ReadToken(); - SkipWhitespace(true); - token = ReadToken(); //this should be "{" - - if (token == "{") - { - ScriptItem i = new ScriptItem(modelName, startPos, false); - models.Add(i); - } - } + while(SkipWhitespace(true)) + { + token = ReadToken(); + if(string.IsNullOrEmpty(token) || token == "}") break; } } - //sort nodes + // Sort nodes models.Sort(ScriptItem.SortByName); return true; } + + protected override string GetLanguageType() + { + return "MODELDEF"; + } } } diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs index 225b764a..a62e4ef4 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs @@ -14,8 +14,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom internal sealed class ModeldefStructure { private const int MAX_MODELS = 4; //maximum models per modeldef entry, zero-based + private bool parsingfinished; - internal ModelData Parse(ModeldefParser parser) + internal ModelData ModelData; + internal bool ParsingFinished { get { return parsingfinished; } } + + internal bool Parse(ModeldefParser parser, string classname) { #region ================== Vars @@ -35,13 +39,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom bool inheritactorroll = false; string token; - bool gotErrors = false; - bool allParsed = false; #endregion //read modeldef structure contents - while(!gotErrors && !allParsed && parser.SkipWhitespace(true)) + parsingfinished = false; + while(!parsingfinished && parser.SkipWhitespace(true)) { token = parser.ReadToken(); if(!string.IsNullOrEmpty(token)) @@ -54,11 +57,11 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom case "path": parser.SkipWhitespace(true); - path = parser.StripTokenQuotes(parser.ReadToken()).Replace("/", "\\"); - if(string.IsNullOrEmpty(path)) + path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline + if(string.IsNullOrEmpty(path)) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected path to model, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected model path, but got '" + token + "'"); + return false; } break; @@ -75,47 +78,42 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out index)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected model index, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected model index, but got '" + token + "'"); + return false; } if(index >= MAX_MODELS) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry!"); - gotErrors = true; - break; + parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry"); + return false; } parser.SkipWhitespace(true); //model path - token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant(); + token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected model name, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("model name required"); + return false; } - else - { - //check extension - string fileExt = Path.GetExtension(token); - if(string.IsNullOrEmpty(fileExt)) - { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": model '" + token + "' won't be loaded. Models without extension are not supported by GZDoom."); - gotErrors = true; - break; - } - if(fileExt != ".md3" && fileExt != ".md2") - { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": model '" + token + "' won't be loaded. Only MD2 and MD3 models are supported."); - gotErrors = true; - break; - } - //GZDoom allows models with identical modelIndex, it uses the last one encountered - modelNames[index] = Path.Combine(path, token); + //check extension + string fileExt = Path.GetExtension(token); + if(string.IsNullOrEmpty(fileExt)) + { + parser.ReportError("model '" + token + "' won't be loaded. Models without extension are not supported by GZDoom"); + return false; } + + if(fileExt != ".md3" && fileExt != ".md2") + { + parser.ReportError("model '" + token + "' won't be loaded. Only MD2 and MD3 models are supported"); + return false; + } + + //GZDoom allows models with identical modelIndex, it uses the last one encountered + modelNames[index] = Path.Combine(path, token); break; #endregion @@ -131,42 +129,36 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinIndex)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected skin index, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected skin index, but got '" + token + "'"); + return false; } if(skinIndex >= MAX_MODELS) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry!"); - gotErrors = true; - break; + parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " skins per MODELDEF entry"); + return false; } parser.SkipWhitespace(true); //skin path - token = parser.StripTokenQuotes(parser.ReadToken()).ToLowerInvariant(); + token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected skin name, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("skin path required"); + return false; } - else + + //check extension + string ext = Path.GetExtension(token); + if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) { - //check extension - string ext = Path.GetExtension(token); - if(Array.IndexOf(TextureData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) - { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": image format '" + ext + "' is not supported!"); - textureNames[skinIndex] = TextureData.INVALID_TEXTURE; - } - else - { - //GZDoom allows skins with identical modelIndex, it uses the last one encountered - textureNames[skinIndex] = Path.Combine(path, token); - } - } + parser.ReportError("image format '" + ext + "' is not supported!"); + return false; + } + + //GZDoom allows skins with identical modelIndex, it uses the last one encountered + textureNames[skinIndex] = Path.Combine(path, token); break; #endregion @@ -179,9 +171,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref scale.Y)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected Scale X value, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected Scale X value, but got '" + token + "'"); + return false; } parser.SkipWhitespace(true); @@ -189,9 +180,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref scale.X)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected Scale Y value, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected Scale Y value, but got '" + token + "'"); + return false; } parser.SkipWhitespace(true); @@ -199,8 +189,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref scale.Z)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected Scale Z value, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected Scale Z value, but got '" + token + "'"); + return false; } break; @@ -214,9 +204,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.X)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected Offset X value, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected Offset X value, but got '" + token + "'"); + return false; } parser.SkipWhitespace(true); @@ -224,9 +213,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.Y)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected Offset Y value, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected Offset Y value, but got '" + token + "'"); + return false; } parser.SkipWhitespace(true); @@ -234,8 +222,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.Z)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected Offset Z value, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected Offset Z value, but got '" + token + "'"); + return false; } break; @@ -249,8 +237,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.Z)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected ZOffset value, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected ZOffset value, but got '" + token + "'"); + return false; } break; @@ -264,8 +252,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref angleOffset)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected AngleOffset value, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected AngleOffset value, but got '" + token + "'"); + return false; } break; @@ -279,8 +267,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref pitchOffset)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected PitchOffset value, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected PitchOffset value, but got '" + token + "'"); + return false; } break; @@ -294,8 +282,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref rollOffset)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected RollOffset value, but got '" + token + "'"); - gotErrors = true; + parser.ReportError("expected RollOffset value, but got '" + token + "'"); + return false; } break; @@ -322,8 +310,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom case "frameindex": case "frame": //parsed all required fields. if got more than one model - find which one(s) should be displayed - int len = modelNames.GetLength(0); - if(!gotErrors && len > 1) + if(modelNames.GetLength(0) > 1) { string spriteLump = null; string spriteFrame = null; @@ -389,23 +376,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelIndex)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected model index, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected model index, but got '" + token + "'"); + return false; } if(modelIndex >= MAX_MODELS) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry!"); - gotErrors = true; - break; + parser.ReportError("GZDoom doesn't allow more than " + MAX_MODELS + " models per MODELDEF entry"); + return false; } if(modelNames[modelIndex] == null) { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": got model index, which doesn't correspond to any defined model!"); - gotErrors = true; - break; + parser.ReportError("got model index, which doesn't correspond to any defined model"); + return false; } modelsUsed[modelIndex] = true; @@ -420,9 +404,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedInt(token, ref frame)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected model frame index, but got '" + token + "'"); - gotErrors = true; - break; + parser.ReportError("expected model frame index, but got '" + token + "'"); + return false; } // Skip the model if frame index is -1 @@ -434,9 +417,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(string.IsNullOrEmpty(token)) { // Missing! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + parser.Source + " at line " + parser.GetCurrentLineNumber() + ": expected model frame name"); - gotErrors = true; - break; + parser.ReportError("expected model frame name"); + return false; } frameNames[modelIndex] = token.ToLowerInvariant(); @@ -451,7 +433,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } } - allParsed = true; + parsingfinished = true; break; #endregion @@ -467,28 +449,38 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } // Bail out when got errors or no models are used - if(gotErrors || Array.IndexOf(modelsUsed, true) == -1) return null; + if(Array.IndexOf(modelsUsed, true) == -1) + { + parser.ReportError("no models are used by '" + classname + "'"); + return false; + } // Classname is set in ModeldefParser - ModelData mde = new ModelData(); - mde.InheritActorPitch = inheritactorpitch; - mde.InheritActorRoll = inheritactorroll; + ModelData = new ModelData(); + ModelData.InheritActorPitch = inheritactorpitch; + ModelData.InheritActorRoll = inheritactorroll; Matrix moffset = Matrix.Translation(offset.Y, -offset.X, offset.Z); // Things are complicated in GZDoom... Matrix mrotation = Matrix.RotationY(-Angle2D.DegToRad(rollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(pitchOffset)) * Matrix.RotationZ(Angle2D.DegToRad(angleOffset)); - mde.SetTransform(mrotation, moffset, scale); + ModelData.SetTransform(mrotation, moffset, scale); for(int i = 0; i < modelNames.Length; i++) { if(!string.IsNullOrEmpty(modelNames[i]) && modelsUsed[i]) { - mde.TextureNames.Add(string.IsNullOrEmpty(textureNames[i]) ? string.Empty : textureNames[i].ToLowerInvariant()); - mde.ModelNames.Add(modelNames[i].ToLowerInvariant()); - mde.FrameNames.Add(frameNames[i]); - mde.FrameIndices.Add(frameIndices[i]); + ModelData.TextureNames.Add(string.IsNullOrEmpty(textureNames[i]) ? string.Empty : textureNames[i].ToLowerInvariant()); + ModelData.ModelNames.Add(modelNames[i].ToLowerInvariant()); + ModelData.FrameNames.Add(frameNames[i]); + ModelData.FrameIndices.Add(frameIndices[i]); } } - return (mde.ModelNames.Count > 0 ? mde : null); + if(ModelData.ModelNames.Count == 0) + { + parser.ReportError("'" + classname + "' has no models"); + return false; + } + + return true; } } } diff --git a/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs b/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs index 64e36725..e018b903 100644 --- a/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs +++ b/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs @@ -28,18 +28,18 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { string token = ReadToken(); - if (!string.IsNullOrEmpty(token)) + if(!string.IsNullOrEmpty(token)) { token = token.ToUpperInvariant(); - if (token == "MODEL") + if(token == "MODEL") { SkipWhitespace(true); ReadToken(); //should be model name SkipWhitespace(true); token = ReadToken();//should be opening brace - if (token == "{") + if(token == "{") { scriptType = ScriptType.MODELDEF; return true; @@ -55,7 +55,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom SkipWhitespace(true); token = ReadToken(); //should be opening brace - if (token == "{") + if(token == "{") { scriptType = ScriptType.ACS; return true; @@ -70,7 +70,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom SkipWhitespace(true); token = ReadToken(); - if (token == ":" || token == "{" || token == "REPLACES") + if(token == ":" || token == "{" || token == "REPLACES") { scriptType = ScriptType.DECORATE; return true; @@ -79,7 +79,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom SkipWhitespace(true); token = ReadToken(); //should be actor name - if (token == "{") + if(token == "{") { scriptType = ScriptType.DECORATE; return true; @@ -90,5 +90,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return false; } + + protected override string GetLanguageType() + { + return "SCRIPT TYPE CHECKER"; + } } } diff --git a/Source/Core/GZBuilder/md3/ModelReader.cs b/Source/Core/GZBuilder/md3/ModelReader.cs index 18b1396d..62f174d5 100644 --- a/Source/Core/GZBuilder/md3/ModelReader.cs +++ b/Source/Core/GZBuilder/md3/ModelReader.cs @@ -148,15 +148,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3 //load texture List errors = new List(); - //texture has unsupported extension? - if(mde.TextureNames[i] == TextureData.INVALID_TEXTURE) - { - for(int c = 0; c < result.Meshes.Count; c++) - mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture); - - //texture not defined in MODELDEF? - } - else if(useSkins) + // Texture not defined in MODELDEF? + if(useSkins) { //try to use model's own skins for(int m = 0; m < result.Meshes.Count; m++) @@ -171,7 +164,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3 string path = result.Skins[m].Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); ext = Path.GetExtension(path); - if(Array.IndexOf(TextureData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) + if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) { mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture); errors.Add("image format '" + ext + "' is not supported!"); @@ -193,8 +186,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3 mde.Model.Textures.Add(t); } - } - else //try to use texture loaded from MODELDEFS + } + //Try to use texture loaded from MODELDEFS + else { Texture t = LoadTexture(containers, mde.TextureNames[i], device); if(t == null) diff --git a/Source/Core/General/ErrorLogger.cs b/Source/Core/General/ErrorLogger.cs index a4e0c80e..9ce3bc07 100644 --- a/Source/Core/General/ErrorLogger.cs +++ b/Source/Core/General/ErrorLogger.cs @@ -81,29 +81,38 @@ namespace CodeImp.DoomBuilder lock(this) { - errors.Add(new ErrorItem(type, message)); - switch(type) + //mxd. Don't add duplicate messages + if(errors.Count == 0 || message != errors[errors.Count - 1].message || type != errors[errors.Count - 1].type) { - case ErrorType.Error: - erroradded = true; - prefix = "ERROR: "; + errors.Add(new ErrorItem(type, message)); + switch(type) + { + case ErrorType.Error: + erroradded = true; + prefix = "ERROR: "; #if DEBUG - DebugConsole.WriteLine(DebugMessageType.ERROR, message); + DebugConsole.WriteLine(DebugMessageType.ERROR, message); #endif - break; - - case ErrorType.Warning: - warningadded = true; - prefix = "WARNING: "; -#if DEBUG - DebugConsole.WriteLine(DebugMessageType.WARNING, message); -#endif - break; - } - changed = true; + break; - General.WriteLogLine(prefix + message); - General.MainWindow.SetWarningsCount(errors.Count, erroradded); //mxd + case ErrorType.Warning: + warningadded = true; + prefix = "WARNING: "; +#if DEBUG + DebugConsole.WriteLine(DebugMessageType.WARNING, message); +#endif + break; + } + + changed = true; + General.WriteLogLine(prefix + message); + General.MainWindow.SetWarningsCount(errors.Count, erroradded); //mxd + } + //mxd. But still blink the indicator on errors + else if(type == ErrorType.Error) + { + General.MainWindow.SetWarningsCount(errors.Count, true); + } } } diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs index 41e88a53..058e98f1 100644 --- a/Source/Core/General/MapManager.cs +++ b/Source/Core/General/MapManager.cs @@ -1879,10 +1879,12 @@ namespace CodeImp.DoomBuilder List compilererrors = UpdateScriptNames(); if(logerrors && compilererrors.Count > 0) { + //INFO: CompileLump() prepends lumpname with "?" to distinguish between temporary files and files compiled in place + //INFO: also, error.linenumber is zero-based foreach(CompilerError error in compilererrors) { - General.ErrorLogger.Add(ErrorType.Error, "ACS error in '" + error.filename - + (error.linenumber != CompilerError.NO_LINE_NUMBER ? "', line " + error.linenumber : "'") + General.ErrorLogger.Add(ErrorType.Error, "ACS error in '" + (error.filename.StartsWith("?") ? error.filename.Replace("?", "") : error.filename) + + (error.linenumber != CompilerError.NO_LINE_NUMBER ? "', line " + (error.linenumber + 1) : "'") + ". " + error.description + "."); } } @@ -1927,15 +1929,18 @@ namespace CodeImp.DoomBuilder { // Get script names AcsParserSE parser = new AcsParserSE { OnInclude = (se, path) => se.Parse(General.Map.Data.LoadFile(path), path, true, true) }; - if(parser.Parse(stream, "SCRIPTS", scriptconfig.Compiler.Files, 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)) { // Add them to arrays namedscriptslist.AddRange(parser.NamedScripts); numberedscriptslist.AddRange(parser.NumberedScripts); scripincludeslist.AddRange(parser.Includes); } + // Check for errors - else if(parser.HasError) + if(parser.HasError) { compilererrors.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine)); break; diff --git a/Source/Core/ZDoom/ActorStructure.cs b/Source/Core/ZDoom/ActorStructure.cs index 05d8cca1..c0f2ed3b 100644 --- a/Source/Core/ZDoom/ActorStructure.cs +++ b/Source/Core/ZDoom/ActorStructure.cs @@ -109,7 +109,7 @@ namespace CodeImp.DoomBuilder.ZDoom { token = token.ToLowerInvariant(); - switch (token) + switch(token) { case ":": // The next token must be the class to inherit from @@ -123,9 +123,6 @@ namespace CodeImp.DoomBuilder.ZDoom // Find the actor to inherit from baseclass = parser.GetArchivedActorByName(inheritclass); - - //mxd. Does it exist? (We can carry on regardless, so add a warning) - if(baseclass == null) parser.ReportWarning("Parent class '" + inheritclass + "' does not exist"); break; case "replaces": @@ -171,7 +168,7 @@ namespace CodeImp.DoomBuilder.ZDoom break; } - if (done) break; //mxd + if(done) break; //mxd } else { @@ -188,7 +185,7 @@ namespace CodeImp.DoomBuilder.ZDoom string token = parser.ReadToken(); token = token.ToLowerInvariant(); - switch (token) + switch(token) { case "+": case "-": @@ -196,7 +193,7 @@ namespace CodeImp.DoomBuilder.ZDoom bool flagvalue = (token == "+"); parser.SkipWhitespace(true); string flagname = parser.ReadToken(); - if (!string.IsNullOrEmpty(flagname)) + if(!string.IsNullOrEmpty(flagname)) { // Add the flag with its value flagname = flagname.ToLowerInvariant(); @@ -212,10 +209,10 @@ namespace CodeImp.DoomBuilder.ZDoom case "action": case "native": // We don't need this, ignore up to the first next ; - while (parser.SkipWhitespace(true)) + while(parser.SkipWhitespace(true)) { string t = parser.ReadToken(); - if (string.IsNullOrEmpty(t) || t == ";") break; + if(string.IsNullOrEmpty(t) || t == ";") break; } break; @@ -225,31 +222,31 @@ namespace CodeImp.DoomBuilder.ZDoom case "states": // Now parse actor states until we reach the end of the states structure - while (parser.SkipWhitespace(true)) + while(parser.SkipWhitespace(true)) { string statetoken = parser.ReadToken(); - if (!string.IsNullOrEmpty(statetoken)) + if(!string.IsNullOrEmpty(statetoken)) { // Start of scope? - if (statetoken == "{") + if(statetoken == "{") { // This is fine } // End of scope? - else if (statetoken == "}") + else if(statetoken == "}") { // Done with the states, // break out of this parse loop break; } // State label? - else if (statetoken == ":") + else if(statetoken == ":") { - if (!string.IsNullOrEmpty(previoustoken)) + if(!string.IsNullOrEmpty(previoustoken)) { // Parse actor state StateStructure st = new StateStructure(this, parser); - if (parser.HasError) return; + if(parser.HasError) return; states[previoustoken.ToLowerInvariant()] = st; } else @@ -273,11 +270,11 @@ namespace CodeImp.DoomBuilder.ZDoom break; case "var": //mxd - while (parser.SkipWhitespace(true)) + while(parser.SkipWhitespace(true)) { string t = parser.ReadToken(); - if (string.IsNullOrEmpty(t) || t == ";") break; - if (t.StartsWith("user_") && !userVars.Contains(t)) + if(string.IsNullOrEmpty(t) || t == ";") break; + if(t.StartsWith("user_") && !userVars.Contains(t)) userVars.Add(t); } break; @@ -323,17 +320,17 @@ namespace CodeImp.DoomBuilder.ZDoom case "game": // Include all tokens on the same line List games = new List(); - while (parser.SkipWhitespace(false)) + while(parser.SkipWhitespace(false)) { string v = parser.ReadToken(); - if (string.IsNullOrEmpty(v)) + if(string.IsNullOrEmpty(v)) { parser.ReportError("Unexpected end of structure"); return; } - if (v == "\n") break; - if (v == "}") return; //mxd - if (v != ",") games.Add(v.ToLowerInvariant()); + if(v == "\n") break; + if(v == "}") return; //mxd + if(v != ",") games.Add(v.ToLowerInvariant()); } props[token] = games; break; @@ -341,7 +338,7 @@ namespace CodeImp.DoomBuilder.ZDoom // Property default: // Property begins with $? Then the whole line is a single value - if (token.StartsWith("$")) + if(token.StartsWith("$")) { // This is for editor-only properties such as $sprite and $category props[token] = new List { (parser.SkipWhitespace(false) ? parser.ReadLine() : "") }; @@ -350,17 +347,17 @@ namespace CodeImp.DoomBuilder.ZDoom { // Next tokens up until the next newline are values List values = new List(); - while (parser.SkipWhitespace(false)) + while(parser.SkipWhitespace(false)) { string v = parser.ReadToken(); - if (string.IsNullOrEmpty(v)) + if(string.IsNullOrEmpty(v)) { parser.ReportError("Unexpected end of structure"); return; } - if (v == "\n") break; - if (v == "}") return; //mxd - if (v != ",") values.Add(v); + if(v == "\n") break; + if(v == "}") return; //mxd + if(v != ",") values.Add(v); } //mxd. Translate scale to xscale and yscale @@ -377,7 +374,7 @@ namespace CodeImp.DoomBuilder.ZDoom break; } - if (done) break; //mxd + if(done) break; //mxd // Keep token previoustoken = token; @@ -407,16 +404,16 @@ namespace CodeImp.DoomBuilder.ZDoom //properties if(!props.ContainsKey("height")) - props["height"] = new List() { ti.Value.Height.ToString() }; + props["height"] = new List { ti.Value.Height.ToString() }; if(!props.ContainsKey("radius")) - props["radius"] = new List() { ti.Value.Radius.ToString() }; + props["radius"] = new List { ti.Value.Radius.ToString() }; return; } } - General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + ":" + doomednum + "'"); + parser.LogWarning("Unable to find '" + inheritclass + "' class to inherit from, while parsing '" + classname + ":" + doomednum + "'"); } } @@ -438,12 +435,9 @@ namespace CodeImp.DoomBuilder.ZDoom /// public bool HasProperty(string propname) { - if(props.ContainsKey(propname)) - return true; - else if(!skipsuper && (baseclass != null)) - return baseclass.HasProperty(propname); - else - return false; + if(props.ContainsKey(propname)) return true; + if(!skipsuper && (baseclass != null)) return baseclass.HasProperty(propname); + return false; } /// @@ -451,12 +445,9 @@ namespace CodeImp.DoomBuilder.ZDoom /// public bool HasPropertyWithValue(string propname) { - if(props.ContainsKey(propname) && (props[propname].Count > 0)) - return true; - else if(!skipsuper && (baseclass != null)) - return baseclass.HasPropertyWithValue(propname); - else - return false; + if(props.ContainsKey(propname) && (props[propname].Count > 0)) return true; + if(!skipsuper && (baseclass != null)) return baseclass.HasPropertyWithValue(propname); + return false; } /// @@ -466,10 +457,9 @@ namespace CodeImp.DoomBuilder.ZDoom { if(props.ContainsKey(propname) && (props[propname].Count > 0)) return string.Join(" ", props[propname].ToArray()); - else if(!skipsuper && (baseclass != null)) + if(!skipsuper && (baseclass != null)) return baseclass.GetPropertyAllValues(propname); - else - return ""; + return ""; } /// @@ -479,10 +469,9 @@ namespace CodeImp.DoomBuilder.ZDoom { if(props.ContainsKey(propname) && (props[propname].Count > valueindex)) return props[propname][valueindex]; - else if(!skipsuper && (baseclass != null)) + if(!skipsuper && (baseclass != null)) return baseclass.GetPropertyValueString(propname, valueindex); - else - return ""; + return ""; } /// @@ -524,10 +513,8 @@ namespace CodeImp.DoomBuilder.ZDoom /// public bool HasFlagValue(string flag) { - if(flags.ContainsKey(flag)) - return true; - if(!skipsuper && (baseclass != null)) - return baseclass.HasFlagValue(flag); + if(flags.ContainsKey(flag)) return true; + if(!skipsuper && (baseclass != null)) return baseclass.HasFlagValue(flag); return false; } @@ -536,10 +523,8 @@ namespace CodeImp.DoomBuilder.ZDoom /// public bool GetFlagValue(string flag, bool defaultvalue) { - if(flags.ContainsKey(flag)) - return flags[flag]; - if(!skipsuper && (baseclass != null)) - return baseclass.GetFlagValue(flag, defaultvalue); + if(flags.ContainsKey(flag)) return flags[flag]; + if(!skipsuper && (baseclass != null)) return baseclass.GetFlagValue(flag, defaultvalue); return defaultvalue; } @@ -548,10 +533,8 @@ namespace CodeImp.DoomBuilder.ZDoom /// public bool HasState(string statename) { - if(states.ContainsKey(statename)) - return true; - if(!skipsuper && (baseclass != null)) - return baseclass.HasState(statename); + if(states.ContainsKey(statename)) return true; + if(!skipsuper && (baseclass != null)) return baseclass.HasState(statename); return false; } @@ -560,10 +543,8 @@ namespace CodeImp.DoomBuilder.ZDoom /// public StateStructure GetState(string statename) { - if(states.ContainsKey(statename)) - return states[statename]; - if(!skipsuper && (baseclass != null)) - return baseclass.GetState(statename); + if(states.ContainsKey(statename)) return states[statename]; + if(!skipsuper && (baseclass != null)) return baseclass.GetState(statename); return null; } @@ -734,7 +715,7 @@ namespace CodeImp.DoomBuilder.ZDoom if(!string.IsNullOrEmpty(result)) { - if (voxels.ContainsKey(result)) return result; + if(voxels.ContainsKey(result)) return result; // The sprite name may be incomplete. Find an existing sprite with direction. foreach(string postfix in SPRITE_POSTFIXES) diff --git a/Source/Core/ZDoom/DecorateParser.cs b/Source/Core/ZDoom/DecorateParser.cs index b87e5656..c7215d08 100644 --- a/Source/Core/ZDoom/DecorateParser.cs +++ b/Source/Core/ZDoom/DecorateParser.cs @@ -119,42 +119,56 @@ namespace CodeImp.DoomBuilder.ZDoom if(!string.IsNullOrEmpty(objdeclaration)) { objdeclaration = objdeclaration.ToLowerInvariant(); - if(objdeclaration == "actor") + switch(objdeclaration) { - // Read actor structure - ActorStructure actor = new ActorStructure(this); - if(this.HasError) break; - - // Add the actor - archivedactors[actor.ClassName.ToLowerInvariant()] = actor; - if(actor.CheckActorSupported()) - actors[actor.ClassName.ToLowerInvariant()] = actor; - - // Replace an actor? - if(actor.ReplacesClass != null) + case "actor": { - if(GetArchivedActorByName(actor.ReplacesClass) != null) - archivedactors[actor.ReplacesClass.ToLowerInvariant()] = actor; - else - General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + actor.ReplacesClass + "' to replace, while parsing '" + actor.ClassName + "'"); - + // Read actor structure + ActorStructure actor = new ActorStructure(this); + if(this.HasError) return false; + + // Add the actor + archivedactors[actor.ClassName.ToLowerInvariant()] = actor; if(actor.CheckActorSupported()) + actors[actor.ClassName.ToLowerInvariant()] = actor; + + // Replace an actor? + if(actor.ReplacesClass != null) { - if(GetActorByName(actor.ReplacesClass) != null) + if(GetArchivedActorByName(actor.ReplacesClass) != null) + archivedactors[actor.ReplacesClass.ToLowerInvariant()] = actor; + else + LogWarning("Unable to find '" + actor.ReplacesClass + "' class to replace, while parsing '" + actor.ClassName + "'"); + + if(actor.CheckActorSupported() && GetActorByName(actor.ReplacesClass) != null) actors[actor.ReplacesClass.ToLowerInvariant()] = actor; } } - } - else if(objdeclaration == "#include") - { - // Include a file - SkipWhitespace(true); - string filename = ReadToken(); - if(!string.IsNullOrEmpty(filename)) + break; + + case "#include": { + // Include a file + SkipWhitespace(true); + string filename = ReadToken(false); //mxd. Don't skip newline + + //mxd. Sanity checks + if(!filename.StartsWith("\"") || !filename.EndsWith("\"")) + { + ReportError("#include filename should be quoted"); + return false; + } + // Strip the quotes filename = filename.Replace("\"", ""); + //mxd. More sanity checks + if(string.IsNullOrEmpty(filename)) + { + ReportError("Expected file name to include"); + return false; + } + // Callback to parse this file now if(OnInclude != null) OnInclude(this, filename); @@ -162,50 +176,48 @@ namespace CodeImp.DoomBuilder.ZDoom datastream = localstream; datareader = localreader; sourcename = localsourcename; - if(HasError) break; + if(this.HasError) return false; } - else - { - ReportError("Expected file name to include"); - break; - } - } - else if((objdeclaration == "const") || (objdeclaration == "native") || (objdeclaration == "enum")) - { - // We don't need this, ignore up to the first next ; - while(SkipWhitespace(true)) - { - string t = ReadToken(); - if(string.IsNullOrEmpty(t) || t == ";") break; - } - } - else if(objdeclaration == "$gzdb_skip") //mxd - { break; - } - else - { - // Unknown structure! - // Best we can do now is just find the first { and then - // follow the scopes until the matching } is found - string token2; - do + + case "enum": + case "native": + case "const": + while(SkipWhitespace(true)) + { + string t = ReadToken(); + if(string.IsNullOrEmpty(t) || t == ";") break; + } + break; + + case "$gzdb_skip": break; + + default: { - if(!SkipWhitespace(true)) break; - token2 = ReadToken(); - if(string.IsNullOrEmpty(token2)) break; + // Unknown structure! + // Best we can do now is just find the first { and then + // follow the scopes until the matching } is found + string token2; + do + { + if(!SkipWhitespace(true)) break; + token2 = ReadToken(); + if(string.IsNullOrEmpty(token2)) break; + } + while(token2 != "{"); + + int scopelevel = 1; + do + { + if(!SkipWhitespace(true)) break; + token2 = ReadToken(); + if(string.IsNullOrEmpty(token2)) break; + if(token2 == "{") scopelevel++; + if(token2 == "}") scopelevel--; + } + while(scopelevel > 0); } - while(token2 != "{"); - int scopelevel = 1; - do - { - if(!SkipWhitespace(true)) break; - token2 = ReadToken(); - if(string.IsNullOrEmpty(token2)) break; - if(token2 == "{") scopelevel++; - if(token2 == "}") scopelevel--; - } - while(scopelevel > 0); + break; } } } @@ -224,10 +236,7 @@ namespace CodeImp.DoomBuilder.ZDoom public ActorStructure GetActorByName(string name) { name = name.ToLowerInvariant(); - if(actors.ContainsKey(name)) - return actors[name]; - else - return null; + return actors.ContainsKey(name) ? actors[name] : null; } /// @@ -235,12 +244,8 @@ namespace CodeImp.DoomBuilder.ZDoom /// public ActorStructure GetActorByDoomEdNum(int doomednum) { - ICollection collection = actors.Values; - foreach(ActorStructure a in collection) - { - if(a.DoomEdNum == doomednum) - return a; - } + foreach(ActorStructure a in actors.Values) + if(a.DoomEdNum == doomednum) return a; return null; } @@ -251,6 +256,12 @@ namespace CodeImp.DoomBuilder.ZDoom name = name.ToLowerInvariant(); return (archivedactors.ContainsKey(name) ? archivedactors[name] : null); } + + //mxd + protected override string GetLanguageType() + { + return "DECORATE"; + } #endregion } diff --git a/Source/Core/ZDoom/PatchStructure.cs b/Source/Core/ZDoom/PatchStructure.cs index fac249b7..c036d285 100644 --- a/Source/Core/ZDoom/PatchStructure.cs +++ b/Source/Core/ZDoom/PatchStructure.cs @@ -83,7 +83,7 @@ namespace CodeImp.DoomBuilder.ZDoom // First token is the class name parser.SkipWhitespace(true); - name = parser.StripTokenQuotes(parser.ReadToken()); + name = parser.StripTokenQuotes(parser.ReadToken(false)); //mxd. Don't skip newline if(string.IsNullOrEmpty(name)) { parser.ReportError("Expected patch name"); @@ -163,36 +163,36 @@ namespace CodeImp.DoomBuilder.ZDoom break; case "rotate": - if (!ReadTokenInt(parser, token, out rotation)) return; + if(!ReadTokenInt(parser, token, out rotation)) return; rotation = rotation % 360; //Coalesce multiples - if (rotation < 0) rotation += 360; //Force positive + if(rotation < 0) rotation += 360; //Force positive - if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) + if(rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) { - General.ErrorLogger.Add(ErrorType.Warning, "Got unsupported rotation (" + rotation + ") in patch " + name); + parser.LogWarning("Got unsupported rotation (" + rotation + ") in patch '" + name + "'"); rotation = 0; } break; case "style": //mxd string s; - if (!ReadTokenString(parser, token, out s)) return; + if(!ReadTokenString(parser, token, out s)) return; int index = Array.IndexOf(renderStyles, s.ToLowerInvariant()); renderStyle = index == -1 ? TexturePathRenderStyle.Copy : (TexturePathRenderStyle) index; break; case "blend": //mxd int val; - if (!ReadTokenColor(parser, token, out val)) return; + if(!ReadTokenColor(parser, token, out val)) return; blendColor = PixelColor.FromInt(val); parser.SkipWhitespace(false); token = parser.ReadToken(); - if (token == ",") //read tint ammount + if(token == ",") //read tint ammount { parser.SkipWhitespace(false); - if (!ReadTokenFloat(parser, token, out tintAmmount)) return; + if(!ReadTokenFloat(parser, token, out tintAmmount)) return; tintAmmount = General.Clamp(tintAmmount, 0.0f, 1.0f); blendStyle = TexturePathBlendStyle.Tint; } @@ -278,6 +278,7 @@ namespace CodeImp.DoomBuilder.ZDoom parser.ReportError("Expected a value for property '" + propertyname + "'"); return false; } + return true; } @@ -306,7 +307,8 @@ namespace CodeImp.DoomBuilder.ZDoom { parser.ReportError("Expected color value for property '" + propertyname + "'"); return false; - } + } + return true; } diff --git a/Source/Core/ZDoom/ReverbsParser.cs b/Source/Core/ZDoom/ReverbsParser.cs index 09ffe67a..5186a0de 100644 --- a/Source/Core/ZDoom/ReverbsParser.cs +++ b/Source/Core/ZDoom/ReverbsParser.cs @@ -47,7 +47,7 @@ namespace CodeImp.DoomBuilder.ZDoom if(string.IsNullOrEmpty(name)) { - ReportError("Got empty sound environment name!"); + ReportError("Got empty sound environment name"); break; } @@ -57,7 +57,7 @@ namespace CodeImp.DoomBuilder.ZDoom int arg1; if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out arg1)) { - ReportError("Failed to parse the first part of '" + name + "' sound environment ID!"); + ReportError("Failed to parse the first part of '" + name + "' sound environment ID"); break; } @@ -67,7 +67,7 @@ namespace CodeImp.DoomBuilder.ZDoom int arg2; if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out arg2)) { - ReportError("Failed to parse the second part of '" + name + "' sound environment ID!"); + ReportError("Failed to parse the second part of '" + name + "' sound environment ID"); break; } @@ -75,7 +75,7 @@ namespace CodeImp.DoomBuilder.ZDoom int combinedindex = combinedargs.IndexOf(combined); if(combinedindex != -1) { - General.ErrorLogger.Add(ErrorType.Warning, "'" + names[combinedindex] + "' and '" + name + "' sound environments share the same ID (" + arg1 + " " + arg2 + ")!"); + LogWarning("'" + names[combinedindex] + "' and '" + name + "' sound environments share the same ID (" + arg1 + " " + arg2 + ")"); } else { @@ -84,7 +84,7 @@ namespace CodeImp.DoomBuilder.ZDoom // Add to collections if(names.Contains(name)) { - General.ErrorLogger.Add(ErrorType.Warning, "'" + name + "' sound environment is double-defined in '" + sourcefilename + "'!"); + LogWarning("'" + name + "' sound environment is double-defined in '" + sourcefilename + "'"); int index = names.IndexOf(name); firstargs[index] = arg1; secondargs[index] = arg2; @@ -119,5 +119,10 @@ namespace CodeImp.DoomBuilder.ZDoom return result; } + + protected override string GetLanguageType() + { + return "REVERBS"; + } } } diff --git a/Source/Core/ZDoom/SndSeqParser.cs b/Source/Core/ZDoom/SndSeqParser.cs index 79191452..3459884b 100644 --- a/Source/Core/ZDoom/SndSeqParser.cs +++ b/Source/Core/ZDoom/SndSeqParser.cs @@ -86,6 +86,11 @@ namespace CodeImp.DoomBuilder.ZDoom return result; } + protected override string GetLanguageType() + { + return "SNDSEQ"; + } + #endregion } } \ No newline at end of file diff --git a/Source/Core/ZDoom/StateStructure.cs b/Source/Core/ZDoom/StateStructure.cs index 7d53b7a5..18f79caf 100644 --- a/Source/Core/ZDoom/StateStructure.cs +++ b/Source/Core/ZDoom/StateStructure.cs @@ -32,9 +32,9 @@ namespace CodeImp.DoomBuilder.ZDoom #region ================== Variables // All we care about is the first sprite in the sequence - private List sprites; - private StateGoto gotostate; - private DecorateParser parser; + private readonly List sprites; + private readonly StateGoto gotostate; + private readonly DecorateParser parser; #endregion @@ -77,20 +77,21 @@ namespace CodeImp.DoomBuilder.ZDoom else if(token == ":") { // Rewind so that this label can be read again - parser.DataStream.Seek(-(lasttoken.Length + 1), SeekOrigin.Current); + if(!string.IsNullOrEmpty(lasttoken)) + parser.DataStream.Seek(-(lasttoken.Length + 1), SeekOrigin.Current); // Done here return; } //mxd. Start of inner scope? - else if (token == "{") + else if(token == "{") { int bracelevel = 1; while(!string.IsNullOrEmpty(token) && bracelevel > 0) { parser.SkipWhitespace(false); token = parser.ReadToken(); - switch (token) + switch(token) { case "{": bracelevel++; break; case "}": bracelevel--; break; @@ -143,7 +144,7 @@ namespace CodeImp.DoomBuilder.ZDoom spritename = spritename.ToUpperInvariant(); // Ignore some odd ZDoom things - if (!spritename.StartsWith("TNT1") && !spritename.StartsWith("----") && !spritename.Contains("#")) + if(!spritename.StartsWith("TNT1") && !spritename.StartsWith("----") && !spritename.Contains("#")) sprites.Add(spritename); } @@ -202,10 +203,7 @@ namespace CodeImp.DoomBuilder.ZDoom private string GetSprite(int index, List prevstates) { // If we have sprite of our own, see if we can return this index - if(index < sprites.Count) - { - return sprites[index]; - } + if(index < sprites.Count) return sprites[index]; // Otherwise, continue searching where goto tells us to go if(gotostate != null) diff --git a/Source/Core/ZDoom/TextureStructure.cs b/Source/Core/ZDoom/TextureStructure.cs index 4dddc6cd..63ffb76b 100644 --- a/Source/Core/ZDoom/TextureStructure.cs +++ b/Source/Core/ZDoom/TextureStructure.cs @@ -84,20 +84,20 @@ namespace CodeImp.DoomBuilder.ZDoom // There should be 3 tokens separated by 2 commas now: // Name, Width, Height - // First token is the class name + // First token is the texture name parser.SkipWhitespace(true); - name = parser.StripTokenQuotes(parser.ReadToken()); + name = parser.StripTokenQuotes(parser.ReadToken(false)); //mxd. Don't skip newline //mxd. It can also be "optional" keyword. if(name.ToLowerInvariant() == "optional") { parser.SkipWhitespace(true); - name = parser.StripTokenQuotes(parser.ReadToken()); + name = parser.StripTokenQuotes(parser.ReadToken(false)); //mxd. Don't skip newline } if(string.IsNullOrEmpty(name)) { - parser.ReportError("Expected texture or sprite name"); + parser.ReportError("Expected " + typename + " name"); return; } @@ -153,14 +153,14 @@ namespace CodeImp.DoomBuilder.ZDoom string token = parser.ReadToken(); token = token.ToLowerInvariant(); - switch (token) + switch(token) { case "xscale": - if (!ReadTokenFloat(parser, token, out xscale)) return; + if(!ReadTokenFloat(parser, token, out xscale)) return; break; case "yscale": - if (!ReadTokenFloat(parser, token, out yscale)) return; + if(!ReadTokenFloat(parser, token, out yscale)) return; break; case "worldpanning": @@ -169,25 +169,25 @@ namespace CodeImp.DoomBuilder.ZDoom case "offset": // Read x offset - if (!ReadTokenInt(parser, token, out xoffset)) return; + if(!ReadTokenInt(parser, token, out xoffset)) return; // Now we should find a comma parser.SkipWhitespace(true); tokenstr = parser.ReadToken(); - if (tokenstr != ",") + if(tokenstr != ",") { parser.ReportError("Expected a comma"); return; } // Read y offset - if (!ReadTokenInt(parser, token, out yoffset)) return; + if(!ReadTokenInt(parser, token, out yoffset)) return; break; case "patch": // Read patch structure PatchStructure pt = new PatchStructure(parser); - if (parser.HasError) break; + if(parser.HasError) break; //mxd. Let's ignore TNT1A0 if(pt.Name == IGNORE_SPRITE) break; diff --git a/Source/Core/ZDoom/TexturesParser.cs b/Source/Core/ZDoom/TexturesParser.cs index c9ec9907..7f62efe5 100644 --- a/Source/Core/ZDoom/TexturesParser.cs +++ b/Source/Core/ZDoom/TexturesParser.cs @@ -46,9 +46,9 @@ namespace CodeImp.DoomBuilder.ZDoom #region ================== Properties - public ICollection Textures { get { return textures.Values; } } - public ICollection Flats { get { return flats.Values; } } - public ICollection Sprites { get { return sprites.Values; } } + public IEnumerable Textures { get { return textures.Values; } } + public IEnumerable Flats { get { return flats.Values; } } + public IEnumerable Sprites { get { return sprites.Values; } } #endregion @@ -89,104 +89,139 @@ namespace CodeImp.DoomBuilder.ZDoom if(!string.IsNullOrEmpty(objdeclaration)) { objdeclaration = objdeclaration.ToLowerInvariant(); - if(objdeclaration == "texture") + switch(objdeclaration) { - // Read texture structure - TextureStructure tx = new TextureStructure(this, "texture", virtualpath); - if(this.HasError) break; + case "texture": + { + // Read texture structure + TextureStructure tx = new TextureStructure(this, "texture", virtualpath); + if(this.HasError) return false; + + // if a limit for the texture name length is set make sure that it's not exceeded + if(tx.Name.Length > General.Map.Config.MaxTextureNameLength) + { + ReportError("Texture name \"" + tx.Name + "\" too long. Texture names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); + return false; + } + + //mxd. Can't load image without name + if(string.IsNullOrEmpty(tx.Name)) + { + ReportError("Can't load an unnamed texture. Please consider giving names to your resources."); + return false; + } - // if a limit for the texture name length is set make sure that it's not exceeded - if(tx.Name.Length > General.Map.Config.MaxTextureNameLength) - { - General.ErrorLogger.Add(ErrorType.Error, "Texture name \"" + tx.Name + "\" too long. Texture names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); - } - else - { // Add the texture textures[tx.Name] = tx; if(!General.Map.Config.MixTexturesFlats) flats[tx.Name] = tx; //mxd. If MixTexturesFlats is set, textures and flats will be mixed in DataManager anyway } - } - else if(objdeclaration == "sprite") - { - // Read sprite structure - TextureStructure tx = new TextureStructure(this, "sprite", virtualpath); - if(this.HasError) break; + break; - // if a limit for the sprite name length is set make sure that it's not exceeded - if(tx.Name.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH) - { - General.ErrorLogger.Add(ErrorType.Error, "Sprite name \"" + tx.Name + "\" too long. Sprite names must have a length of " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " characters or less"); - } - else + case "sprite": { + // Read sprite structure + TextureStructure tx = new TextureStructure(this, "sprite", virtualpath); + if(this.HasError) return false; + + // if a limit for the sprite name length is set make sure that it's not exceeded + if(tx.Name.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH) + { + ReportError("Sprite name \"" + tx.Name + "\" too long. Sprite names must have a length of " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " characters or less"); + return false; + } + + //mxd. Can't load image without name + if(string.IsNullOrEmpty(tx.Name)) + { + ReportError("Can't load an unnamed sprite. Please consider giving names to your resources."); + return false; + } + // Add the sprite sprites[tx.Name] = tx; } - } - else if(objdeclaration == "walltexture") - { - // Read walltexture structure - TextureStructure tx = new TextureStructure(this, "walltexture", virtualpath); - if(this.HasError) break; + break; - // if a limit for the walltexture name length is set make sure that it's not exceeded - if(tx.Name.Length > General.Map.Config.MaxTextureNameLength) - { - General.ErrorLogger.Add(ErrorType.Error, "WallTexture name \"" + tx.Name + "\" too long. WallTexture names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); - } - else + case "walltexture": { + // Read walltexture structure + TextureStructure tx = new TextureStructure(this, "walltexture", virtualpath); + if(this.HasError) return false; + + // if a limit for the walltexture name length is set make sure that it's not exceeded + if(tx.Name.Length > General.Map.Config.MaxTextureNameLength) + { + ReportError("WallTexture name \"" + tx.Name + "\" too long. WallTexture names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); + return false; + } + + //mxd. Can't load image without name + if(string.IsNullOrEmpty(tx.Name)) + { + ReportError("Can't load an unnamed WallTexture. Please consider giving names to your resources."); + return false; + } + // Add the walltexture if(!textures.ContainsKey(tx.Name) || (textures[tx.Name].TypeName != "texture")) textures[tx.Name] = tx; } - } - else if(objdeclaration == "flat") - { - // Read flat structure - TextureStructure tx = new TextureStructure(this, "flat", virtualpath); - if(this.HasError) break; + break; - // if a limit for the flat name length is set make sure that it's not exceeded - if(tx.Name.Length > General.Map.Config.MaxTextureNameLength) - { - General.ErrorLogger.Add(ErrorType.Error, "Flat name \"" + tx.Name + "\" too long. Flat names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); - } - else + case "flat": { + // Read flat structure + TextureStructure tx = new TextureStructure(this, "flat", virtualpath); + if(this.HasError) return false; + + // if a limit for the flat name length is set make sure that it's not exceeded + if(tx.Name.Length > General.Map.Config.MaxTextureNameLength) + { + ReportError("Flat name \"" + tx.Name + "\" too long. Flat names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); + return false; + } + + //mxd. Can't load image without name + if(string.IsNullOrEmpty(tx.Name)) + { + ReportError("Can't load an unnamed flat. Please consider giving names to your resources."); + return false; + } + // Add the flat if(!flats.ContainsKey(tx.Name) || (flats[tx.Name].TypeName != "texture")) flats[tx.Name] = tx; } - } - else if(objdeclaration == "$gzdb_skip") //mxd - { break; - } - else - { - // Unknown structure! - // Best we can do now is just find the first { and then - // follow the scopes until the matching } is found - string token2; - do + + case "$gzdb_skip": break; + + default: { - if(!SkipWhitespace(true)) break; - token2 = ReadToken(); - if(string.IsNullOrEmpty(token2)) break; + // Unknown structure! + // Best we can do now is just find the first { and then + // follow the scopes until the matching } is found + string token2; + do + { + if(!SkipWhitespace(true)) break; + token2 = ReadToken(); + if(string.IsNullOrEmpty(token2)) break; + } + while(token2 != "{"); + + int scopelevel = 1; + do + { + if(!SkipWhitespace(true)) break; + token2 = ReadToken(); + if(string.IsNullOrEmpty(token2)) break; + if(token2 == "{") scopelevel++; + if(token2 == "}") scopelevel--; + } + while(scopelevel > 0); } - while(token2 != "{"); - int scopelevel = 1; - do - { - if(!SkipWhitespace(true)) break; - token2 = ReadToken(); - if(string.IsNullOrEmpty(token2)) break; - if(token2 == "{") scopelevel++; - if(token2 == "}") scopelevel--; - } - while(scopelevel > 0); + break; } } } @@ -194,6 +229,12 @@ namespace CodeImp.DoomBuilder.ZDoom // Return true when no errors occurred return (ErrorDescription == null); } + + //mxd + protected override string GetLanguageType() + { + return "TEXTURES"; + } #endregion } diff --git a/Source/Core/ZDoom/VoxeldefParser.cs b/Source/Core/ZDoom/VoxeldefParser.cs index 33b338ac..89a46d42 100644 --- a/Source/Core/ZDoom/VoxeldefParser.cs +++ b/Source/Core/ZDoom/VoxeldefParser.cs @@ -49,17 +49,15 @@ namespace CodeImp.DoomBuilder.ZDoom if(string.IsNullOrEmpty(token)) { - General.ErrorLogger.Add(ErrorType.Error, "Unable to get voxel model name from '" + sourcefilename + "', line " + GetCurrentLineNumber()); - spriteNames.Clear(); - continue; + ReportError("Expected voxel name"); + return false; } modelName = StripTokenQuotes(token).ToLowerInvariant(); } else if(token == "{") //read the settings { - ModelData mde = new ModelData(); - mde.IsVoxel = true; + ModelData mde = new ModelData { IsVoxel = true }; float scale = 1.0f; float angleoffset = 0; @@ -109,15 +107,16 @@ namespace CodeImp.DoomBuilder.ZDoom token = StripTokenQuotes(ReadToken()); if(token != "=") { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected '=', but got '" + token + "'"); - break; + ReportError("expected '=', but got '" + token + "'"); + return false; } token = StripTokenQuotes(ReadToken()); if(!ReadSignedFloat(token, ref angleoffset)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected AngleOffset value, but got '" + token + "'"); + ReportError("expected AngleOffset value, but got '" + token + "'"); + return false; } } else if(token == "scale") @@ -127,17 +126,19 @@ namespace CodeImp.DoomBuilder.ZDoom token = StripTokenQuotes(ReadToken()); if(token != "=") { - General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected '=', but got '" + token + "'"); - break; + ReportError("expected '=', but got '" + token + "'"); + return false; } token = StripTokenQuotes(ReadToken()); if(!ReadSignedFloat(token, ref scale)) { // Not numeric! - General.ErrorLogger.Add(ErrorType.Error, "Error in " + sourcefilename + " at line " + GetCurrentLineNumber() + ": expected Scale value, but got '" + token + "'"); + ReportError("expected Scale value, but got '" + token + "'"); + return false; } } + prevToken = token.ToUpperInvariant(); } } @@ -151,5 +152,10 @@ namespace CodeImp.DoomBuilder.ZDoom return entries.Count > 0; } + + protected override string GetLanguageType() + { + return "VOXELDEF"; + } } } diff --git a/Source/Core/ZDoom/ZDTextParser.cs b/Source/Core/ZDoom/ZDTextParser.cs index d677989d..b95331b6 100644 --- a/Source/Core/ZDoom/ZDTextParser.cs +++ b/Source/Core/ZDoom/ZDTextParser.cs @@ -47,7 +47,7 @@ namespace CodeImp.DoomBuilder.ZDoom private int errorline; private string errordesc; private string errorsource; - private long prevstreamposition; //mxd. Text stream position storted before poerforming ReadToken. + private long prevstreamposition; //mxd. Text stream position storted before performing ReadToken. #endregion @@ -213,7 +213,8 @@ namespace CodeImp.DoomBuilder.ZDoom // This reads a token (all sequential non-whitespace characters or a single character) // Returns null when the end of the stream has been reached - protected internal string ReadToken() + protected internal string ReadToken() { return ReadToken(true); } //mxd. Added "multiline" param + protected internal string ReadToken(bool multiline) { //mxd. Return empty string when the end of the stream has been reached if(datastream.Position == datastream.Length) return string.Empty; @@ -228,6 +229,14 @@ namespace CodeImp.DoomBuilder.ZDoom char c = (char)datareader.ReadByte(); while(!IsWhitespace(c) || quotedstring || IsSpecialToken(c)) { + //mxd. Break at newline? + if(!multiline && c == '\r') + { + // Go one character back so line number is correct + datastream.Seek(-1, SeekOrigin.Current); + return token; + } + // Special token? if(!quotedstring && IsSpecialToken(c)) { @@ -418,7 +427,7 @@ namespace CodeImp.DoomBuilder.ZDoom if(token != expectedtoken) { - if(reporterror) General.ErrorLogger.Add(ErrorType.Error, "Error in '" + sourcename + "' at line " + GetCurrentLineNumber() + ": expected '" + expectedtoken + "', but got '" + token + "'"); + if(reporterror) ReportError("expected '" + expectedtoken + "', but got '" + token + "'"); // Rewind so this structure can be read again DataStream.Seek(-token.Length - 1, SeekOrigin.Current); @@ -469,20 +478,39 @@ namespace CodeImp.DoomBuilder.ZDoom errorsource = sourcename; } - //mxd. This reports a warning - protected internal void ReportWarning(string message) + //mxd. This adds a warning to the ErrorLogger + protected internal void LogWarning(string message) { // Add a warning - General.ErrorLogger.Add(ErrorType.Warning, "DECORATE warning in '" + sourcename + "', line " + GetCurrentLineNumber() + ". " + message + "."); + int errline = GetCurrentLineNumber(); + General.ErrorLogger.Add(ErrorType.Warning, GetLanguageType() + " warning in '" + sourcename + + (errline != CompilerError.NO_LINE_NUMBER ? "', line " + (errline + 1) : "'") + ". " + + message + "."); + } + + //mxd. This adds an error to the ErrorLogger + public void LogError() + { + General.ErrorLogger.Add(ErrorType.Error, GetLanguageType() + " error in '" + errorsource + + (errorline != CompilerError.NO_LINE_NUMBER ? "', line " + (errorline + 1) : "'") + ". " + + errordesc + "."); + } + + //mxd + internal void ClearError() + { + errorline = 0; + errordesc = null; + errorsource = null; } //mxd - protected internal int GetCurrentLineNumber() + protected int GetCurrentLineNumber() { long pos = datastream.Position; long finishpos = Math.Min(prevstreamposition, pos); long readpos = 0; - int linenumber = 0; + int linenumber = -1; // Find the line on which we found this error datastream.Seek(0, SeekOrigin.Begin); @@ -498,9 +526,12 @@ namespace CodeImp.DoomBuilder.ZDoom // Return to original position datastream.Seek(pos, SeekOrigin.Begin); - return linenumber; + return Math.Max(linenumber, 0); } - + + //mxd. Language type + protected abstract string GetLanguageType(); + #endregion } }