From 2925986234d24535886dcd18bf9c1b1d45f4496c Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Wed, 6 Apr 2016 14:58:15 +0200 Subject: [PATCH] Added Lua parser for custom things (longform object definition only). Print error messages if custom thing parsing fails. --- Source/Core/Builder.csproj | 1 + Source/Core/Data/DataManager.cs | 39 +++- Source/Core/Data/DataReader.cs | 3 + Source/Core/Data/WADReader.cs | 17 ++ Source/Core/SRB2/LuaObjectParser.cs | 284 ++++++++++++++++++++++++++++ 5 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 Source/Core/SRB2/LuaObjectParser.cs diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index 7dca7de..b132a85 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -186,6 +186,7 @@ + diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs index 8f7217b..654836d 100644 --- a/Source/Core/Data/DataManager.cs +++ b/Source/Core/Data/DataManager.cs @@ -1720,7 +1720,27 @@ namespace CodeImp.DoomBuilder.Data public int LoadCustomObjects() { - SOCObjectParser parser = new SOCObjectParser { OnInclude = ParseFromLocation }; + LuaObjectParser LuaParser = new LuaObjectParser { OnInclude = ParseFromLocation }; + + // Parse lumps + foreach (DataReader dr in containers) + { + currentreader = dr; + Dictionary stream = dr.GetLuaData(); + + foreach (KeyValuePair group in stream) + { + // Parse the data + LuaParser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false); + } + } + + if (LuaParser.HasError) + { + LuaParser.LogError(); + } + + SOCObjectParser SOCParser = new SOCObjectParser { OnInclude = ParseFromLocation }; // Parse lumps foreach (DataReader dr in containers) @@ -1735,16 +1755,25 @@ namespace CodeImp.DoomBuilder.Data foreach (KeyValuePair group in streams) { // Parse the data - parser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false); + SOCParser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false); } } + if (SOCParser.HasError) + { + SOCParser.LogError(); + } + currentreader = null; - if (!parser.HasError && parser.Objects.Count > 0) + IDictionary objects = new Dictionary(); + if (!LuaParser.HasError) objects = objects.Concat(LuaParser.Objects).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + if (!SOCParser.HasError) objects = objects.Concat(SOCParser.Objects).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + if (objects.Count > 0) { ThingCategory cat = new ThingCategory(null, "customthings", "Custom Things"); - foreach (KeyValuePair o in parser.Objects) + foreach (KeyValuePair o in objects) { ThingTypeInfo t = new ThingTypeInfo(cat, o.Value); cat.AddThing(t); @@ -1764,7 +1793,7 @@ namespace CodeImp.DoomBuilder.Data thingcategories.Add(cat); } - return parser.Objects.Count; + return objects.Count; } //mxd diff --git a/Source/Core/Data/DataReader.cs b/Source/Core/Data/DataReader.cs index 7194598..60dc482 100644 --- a/Source/Core/Data/DataReader.cs +++ b/Source/Core/Data/DataReader.cs @@ -170,6 +170,9 @@ namespace CodeImp.DoomBuilder.Data //mxd. When implemented, this returns the SOC_ lumps public virtual Dictionary GetSOCData() { return new Dictionary(); } + //mxd. When implemented, this returns the LUA_ lumps + public virtual Dictionary GetLuaData() { return new Dictionary(); } + //mxd. When implemented, this returns the GLDEFS lump public abstract Dictionary GetGldefsData(GameType gametype); // { return new Dictionary(); } diff --git a/Source/Core/Data/WADReader.cs b/Source/Core/Data/WADReader.cs index 434d01c..85c5f5a 100644 --- a/Source/Core/Data/WADReader.cs +++ b/Source/Core/Data/WADReader.cs @@ -880,6 +880,23 @@ namespace CodeImp.DoomBuilder.Data return streams; } + public override Dictionary GetLuaData() + { + if (issuspended) throw new Exception("Data reader is suspended"); + + Dictionary streams = new Dictionary(StringComparer.Ordinal); + string prefix = "LUA_"; + + foreach (Lump lump in file.Lumps) + { + if (lump.Name.StartsWith(prefix)) + { + streams.Add(lump.Name, lump.Stream); + } + } + return streams; + } + //mxd public override Dictionary GetGldefsData(GameType gameType) { diff --git a/Source/Core/SRB2/LuaObjectParser.cs b/Source/Core/SRB2/LuaObjectParser.cs new file mode 100644 index 0000000..48b4e6d --- /dev/null +++ b/Source/Core/SRB2/LuaObjectParser.cs @@ -0,0 +1,284 @@ +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using CodeImp.DoomBuilder.Compilers; +using CodeImp.DoomBuilder.Data; +using CodeImp.DoomBuilder.ZDoom; +using CodeImp.DoomBuilder.GZBuilder.Data; + +#endregion + +namespace CodeImp.DoomBuilder.SRB2 +{ + internal sealed class LuaObjectParser : ZDTextParser + { + #region ================== Delegates + + public delegate void IncludeDelegate(LuaObjectParser parser, string includefile, bool clearerror); + public IncludeDelegate OnInclude; + + #endregion + + #region ================== Variables + + private Dictionary objects; + /*private Dictionary states; + private List objectfreeslots; + private List statefreeslots; + private List spritefreeslots;*/ + + #endregion + + #region ================== Properties + + public Dictionary Objects { get { return objects; } } + + #endregion + + #region ================== Constructor + + public LuaObjectParser() + { + // Syntax + whitespace = "\n \t\r\u00A0"; + specialtokens = "=\n"; + + objects = new Dictionary(); + /*states = new Dictionary(); + objectfreeslots = new List(); + statefreeslots = new List(); + spritefreeslots = new List();*/ + } + + // Disposer + public void Dispose() + { + objects = null; + /*states = null; + objectfreeslots = null; + statefreeslots = null; + spritefreeslots = null;*/ + } + + #endregion + + #region ================== Parsing + + override public bool Parse(Stream stream, string sourcefilename, bool clearerrors) + { + if (!base.Parse(stream, sourcefilename, clearerrors)) return false; + + // Keep local data + Stream localstream = datastream; + string localsourcename = sourcename; + BinaryReader localreader = datareader; + + string token; + + while (SkipWhitespace(true)) + { + token = ReadToken(); + if (!string.IsNullOrEmpty(token)) + { + if (!token.StartsWith("mobjinfo[") || !token.EndsWith("]")) continue; + string objname = token.Substring(9); + string sprite = DataManager.INTERNAL_PREFIX + "unknownthing"; + string[] states = new string[8]; + int mapThingNum = -1; + int radius = 0; + int height = 0; + objname = objname.TrimEnd(new char[] { ']' }); + + SkipWhitespace(true); + token = ReadToken(); + if (token != "=") + { + ReportError("Invalid object definition, missing ="); + return false; + } + + SkipWhitespace(true); + token = ReadToken(); + if (token != "{") + { + ReportError("Invalid object definition, missing {"); + return false; + } + + SkipWhitespace(true); + token = ReadToken(); + bool finished = false; + while (token != null) + { + if (finished) break; + switch (token) + { + case "$Sprite": + SkipWhitespace(true); + token = ReadToken(); + if (((token.Length > DataManager.INTERNAL_PREFIX.Length) && + token.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX)) || + General.Map.Data.GetSpriteExists(token)) + { + sprite = token; + break; + } + ReportError("The sprite \"" + token + "\" assigned by the \"$sprite\" property does not exist"); + return false; + case "doomednum": + if (!ReadParameter(out token, out finished)) return false; + if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out mapThingNum)) + { + ReportError("Invalid map thing number"); + return false; + } + break; + case "radius": + if (!ReadParameter(out token, out finished)) return false; + if (!ParseWithArithmetic(token, out radius)) + { + ReportError("Invalid radius"); + return false; + } + radius /= 65536; + break; + case "height": + if (!ReadParameter(out token, out finished)) return false; + if (!ParseWithArithmetic(token, out height)) + { + ReportError("Invalid height"); + return false; + } + height /= 65536; + break; + case "spawnstate": + if (!ReadParameter(out token, out finished)) return false; + states[0] = token; + break; + case "seestate": + if (!ReadParameter(out token, out finished)) return false; + states[1] = token; + break; + case "painstate": + if (!ReadParameter(out token, out finished)) return false; + states[2] = token; + break; + case "meleestate": + if (!ReadParameter(out token, out finished)) return false; + states[3] = token; + break; + case "missilestate": + if (!ReadParameter(out token, out finished)) return false; + states[4] = token; + break; + case "deathstate": + if (!ReadParameter(out token, out finished)) return false; + states[5] = token; + break; + case "xdeathstate": + if (!ReadParameter(out token, out finished)) return false; + states[6] = token; + break; + case "raisestate": + if (!ReadParameter(out token, out finished)) return false; + states[7] = token; + break; + case "spawnhealth": + case "seesound": + case "reactiontime": + case "attacksound": + case "painchance": + case "painsound": + case "deathsound": + case "speed": + case "dispoffset": + case "mass": + case "damage": + case "activesound": + case "flags": + if (!ReadParameter(out token, out finished)) return false; + break; + default: + ReportError("Unknown object definition parameter " + token); + return false; + } + SkipWhitespace(true); + token = ReadToken(); + } + + if (token != "}") + { + ReportError("Invalid object definition, missing }"); + return false; + } + + if (mapThingNum > 0) + { + SRB2Object o = new SRB2Object(objname, sprite, states, mapThingNum, radius, height); + if (objects.ContainsKey(objname)) + objects[objname] = o; + else + objects.Add(objname, o); + } + } + } + // All done + return !this.HasError; + } + + #endregion + + #region ================== Methods + + private bool ReadParameter(out string output, out bool finished) + { + output = ""; + finished = false; + SkipWhitespace(true); + string token = ReadToken(); + if (token != "=") + { + ReportError("Invalid parameter definition, missing ="); + return false; + } + SkipWhitespace(true); + token = ReadToken(); + if (!token.EndsWith(",")) + { + finished = true; + } + output = token.TrimEnd(new char[] { ',' }); + return true; + } + + private bool ParseWithArithmetic(string input, out int output) + { + output = 1; + string[] tokens = input.Split(new char[] { '*' }); + foreach (string t in tokens) + { + string trimmed = t.Trim(); + int val = 1; + if (trimmed == "FRACUNIT") val = 65536; + else if (!int.TryParse(trimmed, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) + { + ReportError("Invalid radius"); + return false; + } + output *= val; + } + return true; + } + + protected override string GetLanguageType() + { + return "Lua"; + } + + #endregion + } +}