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.
This commit is contained in:
MaxED 2015-12-21 14:17:47 +00:00
parent 3621ff78c1
commit f3fbf241d9
16 changed files with 417 additions and 319 deletions

View file

@ -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)
{

View file

@ -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<string> files;
private readonly HashSet<string> 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<string> Files { get { return files; } }
public HashSet<string> 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<string>();
this.files = new HashSet<string>(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);
}
}
}

View file

@ -366,7 +366,7 @@ namespace CodeImp.DoomBuilder.Data
LoadReverbs();
LoadSndSeq();
LoadVoxels();
Dictionary<string, List<int>> actorsbyclass = CreateActorsByClassList();
Dictionary<string, int> 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 <Actor Class, Thing.Type> dictionary. Should be called after all DECORATE actors are parsed
private Dictionary<string, List<int>> CreateActorsByClassList()
private Dictionary<string, int> CreateActorsByClassList()
{
Dictionary<string, List<int>> actors = new Dictionary<string, List<int>>(StringComparer.Ordinal);
Dictionary<string, int> actors = new Dictionary<string, int>(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<int>());
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<string, List<int>> actorsByClass)
private void LoadModeldefs(Dictionary<string, int> actorsbyclass)
{
//if no actors defined in DECORATE or game config...
if(actorsByClass.Count == 0) return;
if(actorsbyclass.Count == 0) return;
Dictionary<string, ModelData> modelDefEntriesByName = new Dictionary<string, ModelData>(StringComparer.Ordinal);
ModeldefParser parser = new ModeldefParser();
Dictionary<string, ModelData> modeldefentriesbyname = new Dictionary<string, ModelData>(StringComparer.Ordinal);
ModeldefParser parser = new ModeldefParser(actorsbyclass);
foreach(DataReader dr in containers)
{
@ -1819,15 +1821,10 @@ namespace CodeImp.DoomBuilder.Data
{
foreach(KeyValuePair<string, ModelData> 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<string, ModelData> e in modelDefEntriesByName)
foreach(KeyValuePair<string, ModelData> 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<string, List<int>> actorsByClass)
//mxd. This parses gldefs. Should be called after all DECORATE actors are parsed
private void LoadGldefs(Dictionary<string, int> 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<string, string> e in parser.Objects) //ClassName, Light name
//create Gldefs Entries dictionary
foreach(KeyValuePair<string, string> e in parser.Objects) //<ClassName, Light name>
{
//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;
}

View file

@ -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<string> parsedlumps;
private readonly HashSet<string> includes;
private List<string> includestoskip;
private HashSet<string> includestoskip;
private string libraryname;
private readonly List<ScriptItem> namedscripts;
@ -36,15 +37,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
namedscripts = new List<ScriptItem>();
numberedscripts = new List<ScriptItem>();
functions = new List<ScriptItem>();
parsedlumps = new HashSet<string>();
includes = new HashSet<string>();
includestoskip = new List<string>();
parsedlumps = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
includes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
includestoskip = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
specialtokens += "(,)";
}
public override bool Parse(Stream stream, string sourcefilename, bool clearerrors)
{
return Parse(stream, sourcefilename, new List<string>(), false, false, clearerrors);
return Parse(stream, sourcefilename, new HashSet<string>(), 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<string> configincludes, bool processincludes, bool isinclude, bool clearerrors)
public bool Parse(Stream stream, string sourcefilename, HashSet<string> 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;
}
}

View file

@ -49,7 +49,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
private readonly Dictionary<string, DynamicLightData> lightsByName; //LightName, light definition
private readonly Dictionary<string, string> objects; //ClassName, LightName
private readonly Dictionary<long, GlowingFlatData> glowingflats;
private readonly List<string> parsedlumps;
private readonly HashSet<string> parsedlumps;
#endregion
@ -69,7 +69,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
whitespace = "\n \t\r\u00A0";
specialtokens = ",{}\n";
parsedlumps = new List<string>();
parsedlumps = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
lightsByName = new Dictionary<string, DynamicLightData>(StringComparer.Ordinal); //LightName, Light params
objects = new Dictionary<string, string>(StringComparer.Ordinal); //ClassName, LightName
glowingflats = new Dictionary<long, GlowingFlatData>(); // 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;

View file

@ -51,7 +51,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
mapinfo = new MapInfo();
spawnnums = new Dictionary<int, string>();
doomednums = new Dictionary<int, string>();
parsedlumps = new HashSet<string>();
parsedlumps = new HashSet<string>(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;
}

View file

@ -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<string, int> actorsbyclass;
internal Dictionary<string, int> ActorsByClass { get { return actorsbyclass; } }
private Dictionary<string, ModelData> entries; //classname, entry
internal Dictionary<string, ModelData> Entries { get { return entries; } }
internal ModeldefParser()
internal ModeldefParser(Dictionary<string, int> actorsbyclass)
{
entries = new Dictionary<string, ModelData>(StringComparer.Ordinal);
this.actorsbyclass = actorsbyclass;
this.entries = new Dictionary<string, ModelData>(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";

View file

@ -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;
}

View file

@ -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;
}

View file

@ -45,6 +45,9 @@ namespace CodeImp.DoomBuilder.ZDoom
// These are all parsed actors, also those from other games
private Dictionary<string, ActorStructure> archivedactors;
//mxd. Includes tracking
private readonly HashSet<string> parsedlumps;
#endregion
@ -84,6 +87,7 @@ namespace CodeImp.DoomBuilder.ZDoom
// Initialize
actors = new Dictionary<string, ActorStructure>(StringComparer.Ordinal);
archivedactors = new Dictionary<string, ActorStructure>(StringComparer.Ordinal);
parsedlumps = new HashSet<string>(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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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();