From 3a817fb1601ca132e2bd74ebaa7f5077951d7b64 Mon Sep 17 00:00:00 2001 From: Daniel Walder Date: Sat, 4 Jan 2025 12:05:45 +1000 Subject: [PATCH] Handle inline comments during parsing instead of trying to preprocess lines Fixes #40 --- .../Sledge.Formats.Bsp.Tests.csproj | 2 +- Sledge.Formats.Bsp.Tests/TestEntitiesLump.cs | 60 +++++++++++++++++++ Sledge.Formats.Bsp/Lumps/Entities.cs | 44 +++++++------- Sledge.Formats.Bsp/Sledge.Formats.Bsp.csproj | 4 +- 4 files changed, 83 insertions(+), 27 deletions(-) diff --git a/Sledge.Formats.Bsp.Tests/Sledge.Formats.Bsp.Tests.csproj b/Sledge.Formats.Bsp.Tests/Sledge.Formats.Bsp.Tests.csproj index d58d8da..524c643 100644 --- a/Sledge.Formats.Bsp.Tests/Sledge.Formats.Bsp.Tests.csproj +++ b/Sledge.Formats.Bsp.Tests/Sledge.Formats.Bsp.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable false diff --git a/Sledge.Formats.Bsp.Tests/TestEntitiesLump.cs b/Sledge.Formats.Bsp.Tests/TestEntitiesLump.cs index c433355..c245015 100644 --- a/Sledge.Formats.Bsp.Tests/TestEntitiesLump.cs +++ b/Sledge.Formats.Bsp.Tests/TestEntitiesLump.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Sledge.Formats.Bsp.Lumps; @@ -78,4 +79,63 @@ public void TestEntitiesKeyOrdering() Assert.AreEqual("test3", lump2[0].SortedKeyValues[2].Key); Assert.AreEqual("789", lump2[0].SortedKeyValues[2].Value); } + + [TestMethod] + public void TestEntitiesWithComments() + { + var data = """ + // comment1 + { + "test1" "123" + // "test2" "456" + "com//ment" "val//ue" + } + // comment2 + """; + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms); + bw.WriteFixedLengthString(Encoding.ASCII, data.Length, data); + + ms.Position = 0; + using var br = new BinaryReader(ms); + var lump2 = new Entities(); + lump2.Read(br, new Blob { Offset = 0, Length = (int)ms.Length }, Version.Goldsource); + + Assert.AreEqual(1, lump2.Count); + Assert.AreEqual(2, lump2[0].KeyValues.Count); + Assert.AreEqual("test1", lump2[0].SortedKeyValues[0].Key); + Assert.AreEqual("123", lump2[0].SortedKeyValues[0].Value); + Assert.AreEqual("com//ment", lump2[0].SortedKeyValues[1].Key); + Assert.AreEqual("val//ue", lump2[0].SortedKeyValues[1].Value); + } + + [TestMethod] + public void TestEntitiesWithDoubleSlashInKeyValue() + { + var lump = new Entities + { + new() + { + SortedKeyValues = + { + new KeyValuePair("a//b", "c//d") + } + } + }; + Assert.AreEqual(1, lump[0].KeyValues.Count); + + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms); + lump.Write(bw, Version.Goldsource); + + ms.Position = 0; + using var br = new BinaryReader(ms); + var lump2 = new Entities(); + lump2.Read(br, new Blob { Offset = 0, Length = (int)ms.Length }, Version.Goldsource); + + Assert.AreEqual(1, lump2.Count); + Assert.AreEqual(1, lump2[0].KeyValues.Count); + Assert.AreEqual("a//b", lump2[0].SortedKeyValues[0].Key); + Assert.AreEqual("c//d", lump2[0].SortedKeyValues[0].Value); + } } \ No newline at end of file diff --git a/Sledge.Formats.Bsp/Lumps/Entities.cs b/Sledge.Formats.Bsp/Lumps/Entities.cs index 833b9fe..e37c3d4 100644 --- a/Sledge.Formats.Bsp/Lumps/Entities.cs +++ b/Sledge.Formats.Bsp/Lumps/Entities.cs @@ -21,23 +21,10 @@ public void Read(BinaryReader br, Blob blob, Version version) { var text = Encoding.ASCII.GetString(br.ReadBytes(blob.Length)); - // Remove comments - var cleaned = new StringBuilder(); - foreach (var line in text.Split('\n')) - { - var l = line; - var idx = l.IndexOf("//", StringComparison.Ordinal); - if (idx >= 0) l = l.Substring(0, idx); - l = l.Trim(); - cleaned.Append(l).Append('\n'); - } - - var data = cleaned.ToString(); - Entity cur = null; int i; string key = null; - for (i = 0; i < data.Length; i++) + for (i = 0; i < text.Length; i++) { var token = GetToken(); if (token == "{") @@ -79,29 +66,38 @@ string GetToken() { if (!ScanToNonWhitespace()) return null; - if (data[i] == '{' || data[i] == '}') + if (text[i] == '/' && i + 1 < text.Length && text[i + 1] == '/') + { + // Comment, find end of line and then skip whitespace again + var idx = text.IndexOf('\n', i + 1); + if (idx < 0) return null; + i = idx + 1; + if (!ScanToNonWhitespace()) return null; + } + + if (text[i] == '{' || text[i] == '}') { // Start/end entity - return data[i].ToString(); + return text[i].ToString(); } - if (data[i] == '"') + if (text[i] == '"') { // Quoted string, find end quote - var idx = data.IndexOf('"', i + 1); + var idx = text.IndexOf('"', i + 1); if (idx < 0) return null; - var tok = data.Substring(i + 1, idx - i - 1); + var tok = text.Substring(i + 1, idx - i - 1); i = idx + 1; return tok; } - if (data[i] > 32) + if (text[i] > 32) { // Not whitespace var s = ""; - while (data[i] > 32) + while (text[i] > 32) { - s += data[i++]; + s += text[i++]; } return s; } @@ -111,9 +107,9 @@ string GetToken() bool ScanToNonWhitespace() { - while (i < data.Length) + while (i < text.Length) { - if (data[i] == ' ' || data[i] == '\n') i++; + if (text[i] == ' ' || text[i] == '\n') i++; else return true; } diff --git a/Sledge.Formats.Bsp/Sledge.Formats.Bsp.csproj b/Sledge.Formats.Bsp/Sledge.Formats.Bsp.csproj index 5d80089..edd04ee 100644 --- a/Sledge.Formats.Bsp/Sledge.Formats.Bsp.csproj +++ b/Sledge.Formats.Bsp/Sledge.Formats.Bsp.csproj @@ -11,8 +11,8 @@ https://github.com/LogicAndTrick/sledge-formats Git half-life quake valve bsp - Add ID to faces, add RawLightmapData to Lighmaps lump - 1.0.15 + Fix issue when entity keyvalues contain double forward slashes + 1.0.16