From f3fbf241d92c132056584db699fdb83f48502eff Mon Sep 17 00:00:00 2001 From: MaxED Date: Mon, 21 Dec 2015 14:17:47 +0000 Subject: [PATCH] Fixed, text parsers: in some cases include directives were processed differently from ZDoom. Fixed, ACS parser: absolute and relative include paths handling was broken in R2448. --- Source/Core/Compilers/AccCompiler.cs | 2 +- Source/Core/Config/CompilerInfo.cs | 16 +++- Source/Core/Data/DataManager.cs | 72 +++++++-------- Source/Core/GZBuilder/GZDoom/AcsParserSE.cs | 59 +++++++----- Source/Core/GZBuilder/GZDoom/GldefsParser.cs | 89 +++++++++++-------- Source/Core/GZBuilder/GZDoom/MapinfoParser.cs | 86 +++++++++++++----- .../Core/GZBuilder/GZDoom/ModeldefParser.cs | 48 +++++----- .../GZBuilder/GZDoom/ModeldefStructure.cs | 46 +++++----- Source/Core/ZDoom/AnimdefsParser.cs | 12 +-- Source/Core/ZDoom/DecorateParser.cs | 58 +++++++++--- Source/Core/ZDoom/PatchStructure.cs | 34 ++----- Source/Core/ZDoom/ReverbsParser.cs | 6 +- Source/Core/ZDoom/TextureStructure.cs | 84 +++++------------ Source/Core/ZDoom/TexturesParser.cs | 8 +- Source/Core/ZDoom/VoxeldefParser.cs | 39 ++------ Source/Core/ZDoom/ZDTextParser.cs | 77 +++++++++++++++- 16 files changed, 417 insertions(+), 319 deletions(-) diff --git a/Source/Core/Compilers/AccCompiler.cs b/Source/Core/Compilers/AccCompiler.cs index add7b874..89653768 100644 --- a/Source/Core/Compilers/AccCompiler.cs +++ b/Source/Core/Compilers/AccCompiler.cs @@ -79,7 +79,7 @@ namespace CodeImp.DoomBuilder.Compilers foreach(string include in includes) { // Grab the script text from the resources - MemoryStream s = General.Map.Data.LoadFile(include); + Stream s = General.Map.Data.LoadFile(include); if(s != null) { diff --git a/Source/Core/Config/CompilerInfo.cs b/Source/Core/Config/CompilerInfo.cs index b6ecaade..9cba5741 100644 --- a/Source/Core/Config/CompilerInfo.cs +++ b/Source/Core/Config/CompilerInfo.cs @@ -16,6 +16,7 @@ #region ================== Namespaces +using System; using System.Collections; using System.Collections.Generic; using CodeImp.DoomBuilder.IO; @@ -38,7 +39,7 @@ namespace CodeImp.DoomBuilder.Config private readonly string programfile; private readonly string programinterface; private readonly string path; - private readonly List files; + private readonly HashSet files; #endregion @@ -49,7 +50,7 @@ namespace CodeImp.DoomBuilder.Config public string Path { get { return path; } } public string ProgramFile { get { return programfile; } } public string ProgramInterface { get { return programinterface; } } - public List Files { get { return files; } } + public HashSet Files { get { return files; } } #endregion @@ -64,7 +65,7 @@ namespace CodeImp.DoomBuilder.Config this.filename = filename; this.path = path; this.name = name; - this.files = new List(); + this.files = new HashSet(StringComparer.OrdinalIgnoreCase); //mxd. List -> HashSet // Read program file and interface this.programfile = cfg.ReadSetting("compilers." + name + ".program", ""); @@ -75,7 +76,14 @@ namespace CodeImp.DoomBuilder.Config foreach(DictionaryEntry de in cfgfiles) { if(de.Key.ToString() != "interface" && de.Key.ToString() != "program") - files.Add(de.Value.ToString()); + { + //mxd + string include = de.Value.ToString().Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar); + if(files.Contains(include)) + General.ErrorLogger.Add(ErrorType.Warning, "Include file '" + de.Value + "' is double-defined in '" + name + "' compiler configuration"); + else + files.Add(include); + } } } diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs index 24d7b1ee..c6919919 100644 --- a/Source/Core/Data/DataManager.cs +++ b/Source/Core/Data/DataManager.cs @@ -366,7 +366,7 @@ namespace CodeImp.DoomBuilder.Data LoadReverbs(); LoadSndSeq(); LoadVoxels(); - Dictionary> actorsbyclass = CreateActorsByClassList(); + Dictionary actorsbyclass = CreateActorsByClassList(); LoadModeldefs(actorsbyclass); foreach(Thing t in General.Map.Map.Things) t.UpdateCache(); General.MainWindow.DisplayReady(); @@ -1719,9 +1719,9 @@ namespace CodeImp.DoomBuilder.Data #region ================== mxd. Modeldef, Voxeldef, Gldefs, Mapinfo //mxd. This creates dictionary. Should be called after all DECORATE actors are parsed - private Dictionary> CreateActorsByClassList() + private Dictionary CreateActorsByClassList() { - Dictionary> actors = new Dictionary>(StringComparer.Ordinal); + Dictionary actors = new Dictionary(StringComparer.Ordinal); if(string.IsNullOrEmpty(General.Map.Config.DecorateGames)) return actors; //read our new shiny ClassNames for default game things @@ -1730,13 +1730,15 @@ namespace CodeImp.DoomBuilder.Data if(!string.IsNullOrEmpty(ti.Value.ClassName)) { string classname = ti.Value.ClassName.ToLowerInvariant(); - if(!actors.ContainsKey(classname)) actors.Add(classname, new List()); - actors[classname].Add(ti.Key); + + if(actors.ContainsKey(classname) && actors[classname] != ti.Key) + General.ErrorLogger.Add(ErrorType.Warning, "actor '" + ti.Value.ClassName + "' has several editor numbers! Only the last one (" + ti.Key + ") will be used."); + actors[classname] = ti.Key; } } if(actors.Count == 0) - General.ErrorLogger.Add(ErrorType.Warning, "Warning: unable to find any DECORATE actor definitions!"); + General.ErrorLogger.Add(ErrorType.Warning, "unable to find any DECORATE actor definitions!"); return actors; } @@ -1799,13 +1801,13 @@ namespace CodeImp.DoomBuilder.Data } //mxd. This parses modeldefs. Should be called after all DECORATE actors are parsed and actorsByClass dictionary created - private void LoadModeldefs(Dictionary> actorsByClass) + private void LoadModeldefs(Dictionary actorsbyclass) { //if no actors defined in DECORATE or game config... - if(actorsByClass.Count == 0) return; + if(actorsbyclass.Count == 0) return; - Dictionary modelDefEntriesByName = new Dictionary(StringComparer.Ordinal); - ModeldefParser parser = new ModeldefParser(); + Dictionary modeldefentriesbyname = new Dictionary(StringComparer.Ordinal); + ModeldefParser parser = new ModeldefParser(actorsbyclass); foreach(DataReader dr in containers) { @@ -1819,15 +1821,10 @@ namespace CodeImp.DoomBuilder.Data { foreach(KeyValuePair g in parser.Entries) { - if(modelDefEntriesByName.ContainsKey(g.Key)) - { + if(modeldefentriesbyname.ContainsKey(g.Key)) General.ErrorLogger.Add(ErrorType.Warning, "Model definition for actor '" + g.Key + "' is double-defined in '" + group.Key + "'"); - modelDefEntriesByName[g.Key] = g.Value; - } - else - { - modelDefEntriesByName.Add(g.Key, g.Value); - } + + modeldefentriesbyname[g.Key] = g.Value; } } @@ -1838,16 +1835,12 @@ namespace CodeImp.DoomBuilder.Data currentreader = null; - foreach(KeyValuePair e in modelDefEntriesByName) + foreach(KeyValuePair e in modeldefentriesbyname) { - if(actorsByClass.ContainsKey(e.Key)) - { - foreach(int i in actorsByClass[e.Key]) modeldefentries[i] = modelDefEntriesByName[e.Key]; - } + if(actorsbyclass.ContainsKey(e.Key)) + modeldefentries[actorsbyclass[e.Key]] = modeldefentriesbyname[e.Key]; else if(!decorate.ActorsByClass.ContainsKey(e.Key)) - { General.ErrorLogger.Add(ErrorType.Warning, "Got MODELDEF override for class '" + e.Key + "', but haven't found such class in Decorate"); - } } } @@ -1947,11 +1940,11 @@ namespace CodeImp.DoomBuilder.Data } } - //mxd. This parses gldefs. Should be called after all DECORATE actors are parsed and actorsByClass dictionary created - private void LoadGldefs(Dictionary> actorsByClass) + //mxd. This parses gldefs. Should be called after all DECORATE actors are parsed + private void LoadGldefs(Dictionary actorsbyclass) { //if no actors defined in DECORATE or game config... - if(actorsByClass.Count == 0) return; + if(actorsbyclass.Count == 0) return; GldefsParser parser = new GldefsParser { OnInclude = ParseFromLocation }; @@ -1975,19 +1968,14 @@ namespace CodeImp.DoomBuilder.Data } } - //create gldefsEntries dictionary - foreach(KeyValuePair e in parser.Objects) //ClassName, Light name + //create Gldefs Entries dictionary + foreach(KeyValuePair e in parser.Objects) // { //if we have decorate actor and light definition for given ClassName... - if(actorsByClass.ContainsKey(e.Key) && parser.LightsByName.ContainsKey(e.Value)) - { - foreach(int i in actorsByClass[e.Key]) - gldefsentries[i] = parser.LightsByName[e.Value]; - } + if(actorsbyclass.ContainsKey(e.Key) && parser.LightsByName.ContainsKey(e.Value)) + gldefsentries[actorsbyclass[e.Key]] = parser.LightsByName[e.Value]; else if(!decorate.AllActorsByClass.ContainsKey(e.Key)) - { General.ErrorLogger.Add(ErrorType.Warning, "Got GLDEFS light for class '" + e.Key + "', but haven't found such class in DECORATE"); - } } // Grab them glowy flats! @@ -2162,13 +2150,15 @@ namespace CodeImp.DoomBuilder.Data } //mxd - internal MemoryStream LoadFile(string name) + internal Stream LoadFile(string name) { - // Relative path? - if(name.StartsWith("..\\")) name = name.Replace("..\\", ""); - + // Filesystem path? + if(Path.IsPathRooted(name)) + return (File.Exists(name) ? File.OpenRead(name) : null); + foreach(DataReader dr in containers) if(dr.FileExists(name)) return dr.LoadFile(name); + return null; } diff --git a/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs b/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs index 0d65ee8d..03bcdfb7 100644 --- a/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs +++ b/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Collections.Generic; using System.Globalization; using CodeImp.DoomBuilder.ZDoom; @@ -14,7 +15,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom private readonly HashSet parsedlumps; private readonly HashSet includes; - private List includestoskip; + private HashSet includestoskip; private string libraryname; private readonly List namedscripts; @@ -36,15 +37,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom namedscripts = new List(); numberedscripts = new List(); functions = new List(); - parsedlumps = new HashSet(); - includes = new HashSet(); - includestoskip = new List(); + parsedlumps = new HashSet(StringComparer.OrdinalIgnoreCase); + includes = new HashSet(StringComparer.OrdinalIgnoreCase); + includestoskip = new HashSet(StringComparer.OrdinalIgnoreCase); specialtokens += "(,)"; } public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) { - return Parse(stream, sourcefilename, new List(), false, false, clearerrors); + return Parse(stream, sourcefilename, new HashSet(), false, false, clearerrors); } public bool Parse(Stream stream, string sourcefilename, bool processincludes, bool isinclude, bool clearerrors) @@ -52,19 +53,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return Parse(stream, sourcefilename, includestoskip, processincludes, isinclude, clearerrors); } - public bool Parse(Stream stream, string sourcefilename, List configincludes, bool processincludes, bool isinclude, bool clearerrors) + public bool Parse(Stream stream, string sourcefilename, HashSet configincludes, bool processincludes, bool isinclude, bool clearerrors) { - parsedlumps.Add(sourcefilename); - if(isinclude && !includes.Contains(sourcefilename)) includes.Add(sourcefilename); - includestoskip = configincludes; - int bracelevel = 0; + string source = sourcefilename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); - // Integrity check - if(stream == null || stream.Length == 0) + // Duplicate checks + if(parsedlumps.Contains(source)) { - ReportError("Unable to load " + (isinclude ? "include" : "") + " file '" + sourcefilename + "'!"); + ReportError("Already parsed '" + source + "'. Check your #include directives"); return false; } + + parsedlumps.Add(source); + includestoskip = configincludes; + int bracelevel = 0; if(!base.Parse(stream, sourcefilename, clearerrors)) return false; @@ -204,6 +206,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom default: if(processincludes && (token == "#include" || token == "#import")) { + //INFO: ZDoom ACC include paths can be absolute ("d:\stuff\coollib.acs"), relative ("../coollib.acs") + //and can use forward and backward slashes ("acs\map01/script.acs") + //also include paths must be quoted + //long filenames are supported SkipWhitespace(true); string includelump = ReadToken(false); // Don't skip newline @@ -213,7 +219,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return false; } - includelump = StripTokenQuotes(includelump).ToLowerInvariant(); + includelump = StripTokenQuotes(includelump); if(string.IsNullOrEmpty(includelump)) { @@ -221,26 +227,39 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return false; } - string includename = Path.GetFileName(includelump); + includelump = includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); // Compiler files? - if(includestoskip.Contains(includename)) continue; + if(includestoskip.Contains(includelump)) continue; // Already parsed this? - if(includes.Contains(includename)) + string includelumppath = GetRootedPath(source, includelump); + + // Rooting succeeded? + if(HasError || string.IsNullOrEmpty(includelumppath)) return false; + + // Already parsed? + if(includes.Contains(includelumppath)) { - ReportError("already parsed '" + includename + "'. Check your #include directives"); + ReportError("Already parsed '" + includelump + "'. Check your " + token + " directives"); return false; } + + // Add to collections + includes.Add(includelumppath); // Callback to parse this file - if(OnInclude != null) OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); + if(OnInclude != null) OnInclude(this, includelumppath); + + // Bail out on error + if(this.HasError) return false; // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; } + break; } } diff --git a/Source/Core/GZBuilder/GZDoom/GldefsParser.cs b/Source/Core/GZBuilder/GZDoom/GldefsParser.cs index 25064516..3bc2abdb 100644 --- a/Source/Core/GZBuilder/GZDoom/GldefsParser.cs +++ b/Source/Core/GZBuilder/GZDoom/GldefsParser.cs @@ -49,7 +49,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom private readonly Dictionary lightsByName; //LightName, light definition private readonly Dictionary objects; //ClassName, LightName private readonly Dictionary glowingflats; - private readonly List parsedlumps; + private readonly HashSet parsedlumps; #endregion @@ -69,7 +69,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom whitespace = "\n \t\r\u00A0"; specialtokens = ",{}\n"; - parsedlumps = new List(); + parsedlumps = new HashSet(StringComparer.OrdinalIgnoreCase); lightsByName = new Dictionary(StringComparer.Ordinal); //LightName, Light params objects = new Dictionary(StringComparer.Ordinal); //ClassName, LightName glowingflats = new Dictionary(); // Texture name hash, Glowing Flat Data @@ -81,7 +81,6 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom public override bool Parse(Stream stream, string sourcefilename, bool clearerrors) { - parsedlumps.Add(sourcefilename); if(!base.Parse(stream, sourcefilename, clearerrors)) return false; // Keep local data @@ -92,11 +91,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom // Continue until at the end of the stream while(SkipWhitespace(true)) { - string token = ReadToken(); + string token = StripTokenQuotes(ReadToken()).ToLowerInvariant(); //Quotes can be anywhere! ANYWHERE!!! And GZDoom will still parse data correctly 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 || token == GldefsLightType.FLICKER2 || token == GldefsLightType.SECTOR) @@ -131,7 +128,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Red)) { // Not numeric! - ReportError("expected Red color value, but got '" + token + "'"); + ReportError("Expected Red color value, but got '" + token + "'"); return false; } @@ -141,7 +138,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Green)) { // Not numeric! - ReportError("expected Green color value, but got '" + token + "'"); + ReportError("Expected Green color value, but got '" + token + "'"); return false; } @@ -151,7 +148,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if (!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out light.Color.Blue)) { // Not numeric! - ReportError("expected Blue color value, but got '" + token + "'"); + ReportError("Expected Blue color value, but got '" + token + "'"); return false; } //size @@ -166,7 +163,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out light.PrimaryRadius)) { // Not numeric! - ReportError("expected Size value, but got '" + token + "'"); + ReportError("Expected Size value, but got '" + token + "'"); return false; } light.PrimaryRadius *= 2; @@ -174,7 +171,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - ReportError("'" + token + "' is not valid property for " + lightType + "."); + ReportError("'" + token + "' is not valid property for " + lightType); return false; } //offset @@ -187,7 +184,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!ReadSignedFloat(token, ref light.Offset.X)) { // Not numeric! - ReportError("expected Offset X value, but got '" + token + "'"); + ReportError("Expected Offset X value, but got '" + token + "'"); return false; } @@ -197,7 +194,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!ReadSignedFloat(token, ref light.Offset.Z)) { // Not numeric! - ReportError("expected Offset Y value, but got '" + token + "'"); + ReportError("Expected Offset Y value, but got '" + token + "'"); return false; } @@ -207,7 +204,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!ReadSignedFloat(token, ref light.Offset.Y)) { // Not numeric! - ReportError("expected Offset Z value, but got '" + token + "'"); + ReportError("Expected Offset Z value, but got '" + token + "'"); return false; } //subtractive @@ -237,7 +234,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out i)) { // Not numeric! - ReportError("expected Dontlightself value, but got '" + token + "'"); + ReportError("Expected DontLightSelf value, but got '" + token + "'"); return false; } @@ -255,7 +252,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out interval)) { // Not numeric! - ReportError("expected Interval value, but got '" + token + "'"); + ReportError("Expected Interval value, but got '" + token + "'"); return false; } @@ -288,7 +285,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out light.SecondaryRadius)) { // Not numeric! - ReportError("expected SecondarySize value, but got '" + token + "'"); + ReportError("Expected SecondarySize value, but got '" + token + "'"); return false; } light.SecondaryRadius *= 2; @@ -311,7 +308,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out chance)) { // Not numeric! - ReportError("expected Chance value, but got '" + token + "'"); + ReportError("Expected Chance value, but got '" + token + "'"); return false; } @@ -336,13 +333,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!float.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out scale)) { // Not numeric! - ReportError("expected Scale value, but got '" + token + "'"); + ReportError("Expected Scale value, but got '" + token + "'"); return false; } if(scale > 1.0f) { - ReportError("scale must be in 0.0 - 1.0 range, but is " + scale); + ReportError("Scale must be in 0.0 - 1.0 range, but is " + scale); return false; } @@ -623,37 +620,59 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else if(token == "#include") { + //INFO: ZDoom GLDEFS include paths can't be relative ("../glstuff.txt") + //or absolute ("d:/project/glstuff.txt") + //or have backward slases ("info\glstuff.txt") + //include paths are relative to the first parsed entry, not the current one + //also include paths may or may not be quoted SkipWhitespace(true); - string includelump = ReadToken(false); // Don't skip newline + string includelump = StripTokenQuotes(ReadToken(false)); // Don't skip newline // Sanity checks - if(!includelump.StartsWith("\"") || !includelump.EndsWith("\"")) - { - 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) + // Absolute paths are not supported... + if(Path.IsPathRooted(includelump)) { - ReportError("already parsed '" + includelump + "'. Check your #include directives"); + ReportError("Absolute include paths are not supported by ZDoom"); return false; } + // Relative paths are not supported + if(includelump.StartsWith(RELATIVE_PATH_MARKER) || includelump.StartsWith(CURRENT_FOLDER_PATH_MARKER) || + includelump.StartsWith(ALT_RELATIVE_PATH_MARKER) || includelump.StartsWith(ALT_CURRENT_FOLDER_PATH_MARKER)) + { + ReportError("Relative include paths are not supported by ZDoom"); + return false; + } + + // Backward slases are not supported + if(includelump.Contains(Path.DirectorySeparatorChar.ToString())) + { + ReportError("Only forward slases are supported by ZDoom"); + return false; + } + + // Already parsed? + if(parsedlumps.Contains(includelump)) + { + ReportError("Already parsed '" + includelump + "'. Check your #include directives"); + return false; + } + + // Add to collection + parsedlumps.Add(includelump); + // Callback to parse this file if(OnInclude != null) OnInclude(this, includelump, clearerrors); + // Bail out on error + if(this.HasError) return false; + // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; diff --git a/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs b/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs index 3988f869..d7318589 100644 --- a/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs +++ b/Source/Core/GZBuilder/GZDoom/MapinfoParser.cs @@ -51,7 +51,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom mapinfo = new MapInfo(); spawnnums = new Dictionary(); doomednums = new Dictionary(); - parsedlumps = new HashSet(); + parsedlumps = new HashSet(StringComparer.OrdinalIgnoreCase); } #endregion @@ -68,7 +68,6 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom public bool Parse(Stream stream, string sourcefilename, string mapname, bool clearerrors) { this.mapname = mapname.ToLowerInvariant(); - parsedlumps.Add(sourcefilename); if(!base.Parse(stream, sourcefilename, clearerrors)) return false; while(SkipWhitespace(true)) @@ -166,7 +165,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!ReadSignedFloat(token, ref scrollSpeed)) { // Not numeric! - ReportError("expected " + skyType + " scroll speed value, but got '" + token + "'"); + ReportError("Expected " + skyType + " scroll speed value, but got '" + token + "'"); return false; } @@ -182,7 +181,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - ReportError("expected " + skyType + " texture name"); + ReportError("Expected " + skyType + " texture name"); return false; } } @@ -217,7 +216,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - ReportError("expected " + skyType + " texture name"); + ReportError("Expected " + skyType + " texture name"); return false; } } @@ -251,13 +250,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else //...or not { - ReportError("failed to parse " + fadeType + " value from string '" + colorVal + "'"); + ReportError("Failed to parse " + fadeType + " value from string '" + colorVal + "'"); return false; } } else { - ReportError("expected " + fadeType + " color value"); + ReportError("Expected " + fadeType + " color value"); return false; } } @@ -279,7 +278,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!ReadSignedInt(token, ref val)) { // Not numeric! - ReportError("expected " + shadeType + " value, but got '" + token + "'"); + ReportError("Expected " + shadeType + " value, but got '" + token + "'"); return false; } @@ -306,7 +305,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) { // Not numeric! - ReportError("expected " + densityType + " value, but got '" + token + "'"); + ReportError("Expected " + densityType + " value, but got '" + token + "'"); return false; } @@ -352,17 +351,56 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } } } - } - else if(token == "include") + } + else if(token == "include") // It's "include", not "#include". Cause fuck consistency. { SkipWhitespace(true); - string includelump = StripTokenQuotes(ReadToken(false)).ToLowerInvariant(); // Don't skip newline - + string includelump = StripTokenQuotes(ReadToken(false)); // Don't skip newline + + //INFO: ZDoom MAPINFO include paths can't be relative ("../mapstuff.txt") + //or absolute ("d:/project/mapstuff.txt") + //or have backward slases ("info\mapstuff.txt") + //include paths are relative to the first parsed entry, not the current one + //also include paths may or may not be quoted if(!string.IsNullOrEmpty(includelump)) { + // Absolute paths are not supported... + if(Path.IsPathRooted(includelump)) + { + ReportError("Absolute include paths are not supported by ZDoom"); + return false; + } + + // Relative paths are not supported + if(includelump.StartsWith(RELATIVE_PATH_MARKER) || includelump.StartsWith(CURRENT_FOLDER_PATH_MARKER) || + includelump.StartsWith(ALT_RELATIVE_PATH_MARKER) || includelump.StartsWith(ALT_CURRENT_FOLDER_PATH_MARKER)) + { + ReportError("Relative include paths are not supported by ZDoom"); + return false; + } + + // Backward slases are not supported + if(includelump.Contains(Path.DirectorySeparatorChar.ToString())) + { + ReportError("Only forward slases are supported by ZDoom"); + return false; + } + + // Already parsed? + if(parsedlumps.Contains(includelump)) + { + ReportError("Already parsed '" + includelump + "'. Check your include directives"); + return false; + } + + // Add to collection + parsedlumps.Add(includelump); + // Callback to parse this file - if(OnInclude != null) - OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), clearerrors); + if(OnInclude != null) OnInclude(this, includelump, clearerrors); + + // Bail out on error + if(this.HasError) return false; // Set our buffers back to continue parsing datastream = localstream; @@ -371,7 +409,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } else { - ReportError("got include directive with missing or incorrect path: '" + includelump + "'"); + ReportError("Expected filename to include"); return false; } } @@ -384,7 +422,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = ReadToken(); if(string.IsNullOrEmpty(token)) { - ReportError("failed to find the end of GameInfo block"); + ReportError("Failed to find the end of GameInfo block"); return false; // Finished with this file } if(token == "}") break; @@ -396,7 +434,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom string skyflatname = StripTokenQuotes(ReadToken()); if(string.IsNullOrEmpty(skyflatname)) { - ReportError("unable to get SkyFlatName value"); + ReportError("Unable to get SkyFlatName value"); return false; // Finished with this file } @@ -413,7 +451,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = ReadToken(); if(string.IsNullOrEmpty(token)) { - ReportError("failed to find the end of DoomEdNums block"); + ReportError("Failed to find the end of DoomEdNums block"); return false; // Finished with this file } if(token == "}") break; @@ -423,7 +461,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out id)) { // Not numeric! - ReportError("expected DoomEdNums entry number, but got '" + token + "'"); + ReportError("Expected DoomEdNums entry number, but got '" + token + "'"); return false; // Finished with this file } @@ -435,7 +473,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom string classname = StripTokenQuotes(ReadToken()); if(string.IsNullOrEmpty(classname)) { - ReportError("unable to get DoomEdNums entry class definition"); + ReportError("Unable to get DoomEdNums entry class definition"); return false; // Finished with this file } @@ -461,7 +499,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = ReadToken(); if(string.IsNullOrEmpty(token)) { - ReportError("failed to find the end of SpawnNums block"); + ReportError("Failed to find the end of SpawnNums block"); return false; // Finished with this file } if(token == "}") break; @@ -471,7 +509,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out id)) { // Not numeric! - ReportError("expected SpawnNums number, but got '" + token + "'"); + ReportError("Expected SpawnNums number, but got '" + token + "'"); return false; // Finished with this file } @@ -483,7 +521,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = StripTokenQuotes(ReadToken()); if(string.IsNullOrEmpty(token)) { - ReportError("unable to get SpawnNums entry class definition"); + ReportError("Unable to get SpawnNums entry class definition"); return false; } diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs index 1cddc3e5..8417f416 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefParser.cs @@ -6,14 +6,18 @@ using CodeImp.DoomBuilder.GZBuilder.Data; namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { - internal class ModeldefParser : ZDTextParser + internal class ModeldefParser : ZDTextParser { + private readonly Dictionary actorsbyclass; + internal Dictionary ActorsByClass { get { return actorsbyclass; } } + private Dictionary entries; //classname, entry internal Dictionary Entries { get { return entries; } } - internal ModeldefParser() + internal ModeldefParser(Dictionary actorsbyclass) { - entries = new Dictionary(StringComparer.Ordinal); + this.actorsbyclass = actorsbyclass; + this.entries = new Dictionary(StringComparer.Ordinal); } //should be called after all decorate actors are parsed @@ -60,22 +64,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom } // Skip untill current structure end - if(!mds.ParsingFinished) - { - while(SkipWhitespace(true)) - { - token = ReadToken(); - if(string.IsNullOrEmpty(token) || token == "}") break; - } - } + if(!mds.ParsingFinished) SkipStructure(1); } } else { // Unknown structure! - string token2; - if(token != "{") + if(token != "{") { + string token2; do { if(!SkipWhitespace(true)) break; @@ -85,16 +82,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom 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); + SkipStructure(1); } } } @@ -102,6 +90,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom return entries.Count > 0; } + // Skips untill current structure end + private void SkipStructure(int scopelevel) + { + do + { + if(!SkipWhitespace(true)) break; + string token = ReadToken(); + if(string.IsNullOrEmpty(token)) break; + if(token == "{") scopelevel++; + if(token == "}") scopelevel--; + } + while(scopelevel > 0); + } + protected override string GetLanguageType() { return "MODELDEF"; diff --git a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs index a62e4ef4..63607103 100644 --- a/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs +++ b/Source/Core/GZBuilder/GZDoom/ModeldefStructure.cs @@ -60,7 +60,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom path = parser.StripTokenQuotes(parser.ReadToken(false)).Replace("/", "\\"); // Don't skip newline if(string.IsNullOrEmpty(path)) { - parser.ReportError("expected model path, but got '" + token + "'"); + parser.ReportError("Expected model path, but got '" + token + "'"); return false; } break; @@ -78,7 +78,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out index)) { // Not numeric! - parser.ReportError("expected model index, but got '" + token + "'"); + parser.ReportError("Expected model index, but got '" + token + "'"); return false; } @@ -94,7 +94,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline if(string.IsNullOrEmpty(token)) { - parser.ReportError("model name required"); + parser.ReportError("Expected model name"); return false; } @@ -102,13 +102,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom 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"); + 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"); + parser.ReportError("Model '" + token + "' won't be loaded. Only MD2 and MD3 models are supported"); return false; } @@ -129,7 +129,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out skinIndex)) { // Not numeric! - parser.ReportError("expected skin index, but got '" + token + "'"); + parser.ReportError("Expected skin index, but got '" + token + "'"); return false; } @@ -145,7 +145,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom token = parser.StripTokenQuotes(parser.ReadToken(false)).ToLowerInvariant(); // Don't skip newline if(string.IsNullOrEmpty(token)) { - parser.ReportError("skin path required"); + parser.ReportError("Skin path required"); return false; } @@ -153,7 +153,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom string ext = Path.GetExtension(token); if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) { - parser.ReportError("image format '" + ext + "' is not supported!"); + parser.ReportError("Image format '" + ext + "' is not supported"); return false; } @@ -171,7 +171,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref scale.Y)) { // Not numeric! - parser.ReportError("expected Scale X value, but got '" + token + "'"); + parser.ReportError("Expected Scale X value, but got '" + token + "'"); return false; } @@ -180,7 +180,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref scale.X)) { // Not numeric! - parser.ReportError("expected Scale Y value, but got '" + token + "'"); + parser.ReportError("Expected Scale Y value, but got '" + token + "'"); return false; } @@ -189,7 +189,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref scale.Z)) { // Not numeric! - parser.ReportError("expected Scale Z value, but got '" + token + "'"); + parser.ReportError("Expected Scale Z value, but got '" + token + "'"); return false; } break; @@ -204,7 +204,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.X)) { // Not numeric! - parser.ReportError("expected Offset X value, but got '" + token + "'"); + parser.ReportError("Expected Offset X value, but got '" + token + "'"); return false; } @@ -213,7 +213,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.Y)) { // Not numeric! - parser.ReportError("expected Offset Y value, but got '" + token + "'"); + parser.ReportError("Expected Offset Y value, but got '" + token + "'"); return false; } @@ -222,7 +222,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.Z)) { // Not numeric! - parser.ReportError("expected Offset Z value, but got '" + token + "'"); + parser.ReportError("Expected Offset Z value, but got '" + token + "'"); return false; } break; @@ -237,7 +237,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref offset.Z)) { // Not numeric! - parser.ReportError("expected ZOffset value, but got '" + token + "'"); + parser.ReportError("Expected ZOffset value, but got '" + token + "'"); return false; } break; @@ -252,7 +252,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref angleOffset)) { // Not numeric! - parser.ReportError("expected AngleOffset value, but got '" + token + "'"); + parser.ReportError("Expected AngleOffset value, but got '" + token + "'"); return false; } break; @@ -267,7 +267,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref pitchOffset)) { // Not numeric! - parser.ReportError("expected PitchOffset value, but got '" + token + "'"); + parser.ReportError("Expected PitchOffset value, but got '" + token + "'"); return false; } break; @@ -282,7 +282,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedFloat(token, ref rollOffset)) { // Not numeric! - parser.ReportError("expected RollOffset value, but got '" + token + "'"); + parser.ReportError("Expected RollOffset value, but got '" + token + "'"); return false; } break; @@ -376,7 +376,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out modelIndex)) { // Not numeric! - parser.ReportError("expected model index, but got '" + token + "'"); + parser.ReportError("Expected model index, but got '" + token + "'"); return false; } @@ -388,7 +388,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(modelNames[modelIndex] == null) { - parser.ReportError("got model index, which doesn't correspond to any defined model"); + parser.ReportError("Model index doesn't correspond to any defined model"); return false; } @@ -404,7 +404,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(!parser.ReadSignedInt(token, ref frame)) { // Not numeric! - parser.ReportError("expected model frame index, but got '" + token + "'"); + parser.ReportError("Expected model frame index, but got '" + token + "'"); return false; } @@ -417,7 +417,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom if(string.IsNullOrEmpty(token)) { // Missing! - parser.ReportError("expected model frame name"); + parser.ReportError("Expected model frame name"); return false; } @@ -451,7 +451,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom // Bail out when got errors or no models are used if(Array.IndexOf(modelsUsed, true) == -1) { - parser.ReportError("no models are used by '" + classname + "'"); + parser.ReportError("No models are used by '" + classname + "'"); return false; } diff --git a/Source/Core/ZDoom/AnimdefsParser.cs b/Source/Core/ZDoom/AnimdefsParser.cs index 944e7094..335c7fef 100644 --- a/Source/Core/ZDoom/AnimdefsParser.cs +++ b/Source/Core/ZDoom/AnimdefsParser.cs @@ -59,14 +59,14 @@ namespace CodeImp.DoomBuilder.ZDoom string texturename = StripTokenQuotes(ReadToken(false)); if(string.IsNullOrEmpty(texturename)) { - ReportError("expected camera texture name"); + ReportError("Expected camera texture name"); return false; } // Camera texture names are limited to 8 chars if(texturename.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH) { - ReportError("camera texture names must be no longer than " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " chars"); + ReportError("Camera texture names must be no longer than " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " chars"); return false; } @@ -75,7 +75,7 @@ namespace CodeImp.DoomBuilder.ZDoom SkipWhitespace(true); if(!ReadSignedInt(ref width) || width < 1) { - ReportError("expected camera texture width"); + ReportError("Expected camera texture width"); return false; } @@ -84,7 +84,7 @@ namespace CodeImp.DoomBuilder.ZDoom SkipWhitespace(true); if(!ReadSignedInt(ref height) || height < 1) { - ReportError("expected camera texture height"); + ReportError("Expected camera texture height"); return false; } @@ -104,7 +104,7 @@ namespace CodeImp.DoomBuilder.ZDoom SkipWhitespace(true); if(!ReadSignedInt(ref fitwidth) || fitwidth < 1) { - ReportError("expected camera texture fit width"); + ReportError("Expected camera texture fit width"); return false; } @@ -112,7 +112,7 @@ namespace CodeImp.DoomBuilder.ZDoom SkipWhitespace(true); if(!ReadSignedInt(ref fitheight) || fitheight < 1) { - ReportError("expected camera texture fit height"); + ReportError("Expected camera texture fit height"); return false; } diff --git a/Source/Core/ZDoom/DecorateParser.cs b/Source/Core/ZDoom/DecorateParser.cs index d5ad82a8..fd098d32 100644 --- a/Source/Core/ZDoom/DecorateParser.cs +++ b/Source/Core/ZDoom/DecorateParser.cs @@ -45,6 +45,9 @@ namespace CodeImp.DoomBuilder.ZDoom // These are all parsed actors, also those from other games private Dictionary archivedactors; + + //mxd. Includes tracking + private readonly HashSet parsedlumps; #endregion @@ -84,6 +87,7 @@ namespace CodeImp.DoomBuilder.ZDoom // Initialize actors = new Dictionary(StringComparer.Ordinal); archivedactors = new Dictionary(StringComparer.Ordinal); + parsedlumps = new HashSet(StringComparer.OrdinalIgnoreCase); //mxd } // Disposer @@ -148,35 +152,63 @@ namespace CodeImp.DoomBuilder.ZDoom case "#include": { - // Include a file + //INFO: ZDoom DECORATE include paths can't be relative ("../actor.txt") + //or absolute ("d:/project/actor.txt") + //or have backward slases ("info\actor.txt") + //include paths are relative to the first parsed entry, not the current one + //also include paths may or may not be quoted SkipWhitespace(true); - string filename = ReadToken(false); //mxd. Don't skip newline + string filename = StripTokenQuotes(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; } + //mxd. Absolute paths are not supported... + if(Path.IsPathRooted(filename)) + { + ReportError("Absolute include paths are not supported by ZDoom"); + return false; + } + + //mxd. Relative paths are not supported + if(filename.StartsWith(RELATIVE_PATH_MARKER) || filename.StartsWith(CURRENT_FOLDER_PATH_MARKER) || + filename.StartsWith(ALT_RELATIVE_PATH_MARKER) || filename.StartsWith(ALT_CURRENT_FOLDER_PATH_MARKER)) + { + ReportError("Relative include paths are not supported by ZDoom"); + return false; + } + + //mxd. Backward slases are not supported + if(filename.Contains(Path.DirectorySeparatorChar.ToString())) + { + ReportError("Only forward slases are supported by ZDoom"); + return false; + } + + //mxd. Already parsed? + if(parsedlumps.Contains(filename)) + { + ReportError("Already parsed '" + filename + "'. Check your include directives"); + return false; + } + + //mxd. Add to collection + parsedlumps.Add(filename); + // Callback to parse this file now if(OnInclude != null) OnInclude(this, filename); + //mxd. Bail out on error + if(this.HasError) return false; + // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; - if(this.HasError) return false; } break; diff --git a/Source/Core/ZDoom/PatchStructure.cs b/Source/Core/ZDoom/PatchStructure.cs index c036d285..e7e13e3b 100644 --- a/Source/Core/ZDoom/PatchStructure.cs +++ b/Source/Core/ZDoom/PatchStructure.cs @@ -71,8 +71,6 @@ namespace CodeImp.DoomBuilder.ZDoom // Constructor internal PatchStructure(TexturesParser parser) { - string tokenstr; - // Initialize alpha = 1.0f; renderStyle = TexturePathRenderStyle.Copy;//mxd @@ -94,17 +92,11 @@ namespace CodeImp.DoomBuilder.ZDoom name = name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); // Now we should find a comma - parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); - if(tokenstr != ",") - { - parser.ReportError("Expected a comma"); - return; - } + if(!parser.NextTokenIs(",")) return; //mxd // Next is the patch width parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); + string tokenstr = parser.ReadToken(); if(string.IsNullOrEmpty(tokenstr) || !int.TryParse(tokenstr, NumberStyles.Integer, CultureInfo.InvariantCulture, out offsetx)) { parser.ReportError("Expected offset in pixels"); @@ -112,13 +104,7 @@ namespace CodeImp.DoomBuilder.ZDoom } // Now we should find a comma again - parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); - if(tokenstr != ",") - { - parser.ReportError("Expected a comma"); - return; - } + if(!parser.NextTokenIs(",")) return; //mxd // Next is the patch height parser.SkipWhitespace(true); @@ -129,16 +115,8 @@ namespace CodeImp.DoomBuilder.ZDoom return; } - // Next token is the beginning of the texture scope. - // If not, then the patch info ends here. - parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); - if(tokenstr != "{") - { - // Rewind so this structure can be read again - parser.DataStream.Seek(-tokenstr.Length - 1, SeekOrigin.Current); - return; - } + // Next token is the beginning of the texture scope. If not, then the patch info ends here. + if(!parser.NextTokenIs("{", false)) return; //mxd // Now parse the contents of texture structure bool done = false; //mxd @@ -169,7 +147,7 @@ namespace CodeImp.DoomBuilder.ZDoom if(rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) { - parser.LogWarning("Got unsupported rotation (" + rotation + ") in patch '" + name + "'"); + parser.LogWarning("Unsupported rotation (" + rotation + ") in patch '" + name + "'"); rotation = 0; } break; diff --git a/Source/Core/ZDoom/ReverbsParser.cs b/Source/Core/ZDoom/ReverbsParser.cs index c2f72af1..4fed5a66 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("Expected 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("Expected first part of '" + name + "' sound environment ID, but got '" + token + "'"); 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("Expected second part of '" + name + "' sound environment ID, but got '" + token + "'"); break; } diff --git a/Source/Core/ZDoom/TextureStructure.cs b/Source/Core/ZDoom/TextureStructure.cs index 63ffb76b..52df44df 100644 --- a/Source/Core/ZDoom/TextureStructure.cs +++ b/Source/Core/ZDoom/TextureStructure.cs @@ -102,17 +102,11 @@ namespace CodeImp.DoomBuilder.ZDoom } // Now we should find a comma - parser.SkipWhitespace(true); - string tokenstr = parser.ReadToken(); - if(tokenstr != ",") - { - parser.ReportError("Expected a comma"); - return; - } + if(!parser.NextTokenIs(",")) return; //mxd // Next is the texture width parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); + string tokenstr = parser.ReadToken(); if(string.IsNullOrEmpty(tokenstr) || !int.TryParse(tokenstr, NumberStyles.Integer, CultureInfo.InvariantCulture, out width)) { parser.ReportError("Expected width in pixels"); @@ -120,13 +114,7 @@ namespace CodeImp.DoomBuilder.ZDoom } // Now we should find a comma again - parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); - if(tokenstr != ",") - { - parser.ReportError("Expected a comma"); - return; - } + if(!parser.NextTokenIs(",")) return; //mxd // Next is the texture height parser.SkipWhitespace(true); @@ -138,9 +126,7 @@ namespace CodeImp.DoomBuilder.ZDoom } // Next token should be the beginning of the texture scope - parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); - if(tokenstr != "{") + if(!parser.NextTokenIs("{", false)) //mxd { parser.ReportError("Expected begin of structure"); return; @@ -172,13 +158,7 @@ namespace CodeImp.DoomBuilder.ZDoom if(!ReadTokenInt(parser, token, out xoffset)) return; // Now we should find a comma - parser.SkipWhitespace(true); - tokenstr = parser.ReadToken(); - if(tokenstr != ",") - { - parser.ReportError("Expected a comma"); - return; - } + if(!parser.NextTokenIs(",")) return; //mxd // Read y offset if(!ReadTokenInt(parser, token, out yoffset)) return; @@ -223,19 +203,15 @@ namespace CodeImp.DoomBuilder.ZDoom parser.ReportError("Expected numeric value for property '" + propertyname + "'"); return false; } - else - { - // Success - return true; - } - } - else - { - // Can't find the property value! - parser.ReportError("Expected a value for property '" + propertyname + "'"); - value = 0.0f; - return false; + + // Success + return true; } + + // Can't find the property value! + parser.ReportError("Expected a value for property '" + propertyname + "'"); + value = 0.0f; + return false; } // This reads the next token and sets an integral value, returns false when failed @@ -252,41 +228,29 @@ namespace CodeImp.DoomBuilder.ZDoom parser.ReportError("Expected integral value for property '" + propertyname + "'"); return false; } - else - { - // Success - return true; - } - } - else - { - // Can't find the property value! - parser.ReportError("Expected a value for property '" + propertyname + "'"); - value = 0; - return false; + + // Success + return true; } + + // Can't find the property value! + parser.ReportError("Expected a value for property '" + propertyname + "'"); + value = 0; + return false; } // This makes a HighResImage texture for this texture internal HighResImage MakeImage() { - float scalex, scaley; - - // Determine default scale - float defaultscale = General.Map.Config.DefaultTextureScale; - // Determine scale for texture - if(xscale == 0.0f) scalex = defaultscale; else scalex = 1f / xscale; - if(yscale == 0.0f) scaley = defaultscale; else scaley = 1f / yscale; + float scalex = ((xscale == 0.0f) ? General.Map.Config.DefaultTextureScale : 1f / xscale); + float scaley = ((yscale == 0.0f) ? General.Map.Config.DefaultTextureScale : 1f / yscale); // Make texture HighResImage tex = new HighResImage(name, virtualpath, width, height, scalex, scaley, worldpanning, typename == "flat"); // Add patches - foreach(PatchStructure p in patches) - { - tex.AddPatch(new TexturePatch(p));//mxd - } + foreach(PatchStructure p in patches) tex.AddPatch(new TexturePatch(p));//mxd return tex; } diff --git a/Source/Core/ZDoom/TexturesParser.cs b/Source/Core/ZDoom/TexturesParser.cs index 5574fde7..95cf4ff0 100644 --- a/Source/Core/ZDoom/TexturesParser.cs +++ b/Source/Core/ZDoom/TexturesParser.cs @@ -107,7 +107,7 @@ namespace CodeImp.DoomBuilder.ZDoom //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."); + ReportError("Can't load an unnamed texture. Please consider giving names to your resources"); return false; } @@ -133,7 +133,7 @@ namespace CodeImp.DoomBuilder.ZDoom //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."); + ReportError("Can't load an unnamed sprite. Please consider giving names to your resources"); return false; } @@ -158,7 +158,7 @@ namespace CodeImp.DoomBuilder.ZDoom //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."); + ReportError("Can't load an unnamed WallTexture. Please consider giving names to your resources"); return false; } @@ -184,7 +184,7 @@ namespace CodeImp.DoomBuilder.ZDoom //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."); + ReportError("Can't load an unnamed flat. Please consider giving names to your resources"); return false; } diff --git a/Source/Core/ZDoom/VoxeldefParser.cs b/Source/Core/ZDoom/VoxeldefParser.cs index 99efd89f..cd2eca2a 100644 --- a/Source/Core/ZDoom/VoxeldefParser.cs +++ b/Source/Core/ZDoom/VoxeldefParser.cs @@ -38,7 +38,6 @@ namespace CodeImp.DoomBuilder.ZDoom { if(!string.IsNullOrEmpty(prevToken) && !spriteNames.Contains(prevToken)) spriteNames.Add(prevToken); prevToken = token.ToUpperInvariant(); - } else if(token == "=") //next token should be a voxel model name { @@ -78,14 +77,8 @@ namespace CodeImp.DoomBuilder.ZDoom foreach(string s in spriteNames) { - if(entries.ContainsKey(s)) //TODO: is this a proper behaviour? - { - entries[s] = mde; - } - else - { - entries.Add(s, mde); - } + //TODO: is this the proper behaviour? + entries[s] = mde; } //reset local data @@ -96,45 +89,31 @@ namespace CodeImp.DoomBuilder.ZDoom break; } - else if(token == "overridepalette") + else if(token == "overridepalette") { mde.OverridePalette = true; } - else if(token == "angleoffset") + else if(token == "angleoffset") { - SkipWhitespace(true); + if(!NextTokenIs("=")) return false; token = StripTokenQuotes(ReadToken()); - if(token != "=") - { - ReportError("expected '=', but got '" + token + "'"); - return false; - } - - token = StripTokenQuotes(ReadToken()); - if(!ReadSignedFloat(token, ref angleoffset)) + if(!ReadSignedFloat(token, ref angleoffset)) { // Not numeric! - ReportError("expected AngleOffset value, but got '" + token + "'"); + ReportError("Expected AngleOffset value, but got '" + token + "'"); return false; } } else if(token == "scale") { - SkipWhitespace(true); - - token = StripTokenQuotes(ReadToken()); - if(token != "=") - { - ReportError("expected '=', but got '" + token + "'"); - return false; - } + if(!NextTokenIs("=")) return false; token = StripTokenQuotes(ReadToken()); if(!ReadSignedFloat(token, ref scale)) { // Not numeric! - ReportError("expected Scale value, but got '" + token + "'"); + ReportError("Expected Scale value, but got '" + token + "'"); return false; } } diff --git a/Source/Core/ZDoom/ZDTextParser.cs b/Source/Core/ZDoom/ZDTextParser.cs index 62f18438..1257a7a2 100644 --- a/Source/Core/ZDoom/ZDTextParser.cs +++ b/Source/Core/ZDoom/ZDTextParser.cs @@ -21,6 +21,7 @@ using System.Globalization; using System.Text; using System.IO; using CodeImp.DoomBuilder.Compilers; +using CodeImp.DoomBuilder.Data; #endregion @@ -29,6 +30,11 @@ namespace CodeImp.DoomBuilder.ZDoom public abstract class ZDTextParser { #region ================== Constants + + protected static readonly string RELATIVE_PATH_MARKER = ".." + Path.DirectorySeparatorChar; + protected static readonly string CURRENT_FOLDER_PATH_MARKER = "." + Path.DirectorySeparatorChar; + protected static readonly string ALT_RELATIVE_PATH_MARKER = ".." + Path.AltDirectorySeparatorChar; + protected static readonly string ALT_CURRENT_FOLDER_PATH_MARKER = "." + Path.AltDirectorySeparatorChar; #endregion @@ -84,7 +90,7 @@ namespace CodeImp.DoomBuilder.ZDoom //mxd. Integrity check if(stream == null || stream.Length == 0) { - ReportError("Unable to load '" + sourcefilename + "'!"); + ReportError("Unable to load '" + sourcefilename + "'"); return false; } @@ -408,20 +414,20 @@ namespace CodeImp.DoomBuilder.ZDoom } //mxd - protected bool NextTokenIs(string expectedtoken) + internal bool NextTokenIs(string expectedtoken) { return NextTokenIs(expectedtoken, true); } //mxd - protected bool NextTokenIs(string expectedtoken, bool reporterror) + internal bool NextTokenIs(string expectedtoken, bool reporterror) { if(!SkipWhitespace(true)) return false; string token = ReadToken(); if(string.Compare(token, expectedtoken, true) != 0) { - if(reporterror) ReportError("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); @@ -525,6 +531,69 @@ namespace CodeImp.DoomBuilder.ZDoom return Math.Max(linenumber, 0); } + //mxd. This converts given path to be relative to "filename" + protected string GetRootedPath(string filename, string includefilename) + { + // Construct root-relative path... + filename = filename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + // Filename absolute? Try to convert it to resource-rooted + if(Path.IsPathRooted(filename)) + { + foreach(DataReader reader in General.Map.Data.Containers) + { + if(reader is DirectoryReader && filename.StartsWith(reader.Location.location, true, CultureInfo.InvariantCulture)) + { + filename = filename.Substring(reader.Location.location.Length + 1, filename.Length - reader.Location.location.Length - 1); + break; + } + } + } + + string filepath = Path.GetDirectoryName(filename); + string result = includefilename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + if(result.StartsWith(RELATIVE_PATH_MARKER) && !string.IsNullOrEmpty(filepath)) + { + string[] parts = filepath.Split(Path.DirectorySeparatorChar); + int index = parts.Length - 1; + int pos; + + // Count & trim relative path markers + while((pos = result.LastIndexOf(RELATIVE_PATH_MARKER, StringComparison.Ordinal)) != -1) + { + // includefilename references something above the root? + if(index-- < 0) + { + ReportError("Unable to construct rooted path from '" + includefilename + "'"); + return string.Empty; + } + + string start = result.Substring(0, pos); + string end = result.Substring(pos + RELATIVE_PATH_MARKER.Length, result.Length - pos - RELATIVE_PATH_MARKER.Length); + result = start + end; + } + + // Construct absolute path relative to current filename + while(index > -1) + { + result = parts[index--] + Path.DirectorySeparatorChar + result; + } + } + else + { + // Trim "this folder" marker + if(result.StartsWith(CURRENT_FOLDER_PATH_MARKER)) + result = result.TrimStart(CURRENT_FOLDER_PATH_MARKER.ToCharArray()); + + // Treat as relative path + if(!string.IsNullOrEmpty(filepath)) + result = Path.Combine(filepath, result); + } + + return result; + } + //mxd. Language type protected abstract string GetLanguageType();