From 616d46bf5f509c5ae9d1e405bfe1419c490966c9 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 14 Feb 2026 14:01:53 +0100 Subject: [PATCH 01/15] report parsing errors of xml file list --- .../GameObjects/GameObjectTypeGameManager.cs | 43 ++++++++++++++++--- .../Xml/Parsers/XmlContainerContentParser.cs | 13 +++--- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 407d9c5..7ba43d9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -17,18 +17,49 @@ protected override async Task InitializeCoreAsync(CancellationToken token) var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); - await Task.Run(() => contentParser.ParseEntriesFromFileListXml( - "DATA\\XML\\GAMEOBJECTFILES.XML", - GameRepository, - ".\\DATA\\XML", - NamedEntries, - VerifyFilePathLength), token); + contentParser.XmlParseError += OnParseError; + + try + { + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( + "DATA\\XML\\GAMEOBJECTFILES.XML", + GameRepository, + ".\\DATA\\XML\\", + NamedEntries, + VerifyFilePathLength), token); + } + finally + { + contentParser.XmlParseError -= OnParseError; + } + } + + private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) + { + if (e.ErrorInXmlFileList) + { + e.Continue = false; + ErrorReporter.Report(new InitializationError + { + GameManager = ToString(), + Message = GetMessage(e) + }); + } + } + + private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) + { + if (errorEventArgs.HasException) + return $"Error while parsing XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; + return "Could not find GameObjectFiles.xml"; } private void VerifyFilePathLength(string filePath) { if (filePath.Length > PGConstants.MaxGameObjectDatabaseFileName) { + // Technically this is an assert in the engine, but in Release Mode, the game CTDs. + // Thus, we rank this as an initialization error. ErrorReporter.Report(new InitializationError { GameManager = ToString(), diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs index 00f4f61..63735d7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -45,7 +45,7 @@ public void ParseEntriesFromFileListXml( _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); Logger.LogWarning("Could not find XML file '{XmlFile}'", xmlFile); - var args = new XmlContainerParserErrorEventArgs(xmlFile, null, true) + var args = new XmlContainerParserErrorEventArgs(xmlFile, isXmlFileList: true) { // No reason to continue Continue = false @@ -65,7 +65,10 @@ public void ParseEntriesFromFileListXml( } catch (XmlException e) { - var args = new XmlContainerParserErrorEventArgs(xmlFile, e, true) + _reporter?.Report(this, + new XmlParseErrorEventArgs(new XmlLocationInfo(xmlFile, e.LineNumber), XmlParseErrorKind.Unknown, e.Message)); + + var args = new XmlContainerParserErrorEventArgs(xmlFile, e, isXmlFileList: true) { // No reason to continue Continue = false @@ -78,7 +81,7 @@ public void ParseEntriesFromFileListXml( var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); var parser = _fileParserFactory.CreateFileParser(_reporter); - + foreach (var file in xmlFiles) { if (onFileParseAction is not null) @@ -91,7 +94,7 @@ public void ParseEntriesFromFileListXml( _reporter?.Report(parser, XmlParseErrorEventArgs.FromMissingFile(file)); Logger.LogWarning("Could not find XML file '{File}'", file); - var args = new XmlContainerParserErrorEventArgs(file); + var args = new XmlContainerParserErrorEventArgs(file, isXmlFileList: false); XmlParseError?.Invoke(this, args); if (args.Continue) @@ -109,7 +112,7 @@ public void ParseEntriesFromFileListXml( { _reporter?.Report(parser, new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); - var args = new XmlContainerParserErrorEventArgs(file, e); + var args = new XmlContainerParserErrorEventArgs(file, e, isXmlFileList: false); XmlParseError?.Invoke(this, args); if (!args.Continue) From 4f46bcc9904d89e1125d3514a75600255a677f67 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 14 Feb 2026 14:20:32 +0100 Subject: [PATCH 02/15] rename types --- .../ErrorReporting/GameEngineErrorReporterWrapper.cs | 2 +- .../PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs | 2 +- .../Xml/Parsers/XmlContainerContentParser.cs | 4 ++-- .../PG.StarWarsGame.Files.XML/Data/XmlFileList.cs | 8 ++++++++ .../Data/XmlFileListContainer.cs | 8 -------- .../ErrorHandling/IXmlParserErrorReporter.cs | 2 +- .../ErrorHandling/PrimitiveXmlErrorReporter.cs | 2 +- .../ErrorHandling/XmlErrorEventHandler.cs | 2 +- .../ErrorHandling/XmlErrorReporter.cs | 4 ++-- .../Parsers/Base/IPetroglyphXmlElementParser.cs | 2 +- .../Parsers/Base/IPetroglyphXmlFileContainerParser.cs | 2 +- .../Parsers/Base/IPetroglyphXmlFileParser.cs | 2 +- ...PetroglyphXmlParser.cs => IPetroglyphXmlParserInfo.cs} | 2 +- .../Parsers/Base/PetroglyphXmlParserBase.cs | 2 +- .../Parsers/XmlFileListParser.cs | 7 ++++--- 15 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs rename src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/{IPetroglyphXmlParser.cs => IPetroglyphXmlParserInfo.cs} (64%) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs index a636aba..adf715a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs @@ -33,7 +33,7 @@ public void Assert(EngineAssert assert) _errorReporter?.Assert(assert); } - public override void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) + public override void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) { if (_errorReporter is null) return; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs index 83e6e9f..e6124ec 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs @@ -9,7 +9,7 @@ public sealed class XmlError { public required XmlLocationInfo FileLocation { get; init; } - public required IPetroglyphXmlParser Parser { get; init; } + public required IPetroglyphXmlParserInfo Parser { get; init; } public XElement? Element { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs index 63735d7..e640c53 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -14,7 +14,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; -internal sealed class XmlContainerContentParser : ServiceBase, IPetroglyphXmlParser +internal sealed class XmlContainerContentParser : ServiceBase, IPetroglyphXmlParserInfo { public event EventHandler? XmlParseError; @@ -54,7 +54,7 @@ public void ParseEntriesFromFileListXml( return; } - XmlFileListContainer? container; + XmlFileList? container; try { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs new file mode 100644 index 0000000..8553f33 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace PG.StarWarsGame.Files.XML.Data; + +public class XmlFileList(IReadOnlyList files) +{ + public IReadOnlyList Files { get; } = files; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs deleted file mode 100644 index 31a16cc..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace PG.StarWarsGame.Files.XML.Data; - -public class XmlFileListContainer(IList files) -{ - public IList Files { get; } = files; -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs index 6bb14c1..d1dceed 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs @@ -4,5 +4,5 @@ namespace PG.StarWarsGame.Files.XML.ErrorHandling; public interface IXmlParserErrorReporter { - void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error); + void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs index 39de0e6..fda7012 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs @@ -15,7 +15,7 @@ private PrimitiveXmlErrorReporter() { } - public void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) + public void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) { XmlParseError?.Invoke(parser, error); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs index 69962e3..9e4821c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs @@ -2,4 +2,4 @@ namespace PG.StarWarsGame.Files.XML.ErrorHandling; -public delegate void XmlErrorEventHandler(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error); \ No newline at end of file +public delegate void XmlErrorEventHandler(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error); \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs index 8234729..5369633 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs @@ -12,7 +12,7 @@ public XmlErrorReporter() PrimitiveXmlErrorReporter.Instance.XmlParseError += OnPrimitiveError; } - public virtual void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) + public virtual void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) { XmlParseError?.Invoke(parser, error); } @@ -22,7 +22,7 @@ protected override void DisposeResources() PrimitiveXmlErrorReporter.Instance.XmlParseError -= OnPrimitiveError; } - private void OnPrimitiveError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) + private void OnPrimitiveError(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) { Report(parser, error); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs index 14a8aa1..f5a6e95 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs @@ -2,7 +2,7 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public interface IPetroglyphXmlElementParser : IPetroglyphXmlParser where T : notnull +public interface IPetroglyphXmlElementParser : IPetroglyphXmlParserInfo where T : notnull { T Parse(XElement element); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs index cd21e00..34dbd77 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs @@ -4,7 +4,7 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParser where T : notnull +public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParserInfo where T : notnull { void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs index 5f9c4b4..31aa052 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs @@ -2,7 +2,7 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public interface IPetroglyphXmlFileParser : IPetroglyphXmlParser where T : notnull +public interface IPetroglyphXmlFileParser : IPetroglyphXmlParserInfo where T : notnull { T? ParseFile(Stream xmlStream); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParserInfo.cs similarity index 64% rename from src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParserInfo.cs index fdf5fd5..6ba0ebf 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParserInfo.cs @@ -1,6 +1,6 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public interface IPetroglyphXmlParser +public interface IPetroglyphXmlParserInfo { string Name { get; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs index 7395eb6..698ccb2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs @@ -4,7 +4,7 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphXmlParserBase : IPetroglyphXmlParser +public abstract class PetroglyphXmlParserBase : IPetroglyphXmlParserInfo { protected readonly IXmlParserErrorReporter? ErrorReporter; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index 380ea3f..c148da2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; @@ -7,11 +8,11 @@ namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class XmlFileListParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : - PetroglyphXmlFileParser(serviceProvider, errorReporter) + PetroglyphXmlFileParser(serviceProvider, errorReporter) { protected override bool LoadLineInfo => false; - protected override XmlFileListContainer Parse(XElement element, string fileName) + protected override XmlFileList Parse(XElement element, string fileName) { var files = new List(); foreach (var child in element.Elements()) @@ -33,6 +34,6 @@ protected override XmlFileListContainer Parse(XElement element, string fileName) $"Tag '<{tagName}>' is not supported. Only '' is supported.")); } } - return new XmlFileListContainer(files); + return new XmlFileList(new ReadOnlyCollection(files)); } } \ No newline at end of file From c13b08a70281abf7caf674f05d8f52513e0901eb Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 11:32:02 +0100 Subject: [PATCH 03/15] start refactoring xml parsers --- src/ModVerify.CliApp/Program.cs | 2 +- .../Properties/launchSettings.json | 2 +- src/ModVerify/DefaultGameVerifiersProvider.cs | 9 +- .../Audio/Sfx/SfxEventGameManager.cs | 7 +- .../CommandBar/CommandBarGameManager.cs | 7 +- .../GameObjects/GameObjectTypeGameManager.cs | 71 +++- .../GuiDialogGameManager_Initialization.cs | 2 +- .../Xml/IPetroglyphXmlFileParserFactory.cs | 2 +- .../Xml/ParserNotFoundException.cs | 2 +- .../Parsers/Data/CommandBarComponentParser.cs | 35 +- .../Xml/Parsers/Data/GameObjectParser.cs | 28 +- .../Xml/Parsers/Data/SfxEventParser.cs | 37 +- .../Xml/Parsers/EngineXmlParser.cs | 321 ++++++++++++++++++ ...gs.cs => EngineXmlParserErrorEventArgs.cs} | 2 +- .../File/CommandBarComponentFileParser.cs | 31 -- .../{Data => File}/GameConstantsParser.cs | 2 +- .../Xml/Parsers/File/GameObjectFileParser.cs | 25 -- .../Xml/Parsers/File/SfxEventFileParser.cs | 32 -- .../Xml/Parsers/XmlContainerContentParser.cs | 123 ------- .../Xml/Parsers/XmlObjectParser.cs | 107 +++--- .../Xml/PetroglyphXmlParserFactory.cs | 22 +- .../Data/XmlFileList.cs | 2 + .../Base/IPetroglyphXmlFileContainerParser.cs | 4 +- .../Parsers/Base/IPetroglyphXmlFileParser.cs | 2 +- .../Base/PetroglyphXmlFileParserBase.cs | 12 +- .../IPetroglyphXmlNamedElementParser.cs | 10 + .../PetroglyphXmlFileContainerParser.cs | 33 +- .../Parsers/PetroglyphXmlFileParser.cs | 10 +- .../CommaSeparatedStringKeyValueListParser.cs | 5 +- .../PetroglyphPrimitiveXmlParser.cs | 7 +- .../Primitives/PetroglyphXmlBooleanParser.cs | 5 +- .../Primitives/PetroglyphXmlByteParser.cs | 7 +- .../Primitives/PetroglyphXmlFloatParser.cs | 10 +- .../Primitives/PetroglyphXmlIntegerParser.cs | 16 +- .../PetroglyphXmlLooseStringListParser.cs | 4 +- .../PetroglyphXmlMax100ByteParser.cs | 7 +- .../PetroglyphXmlRgbaColorParser.cs | 2 +- .../Primitives/PetroglyphXmlStringParser.cs | 7 +- .../PetroglyphXmlUnsignedIntegerParser.cs | 7 +- .../Primitives/PetroglyphXmlVector2FParser.cs | 5 +- .../XmlFileConstants.cs | 10 + 41 files changed, 616 insertions(+), 418 deletions(-) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{XmlContainerParserErrorEventArgs.cs => EngineXmlParserErrorEventArgs.cs} (82%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{Data => File}/GameConstantsParser.cs (90%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileConstants.cs diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 68056a4..fd698cc 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -51,7 +51,7 @@ private static Task Main(string[] args) internal class Program : SelfUpdateableAppLifecycle { - private static readonly string EngineParserNamespace = typeof(XmlObjectParser<>).Namespace!; + private static readonly string EngineParserNamespace = typeof(EngineXmlParser).Namespace!; private static readonly string ParserNamespace = typeof(PetroglyphXmlFileParser<>).Namespace!; private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}"); diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index 299ce46..128bcdd 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -10,7 +10,7 @@ }, "Verify (Automatic Target Selection)": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --path \"C:\\Program Files (x86)\\Steam\\steamapps\\common\\Star Wars Empire at War\\corruption\"" + "commandLineArgs": "verify --offline -o verifyResults --path \"C:/Program Files (x86)/Steam/steamapps/common/Star Wars Empire at War/corruption/Mods/Test\"" }, "Create Baseline Interactive": { "commandName": "Project", diff --git a/src/ModVerify/DefaultGameVerifiersProvider.cs b/src/ModVerify/DefaultGameVerifiersProvider.cs index c9982b7..e4bb0cd 100644 --- a/src/ModVerify/DefaultGameVerifiersProvider.cs +++ b/src/ModVerify/DefaultGameVerifiersProvider.cs @@ -14,10 +14,11 @@ public IEnumerable GetVerifiers( GameVerifySettings settings, IServiceProvider serviceProvider) { - yield return new ReferencedModelsVerifier(database, settings, serviceProvider); - yield return new DuplicateNameFinder(database, settings, serviceProvider); - yield return new AudioFilesVerifier(database, settings, serviceProvider); - yield return new GuiDialogsVerifier(database, settings, serviceProvider); + yield break; + //yield return new ReferencedModelsVerifier(database, settings, serviceProvider); + //yield return new DuplicateNameFinder(database, settings, serviceProvider); + //yield return new AudioFilesVerifier(database, settings, serviceProvider); + //yield return new GuiDialogsVerifier(database, settings, serviceProvider); //yield return new CommandBarVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index b037939..72fecbb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -24,13 +24,12 @@ protected override async Task InitializeCoreAsync(CancellationToken token) Logger?.LogInformation("Parsing SFXEvents..."); - var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); + var contentParser = new EngineXmlParser(GameRepository, ServiceProvider, ErrorReporter); contentParser.XmlParseError += OnParseError; try { await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\SFXEventFiles.XML", - GameRepository, "DATA\\XML", NamedEntries, VerifyFilePathLength), @@ -42,7 +41,7 @@ await Task.Run(() => contentParser.ParseEntriesFromFileListXml( } } - private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) + private void OnParseError(object sender, EngineXmlParserErrorEventArgs e) { if (e.ErrorInXmlFileList || e.HasException) { @@ -55,7 +54,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) } } - private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) + private static string GetMessage(EngineXmlParserErrorEventArgs errorEventArgs) { if (errorEventArgs.HasException) return $"Error while parsing SFXEvent XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 0b8687e..4af4a86 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -74,7 +74,7 @@ protected override async Task InitializeCoreAsync(CancellationToken token) { Logger?.LogInformation("Creating command bar components..."); - var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); + var contentParser = new EngineXmlParser(GameRepository, ServiceProvider, ErrorReporter); contentParser.XmlParseError += OnParseError; var parsedCommandBarComponents = new FrugalValueListDictionary(); @@ -83,7 +83,6 @@ protected override async Task InitializeCoreAsync(CancellationToken token) { await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\CommandBarComponentFiles.XML", - GameRepository, ".\\DATA\\XML", parsedCommandBarComponents, VerifyFilePathLength), @@ -259,7 +258,7 @@ private void SetComponentGroup(IEnumerable components) } } - private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) + private void OnParseError(object sender, EngineXmlParserErrorEventArgs e) { if (e.ErrorInXmlFileList || e.HasException) { @@ -272,7 +271,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) } } - private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) + private static string GetMessage(EngineXmlParserErrorEventArgs errorEventArgs) { if (errorEventArgs.HasException) return $"Error while parsing CommandBar XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 7ba43d9..7650185 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -1,10 +1,11 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Xml.Parsers; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace PG.StarWarsGame.Engine.GameObjects; @@ -14,31 +15,67 @@ internal class GameObjectTypeGameManager(GameRepository repository, GameEngineEr protected override async Task InitializeCoreAsync(CancellationToken token) { Logger?.LogInformation("Parsing GameObjects..."); + await Task.Run(ParseGameObjectDatabases, token); + } - var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); - - contentParser.XmlParseError += OnParseError; + private void ParseGameObjectDatabases() + { + var parser = new EngineXmlParser(GameRepository, ServiceProvider, ErrorReporter); + parser.XmlParseError += OnParseError; try { - await Task.Run(() => contentParser.ParseEntriesFromFileListXml( - "DATA\\XML\\GAMEOBJECTFILES.XML", - GameRepository, - ".\\DATA\\XML\\", - NamedEntries, - VerifyFilePathLength), token); + var xmlFileList = parser.ParseFileList(@"DATA\XML\GAMEOBJECTFILES.XML").Files + .Select(x => + { + var filePath = FileSystem.Path.Combine(@".\DATA\XML\", x); + VerifyFilePathLength(filePath); + return filePath; + }).ToList(); + + + //var gameObjectFileParser = new GameObjectFileParser(serviceProvider, errorReporter); + + var allLoaded = false; + for (var passNumber = 0; !allLoaded && passNumber < 10; passNumber++) + { + foreach (var gameObjectXmlFile in xmlFileList) + { + if (passNumber == 0) + { + //ParseSingleGameObjectFile(gameObjectXmlFile, parser, gameObjectFileParser); + } + else + { + } + } + + + + //GameObjectTypeClass::Static_Post_Load_Fixup(); + //SFXEventReferenceClass::Static_Post_Load_Fixup(); + //SpeechEventReferenceClass::Static_Post_Load_Fixup(); + //MusicEventReferenceClass::Static_Post_Load_Fixup(); + //FactionReferenceClass::Static_Post_Load_Fixup(); + //... + } } finally { - contentParser.XmlParseError -= OnParseError; + parser.XmlParseError -= OnParseError; } } - private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) + //private void ParseSingleGameObjectFile(string file, EngineXmlParser engineParser, GameObjectFileParser gameObjectFileParser) + //{ + // engineParser.ParseEntriesFromContainerFile(gameObjectFileParser, file, NamedEntries); + //} + + + private void OnParseError(object sender, EngineXmlParserErrorEventArgs e) { if (e.ErrorInXmlFileList) { - e.Continue = false; ErrorReporter.Report(new InitializationError { GameManager = ToString(), @@ -47,7 +84,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) } } - private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) + private static string GetMessage(EngineXmlParserErrorEventArgs errorEventArgs) { if (errorEventArgs.HasException) return $"Error while parsing XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index c3814ea..68fdabf 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -14,7 +14,7 @@ namespace PG.StarWarsGame.Engine.GuiDialog; -partial class GuiDialogGameManager +internal partial class GuiDialogGameManager { public const int MegaTextureMaxFilePathLength = 255; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs index 4532365..b3cf950 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs @@ -5,5 +5,5 @@ namespace PG.StarWarsGame.Engine.Xml; public interface IPetroglyphXmlFileParserFactory { - IPetroglyphXmlFileContainerParser CreateFileParser(IXmlParserErrorReporter? errorReporter) where T : notnull; + IPetroglyphXmlFileContainerParser CreateFileContainerParser(IXmlParserErrorReporter? errorReporter) where T : XmlObject; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs index 19bc797..fd4fba3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs @@ -2,7 +2,7 @@ namespace PG.StarWarsGame.Engine.Xml; -public sealed class ParserNotFoundException : Exception +internal sealed class ParserNotFoundException : Exception { public override string Message { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs index 297b474..e8f2cd2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -1,33 +1,26 @@ -using System; -using System.Collections.ObjectModel; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; +using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; +using System; +using System.Collections.ObjectModel; +using System.Xml.Linq; +using Crc32 = PG.Commons.Hashing.Crc32; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; -public sealed class CommandBarComponentParser( - IReadOnlyFrugalValueListDictionary parsedElements, - IServiceProvider serviceProvider, - IXmlParserErrorReporter? errorReporter = null) - : XmlObjectParser(parsedElements, serviceProvider, errorReporter) +internal class CommandBarComponentParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : NamedXmlObjectParser(serviceProvider, errorReporter) { - public override CommandBarComponentData Parse(XElement element, out Crc32 crc32) + + protected override CommandBarComponentData CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location) { - var name = GetXmlObjectName(element, out crc32, true); - var component = new CommandBarComponentData(name, crc32, XmlLocationInfo.FromElement(element)); - Parse(component, element, default); - ValidateValues(component, element); - component.CoerceValues(); - return component; + return new CommandBarComponentData(name, nameCrc, location); } - - protected override bool ParseTag(XElement tag, CommandBarComponentData componentData) + + protected override bool ParseTag(XElement tag, CommandBarComponentData componentData, in IReadOnlyFrugalValueListDictionary parseState) { switch (tag.Name.LocalName) { @@ -352,7 +345,7 @@ protected override bool ParseTag(XElement tag, CommandBarComponentData component } } - private void ValidateValues(CommandBarComponentData xmlData, XElement element) + protected override void ValidateValues(CommandBarComponentData xmlData, XElement element) { if (xmlData.Name.Length > PGConstants.MaxCommandBarComponentName) { @@ -360,6 +353,4 @@ private void ValidateValues(CommandBarComponentData xmlData, XElement element) $"CommandbarComponent name '{xmlData.Name}' is too long.")); } } - - public override CommandBarComponentData Parse(XElement element) => throw new NotSupportedException(); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs index 5cac3e2..2b9126b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs @@ -1,11 +1,11 @@ -using System; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; +using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; +using System; +using System.Xml.Linq; +using Crc32 = PG.Commons.Hashing.Crc32; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -25,25 +25,17 @@ public static class GameObjectXmlTags public const string DamagedSmokeAssetName = "Damaged_Smoke_Asset_Name"; } -public sealed class GameObjectParser( - IReadOnlyFrugalValueListDictionary parsedElements, - IServiceProvider serviceProvider, - IXmlParserErrorReporter? errorReporter = null) - : XmlObjectParser(parsedElements, serviceProvider, errorReporter) +internal class GameObjectParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : NamedXmlObjectParser(serviceProvider, errorReporter) { - public override GameObject Parse(XElement element, out Crc32 crc32) + protected override GameObject CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location) { - var name = GetXmlObjectName(element, out crc32, true); var type = GetTagName(element); var objectType = EstimateType(type); - var gameObject = new GameObject(type, name, crc32, objectType, XmlLocationInfo.FromElement(element)); - - Parse(gameObject, element, default); - - return gameObject; + return new GameObject(type, name, nameCrc, objectType, location); } - protected override bool ParseTag(XElement tag, GameObject xmlObject) + protected override bool ParseTag(XElement tag, GameObject xmlObject, in IReadOnlyFrugalValueListDictionary parseState) { switch (tag.Name.LocalName) { @@ -143,6 +135,4 @@ private static GameObjectType EstimateType(string tagName) _ => GameObjectType.Unknown }; } - - public override GameObject Parse(XElement element) => throw new NotSupportedException(); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs index bc3439f..c59bc40 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs @@ -1,33 +1,28 @@ -using System; -using System.Collections.ObjectModel; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; +using AnakinRaW.CommonUtilities.Collections; +using Microsoft.Extensions.DependencyInjection; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; -public sealed class SfxEventParser( - IReadOnlyFrugalValueListDictionary parsedElements, - IServiceProvider serviceProvider, - IXmlParserErrorReporter? errorReporter = null) - : XmlObjectParser(parsedElements, serviceProvider, errorReporter) -{ - public override SfxEvent Parse(XElement element, out Crc32 crc32) +internal class SfxEventParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : NamedXmlObjectParser(serviceProvider, errorReporter) +{ + protected override SfxEvent CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location) { - var name = GetXmlObjectName(element, out crc32, true); - var sfxEvent = new SfxEvent(name, crc32, XmlLocationInfo.FromElement(element)); - Parse(sfxEvent, element, default); - ValidateValues(sfxEvent, element); - sfxEvent.CoerceValues(); - return sfxEvent; + return new SfxEvent(name, nameCrc, location); } - private void ValidateValues(SfxEvent sfxEvent, XElement element) + protected override void ValidateValues(SfxEvent sfxEvent, XElement element) { if (sfxEvent.Name.Length > PGConstants.MaxSFXEventName) { @@ -72,7 +67,7 @@ private void ValidateValues(SfxEvent sfxEvent, XElement element) } } - protected override bool ParseTag(XElement tag, SfxEvent sfxEvent) + protected override bool ParseTag(XElement tag, SfxEvent sfxEvent, in IReadOnlyFrugalValueListDictionary parsedEntries) { switch (tag.Name.LocalName) { @@ -86,7 +81,7 @@ protected override bool ParseTag(XElement tag, SfxEvent sfxEvent) { var presetName = PetroglyphXmlStringParser.Instance.Parse(tag); var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.DefaultPGEncoding); - if (presetNameCrc != default && ParsedElements.TryGetFirstValue(presetNameCrc, out var preset)) + if (presetNameCrc != default && parsedEntries.TryGetFirstValue(presetNameCrc, out var preset)) sfxEvent.ApplyPreset(preset); else { @@ -199,6 +194,4 @@ protected override bool ParseTag(XElement tag, SfxEvent sfxEvent) default: return false; } } - - public override SfxEvent Parse(XElement element) => throw new NotSupportedException(); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs new file mode 100644 index 0000000..c93c773 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs @@ -0,0 +1,321 @@ +using AnakinRaW.CommonUtilities; +using AnakinRaW.CommonUtilities.Collections; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.Commons.Hashing; +using PG.Commons.Services; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.IO; +using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +namespace PG.StarWarsGame.Engine.Xml.Parsers; + +public sealed class EngineXmlParser : ServiceBase, IPetroglyphXmlParserInfo +{ + public event EventHandler? XmlParseError; + + private readonly IGameRepository _gameRepository; + private readonly IXmlParserErrorReporter? _reporter; + private readonly IPetroglyphXmlFileParserFactory _fileParserFactory; + + public EngineXmlParser(IGameRepository gameRepository, IServiceProvider serviceProvider, IXmlParserErrorReporter? reporter) + : base(serviceProvider) + { + _gameRepository = gameRepository; + _reporter = reporter; + _fileParserFactory = serviceProvider.GetRequiredService(); + Name = GetType().FullName!; + } + + public string Name { get; } + + public XmlFileList ParseFileList(string xmlFile) + { + Logger.LogDebug("Parsing container data '{XmlFile}'", xmlFile); + + using var containerStream = _gameRepository.TryOpenFile(xmlFile); + if (containerStream == null) + { + _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + Logger.LogWarning("Could not find XML file '{XmlFile}'", xmlFile); + + var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: true); + XmlParseError?.Invoke(this, args); + return XmlFileList.Empty; + } + + XmlFileList? container; + var containerParser = new XmlFileListParser(Services, _reporter); + + try + { + container = containerParser.ParseFile(containerStream); + if (container is null) + throw new XmlException($"Unable to parse XML container file '{xmlFile}'."); + } + catch (XmlException e) + { + _reporter?.Report(containerParser, new XmlParseErrorEventArgs(new XmlLocationInfo(xmlFile, e.LineNumber), + XmlParseErrorKind.Unknown, e.Message)); + var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: true); + XmlParseError?.Invoke(this, args); + return XmlFileList.Empty; + } + + return container; + } + + public void ParseEntriesFromFileListXml( + string xmlFile, + string lookupPath, + FrugalValueListDictionary entries, + Action? onFileParseAction = null) where T : XmlObject + { + var container = ParseFileList(xmlFile); + + var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); + + var parser = _fileParserFactory.CreateFileContainerParser(_reporter); + + foreach (var file in xmlFiles) + { + onFileParseAction?.Invoke(file); + if (!ParseEntriesFromContainerFile(file, parser, entries)) + return; + } + } + + public bool ParseEntriesFromContainerFile( + string xmlFile, + IPetroglyphXmlFileContainerParser parser, + IFrugalValueListDictionary entries) where T : notnull + { + using var fileStream = _gameRepository.TryOpenFile(xmlFile); + + if (fileStream is null) + { + _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + Logger.LogWarning("Could not find XML file '{File}'", xmlFile); + + var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: false); + XmlParseError?.Invoke(this, args); + return args.Continue; + } + + Logger.LogDebug("Parsing File '{File}'", xmlFile); + + try + { + parser.ParseFile(fileStream, entries); + return true; + } + catch (XmlException e) + { + _reporter?.Report(parser, + new XmlParseErrorEventArgs(new XmlLocationInfo(xmlFile, 0), XmlParseErrorKind.Unknown, e.Message)); + + var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: false); + XmlParseError?.Invoke(this, args); + return args.Continue; + } + } +} + + +internal partial class XmlTagMapperDatabase +{ + private readonly IServiceProvider _serviceProvider; + + public XmlTagMapperDatabase(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + SfxEventMap = CreateSfxEventMap(); + } +} + +internal partial class XmlTagMapperDatabase +{ + private XmlTagMapper CreateSfxEventMap() + { + return new SfxEventXmlTagMapper(_serviceProvider); + } + + private sealed class SfxEventXmlTagMapper(IServiceProvider serviceProvider) : XmlTagMapper(serviceProvider) + { + protected override void BuildMappings() + { + AddMapping( + "OverlapTestName", + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.OverlapTestName = val); + } + } +} + + +internal partial class XmlTagMapperDatabase +{ + public XmlTagMapper SfxEventMap { get; } +} + +internal abstract class XmlTagMapper where TClass : notnull +{ + private const int MaxTagLength = 256; + + private delegate void ParserValueAction(TClass target, XElement element); + + private readonly Dictionary _tagMappings = new(); + private readonly ICrc32HashingService _crcService; + + protected XmlTagMapper(IServiceProvider serviceProvider) + { + if (serviceProvider == null) + throw new ArgumentNullException(nameof(serviceProvider)); + _crcService = serviceProvider.GetRequiredService(); + + // ReSharper disable once VirtualMemberCallInConstructor + BuildMappings(); + } + + protected abstract void BuildMappings(); + + protected void AddMapping(string tagName, Func parser, Action setter) + { + ThrowHelper.ThrowIfNullOrEmpty(tagName); + if (tagName.Length >= MaxTagLength) + throw new ArgumentOutOfRangeException( + $"Tag name '{tagName}' exceeds maximum length of {MaxTagLength} characters", nameof(tagName)); + + if (parser == null) + throw new ArgumentNullException(nameof(parser)); + if (setter == null) + throw new ArgumentNullException(nameof(setter)); + + var crc = GetCrc32(tagName); + + _tagMappings[crc] = (target, element) => + { + var value = parser(element); + setter(target, value); + }; + } + + public bool TryParseEntry(XElement element, TClass target) + { + var tagName = element.Name.LocalName; + if (tagName.Length >= MaxTagLength) + return false; + + var crc = GetCrc32(tagName); + + if (!_tagMappings.TryGetValue(crc, out var mapping)) + return false; + + mapping(target, element); + return true; + } + + private Crc32 GetCrc32(string tagName) + { + return _crcService.GetCrc32Upper(tagName, PGConstants.DefaultPGEncoding); + } +} + +enum XmlDataType +{ + DB_DATA_TYPE_BOOL = 0x0, + DB_DATA_TYPE_DWORD = 0x1, + DB_DATA_TYPE_UNSIGNED_CHAR = 0x2, + DB_DATA_TYPE_SIGNED_CHAR = 0x3, + DB_DATA_TYPE_UNSIGNED_CHAR_PERCENT = 0x4, + DB_DATA_TYPE_UNSIGNED_INT = 0x5, + DB_DATA_TYPE_SIGNED_INT = 0x6, + DB_DATA_TYPE_UNSIGNED_INT_PERCENT = 0x7, + DB_DATA_TYPE_FLOAT = 0x8, + DB_DATA_TYPE_UNIT_FLOAT = 0x9, + DB_DATA_TYPE_DOUBLE = 0xa, + DB_DATA_TYPE_UNIT_DOUBLE = 0xb, + DB_DATA_TYPE_SIGNED_INT_HEX = 0xc, + DB_DATA_TYPE_DWORD_HEX = 0xd, + DB_DATA_TYPE_CONVERSION = 0xe, + DB_DATA_TYPE_VECTOR2 = 0xf, + DB_DATA_TYPE_VECTOR3 = 0x10, + DB_DATA_TYPE_VECTOR4 = 0x11, + DB_DATA_TYPE_DYN_VECTOR_INT = 0x12, + DB_DATA_TYPE_DYN_VECTOR_FLOAT = 0x13, + DB_DATA_TYPE_DYN_VECTOR_VECTOR3 = 0x14, + DB_DATA_TYPE_DYN_VECTOR_VECTOR2 = 0x15, + DB_DATA_TYPE_RGBA = 0x16, + DB_DATA_TYPE_STL_STRING = 0x17, + DB_DATA_TYPE_STL_LIST_STL_STRINGS = 0x18, + DB_DATA_TYPE_STL_VECTOR_DWORDS = 0x19, + DB_DATA_TYPE_STL_VECTOR_DWORDS_HEX = 0x1a, + DB_DATA_TYPE_STL_VECTOR_STL_STRINGS = 0x1b, + DB_DATA_TYPE_STL_STRING_UPPER = 0x1c, + DB_DATA_TYPE_OBJECT_REFERENCE = 0x1d, + DB_DATA_TYPE_MULTI_OBJECT_REFERENCE = 0x1e, + DB_DATA_TYPE_SFX_EVENT = 0x1f, + DB_DATA_TYPE_SPEECH_EVENT = 0x20, + DB_DATA_TYPE_MUSIC_EVENT = 0x21, + DB_DATA_TYPE_SFXEVENT_OVERRIDE_LIST_ENTRY = 0x22, + DB_DATA_TYPE_WEATHER_SFXEVENT_LOOP_LIST_ENTRY = 0x23, + DB_DATA_TYPE_WEATHER_SFXEVENT_INTERMITTENT_LIST_ENTRY = 0x24, + DB_DATA_TYPE_WEATHER_SFX_EVENT_PAIR_ARRAY_ENTRY = 0x25, + DB_DATA_TYPE_AMBIENT_SFXEVENT_INTERMITTENT_LIST_ENTRY = 0x26, + DB_DATA_TYPE_DYN_VECTOR_SFX_EVENT_ENTRY = 0x27, + DB_DATA_TYPE_TRIPLE_OBJ_TYPE_AND_SPEECH_EVENT_ENTRY = 0x28, + DB_DATA_TYPE_DYN_VECTOR_FACTION_AND_MUSIC_EVENT_PAIR_ENTRY = 0x29, + DB_DATA_TYPE_DYN_VECTOR_STL_STRINGS = 0x2a, + DB_DATA_TYPE_FACTION_DATA_OVERRIDE_UINT = 0x2b, + DB_DATA_TYPE_FACTION_DATA_OVERRIDE_FLOAT = 0x2c, + DB_DATA_TYPE_FACTION_DATA_OVERRIDE_NAMEREF = 0x2d, + DB_DATA_TYPE_QUADRATIC = 0x2e, + DB_DATA_TYPE_SPLINE = 0x2f, + DB_DATA_TYPE_LINEAR = 0x30, + DB_DATA_TYPE_PARABOLIC = 0x31, + DB_DATA_TYPE_SHIPCLASS = 0x32, + DB_DATA_TYPE_SCRIPT_VARIABLE = 0x33, + DB_DATA_TYPE_CONVERSION_STRING_PAIR_VECTOR = 0x34, + DB_DATA_TYPE_CONVERSION_OBJECT_REF_VECTOR = 0x35, + DB_DATA_TYPE_STL_VECTOR_OBJECT_REFERENCE_STRING_PAIR = 0x36, + DB_DATA_TYPE_STARTING_FORCE_DEFINITION = 0x37, + DB_DATA_TYPE_STL_VECTOR_ABILITIES = 0x38, + DB_DATA_TYPE_STL_VECTOR_ABILITIES_DATA = 0x39, + DB_DATA_TYPE_STL_VECTOR_ACTIONS = 0x3a, + DB_DATA_TYPE_STL_HASHMAP_DAMAGE_TO_DEATH_CLONE = 0x3b, + DB_DATA_TYPE_STL_VECTOR_STRING_INT_PAIR = 0x3c, + DB_DATA_TYPE_LIST_GAME_OBJECT_CATEGORY_FLOAT_PAIR = 0x3d, + DB_DATA_TYPE_DAMAGE_TO_ARMOR_MOD_ENTRY = 0x3e, + DB_DATA_QUOTED_STRING_DYN_VECTOR_ENTRY = 0x3f, + DB_DATA_TYPE_LANGUAGE_STL_STRING_ARRAY_ENTRY = 0x40, + DB_DATA_TYPE_HARD_POINT_TYPE_ARRAY_OF_DYN_VECTOR_STL_STRINGS = 0x41, + DB_DATA_TYPE_HARD_POINT_TYPE_ARRAY_OF_SFXEVENTS = 0x42, + DB_DATA_TYPE_FACTION = 0x43, + DB_DATA_TYPE_WEIGHTED_TYPE_LIST = 0x44, + DB_DATA_TYPE_STL_VECTOR_CONVERSION = 0x45, + DB_DATA_TYPE_STL_VECTOR_OBJECT_REFERENCE_INT_PAIR = 0x46, + DB_DATA_TYPE_DISCRETE_DISTRIBUTION_NAME_REFERENCE = 0x47, + DB_DATA_TYPE_STL_HASHMAP_CONVERSION_TO_FLOAT = 0x48, + DB_DATA_TYPE_UNIT_ABILITY = 0x49, + DB_TYPE_PROJECTILE_CATEGORY = 0x4a, + DB_DATA_TYPE_RENDER_MODE = 0x4b, + DB_DATA_TYPE_NON_HERO_ABILITIES_SFX_EVENT_PAIR = 0x4c, + DB_DATA_TYPE_UNIT_MODE_MULTIPLIER_MOD = 0x4d, + DB_DATA_TYPE_UNIT_MODE_FLAG_MOD = 0x4e, + DB_DATA_TYPE_STRING_INT_PAIR = 0x4f, + DB_DATA_TYPE_BIT_BOOL = 0x50, + DB_DATA_TYPE_STL_VECTOR_PROJECTILE_CATEGORIES = 0x51, + DB_DATA_TYPE_COMBAT_MOD = 0x52, + DB_DATA_TYPE_MULTI_OBJECT_REFERENCE_WITH_OR = 0x53, + DB_DATA_TYPE_CONVERSION_PTR = 0x54, + DB_DATA_TYPE_CONVERSION_OBJECT_REF_VECTOR_PTR = 0x55, + DB_DATA_TYPE_CONVERSION_64 = 0x56 +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParserErrorEventArgs.cs similarity index 82% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParserErrorEventArgs.cs index 87ccce1..d8ff77b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParserErrorEventArgs.cs @@ -3,7 +3,7 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; -internal class XmlContainerParserErrorEventArgs(string file, XmlException? exception = null, bool isXmlFileList = false) +public class EngineXmlParserErrorEventArgs(string file, XmlException? exception = null, bool isXmlFileList = false) { public bool Continue { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs deleted file mode 100644 index 763d244..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.CommandBar.Xml; -using PG.StarWarsGame.Engine.Xml.Parsers.Data; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Xml.Parsers.File; - -internal class CommandBarComponentFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) -{ - protected override void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName) - { - var parser = new CommandBarComponentParser(parsedElements, ServiceProvider, ErrorReporter); - - if (!element.HasElements) - { - OnParseError(XmlParseErrorEventArgs.FromEmptyRoot(element)); - return; - } - - foreach (var xElement in element.Elements()) - { - var commandBarComponent = parser.Parse(xElement, out var nameCrc); - parsedElements.Add(nameCrc, commandBarComponent); - } - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameConstantsParser.cs similarity index 90% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameConstantsParser.cs index 64de5ec..8f2806f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameConstantsParser.cs @@ -4,7 +4,7 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; +namespace PG.StarWarsGame.Engine.Xml.Parsers.File; internal class GameConstantsParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileParser(serviceProvider, errorReporter) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs deleted file mode 100644 index ffeafff..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileParser.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.GameObjects; -using PG.StarWarsGame.Engine.Xml.Parsers.Data; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Xml.Parsers.File; - -internal class GameObjectFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) -{ - protected override void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName) - { - var parser = new GameObjectParser(parsedElements, ServiceProvider, ErrorReporter); - - foreach (var xElement in element.Elements()) - { - var gameObject = parser.Parse(xElement, out var nameCrc); - parsedElements.Add(nameCrc, gameObject); - } - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs deleted file mode 100644 index 841d805..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Audio.Sfx; -using PG.StarWarsGame.Engine.Xml.Parsers.Data; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Xml.Parsers.File; - -internal class SfxEventFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) -{ - protected override void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName) - { - var parser = new SfxEventParser(parsedElements, ServiceProvider, ErrorReporter); - - if (!element.HasElements) - { - OnParseError(XmlParseErrorEventArgs.FromEmptyRoot(element)); - return; - } - - foreach (var xElement in element.Elements()) - { - var sfxEvent = parser.Parse(xElement, out var nameCrc); - parsedElements.Add(nameCrc, sfxEvent); - } - - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs deleted file mode 100644 index e640c53..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Linq; -using System.Xml; -using AnakinRaW.CommonUtilities.Collections; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using PG.Commons.Hashing; -using PG.Commons.Services; -using PG.StarWarsGame.Engine.IO; -using PG.StarWarsGame.Files.XML; -using PG.StarWarsGame.Files.XML.Data; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Xml.Parsers; - -internal sealed class XmlContainerContentParser : ServiceBase, IPetroglyphXmlParserInfo -{ - public event EventHandler? XmlParseError; - - private readonly IXmlParserErrorReporter? _reporter; - private readonly IPetroglyphXmlFileParserFactory _fileParserFactory; - - public XmlContainerContentParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? reporter) : base(serviceProvider) - { - _reporter = reporter; - _fileParserFactory = serviceProvider.GetRequiredService(); - Name = GetType().FullName!; - } - - public string Name { get; } - - public void ParseEntriesFromFileListXml( - string xmlFile, - IGameRepository gameRepository, - string lookupPath, - FrugalValueListDictionary entries, - Action? onFileParseAction = null) where T : notnull - { - Logger.LogDebug("Parsing container data '{XmlFile}'", xmlFile); - - using var containerStream = gameRepository.TryOpenFile(xmlFile); - if (containerStream == null) - { - _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); - Logger.LogWarning("Could not find XML file '{XmlFile}'", xmlFile); - - var args = new XmlContainerParserErrorEventArgs(xmlFile, isXmlFileList: true) - { - // No reason to continue - Continue = false - }; - XmlParseError?.Invoke(this, args); - return; - } - - XmlFileList? container; - - try - { - var containerParser = new XmlFileListParser(Services, _reporter); - container = containerParser.ParseFile(containerStream); - if (container is null) - throw new XmlException($"Unable to parse XML container file '{xmlFile}'."); - } - catch (XmlException e) - { - _reporter?.Report(this, - new XmlParseErrorEventArgs(new XmlLocationInfo(xmlFile, e.LineNumber), XmlParseErrorKind.Unknown, e.Message)); - - var args = new XmlContainerParserErrorEventArgs(xmlFile, e, isXmlFileList: true) - { - // No reason to continue - Continue = false - }; - XmlParseError?.Invoke(this, args); - return; - } - - - var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); - - var parser = _fileParserFactory.CreateFileParser(_reporter); - - foreach (var file in xmlFiles) - { - if (onFileParseAction is not null) - onFileParseAction(file); - - using var fileStream = gameRepository.TryOpenFile(file); - - if (fileStream is null) - { - _reporter?.Report(parser, XmlParseErrorEventArgs.FromMissingFile(file)); - Logger.LogWarning("Could not find XML file '{File}'", file); - - var args = new XmlContainerParserErrorEventArgs(file, isXmlFileList: false); - XmlParseError?.Invoke(this, args); - - if (args.Continue) - continue; - return; - } - - Logger.LogDebug("Parsing File '{File}'", file); - - try - { - parser.ParseFile(fileStream, entries); - } - catch (XmlException e) - { - _reporter?.Report(parser, new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); - - var args = new XmlContainerParserErrorEventArgs(file, e, isXmlFileList: false); - XmlParseError?.Invoke(this, args); - - if (!args.Continue) - return; - } - } - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs index 1565e9e..1e3ea3a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs @@ -1,51 +1,24 @@ -using System; +using AnakinRaW.CommonUtilities.Collections; +using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; +using System; +using System.Text; using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; using PG.Commons.Hashing; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; +using Crc32 = PG.Commons.Hashing.Crc32; namespace PG.StarWarsGame.Engine.Xml.Parsers; -public abstract class XmlObjectParser( - IReadOnlyFrugalValueListDictionary parsedElements, - IServiceProvider serviceProvider, - IXmlParserErrorReporter? errorReporter = null) - : XmlObjectParser(parsedElements, serviceProvider, errorReporter) where TObject : XmlObject +internal abstract class XmlObjectParserBase(IXmlParserErrorReporter? errorReporter) + : PetroglyphXmlParserBase(errorReporter) + where TObject : XmlObject { - protected void Parse(TObject xmlObject, XElement element) - { - Parse(xmlObject, element, EmptyParseState.Instance); - } - - protected sealed override bool ParseTag(XElement tag, TObject xmlObject, in EmptyParseState parseState) + protected virtual void ValidateValues(TObject namedXmlObject, XElement element) { - return ParseTag(tag, xmlObject); } - protected abstract bool ParseTag(XElement tag, TObject xmlObject); -} - -public readonly struct EmptyParseState -{ - public static readonly EmptyParseState Instance = new(); -} - - -public abstract class XmlObjectParser( - IReadOnlyFrugalValueListDictionary parsedElements, - IServiceProvider serviceProvider, - IXmlParserErrorReporter? errorReporter = null) - : PetroglyphXmlElementParser(errorReporter) where TObject : XmlObject -{ - protected IReadOnlyFrugalValueListDictionary ParsedElements { get; } = - parsedElements ?? throw new ArgumentNullException(nameof(parsedElements)); - - protected ICrc32HashingService HashingService { get; } = serviceProvider.GetRequiredService(); - - public abstract TObject Parse(XElement element, out Crc32 crc32); - protected void Parse(TObject xmlObject, XElement element, in TParseState state) { foreach (var tag in element.Elements()) @@ -54,26 +27,74 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) { OnParseError(new XmlParseErrorEventArgs(tag, XmlParseErrorKind.UnknownNode, $"The node '{tag.Name}' is not supported.")); - break; } } } protected abstract bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState); +} + - protected string GetXmlObjectName(XElement element, out Crc32 crc32, bool uppercaseName) +internal abstract class NamedXmlObjectParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter) + : XmlObjectParserBase>(errorReporter), + IPetroglyphXmlNamedElementParser + where TObject : NamedXmlObject +{ + protected readonly ICrc32HashingService HashingService = serviceProvider.GetRequiredService(); + + public TObject Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc) + { + var name = GetXmlObjectName(element, true, out nameCrc); + var namedXmlObject = CreateXmlObject(name, nameCrc, element, XmlLocationInfo.FromElement(element)); + Parse(namedXmlObject, element, parsedEntries); + ValidateValues(namedXmlObject, element); + namedXmlObject.CoerceValues(); + return namedXmlObject; + } + + protected abstract TObject CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location); + + protected string GetXmlObjectName(XElement element, bool uppercaseName, out Crc32 crc32) { GetNameAttributeValue(element, out var name); crc32 = uppercaseName - ? HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.DefaultPGEncoding) - : HashingService.GetCrc32(name.AsSpan(), PGConstants.DefaultPGEncoding); + ? HashingService.GetCrc32Upper(name.AsSpan(), Encoding.ASCII) + : HashingService.GetCrc32(name.AsSpan(), Encoding.ASCII); if (crc32 == default) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Name for XmlObject cannot be empty.")); + "Name for XmlObject cannot be empty.")); } return name; } +} + +internal abstract class XmlObjectParser(IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParserBase(errorReporter), IPetroglyphXmlElementParser + where TObject : XmlObject +{ + public TObject Parse(XElement element) + { + var xmlObject = CreateXmlObject(XmlLocationInfo.FromElement(element)); + Parse(xmlObject, element, EmptyParseState.Instance); + ValidateValues(xmlObject, element); + xmlObject.CoerceValues(); + return xmlObject; + } + + protected abstract TObject CreateXmlObject(XmlLocationInfo location); + + protected sealed override bool ParseTag(XElement tag, TObject xmlObject, in EmptyParseState parseState) + { + return ParseTag(tag, xmlObject); + } + + protected abstract bool ParseTag(XElement tag, TObject xmlObject); +} + +internal readonly struct EmptyParseState +{ + public static readonly EmptyParseState Instance = new(); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs index 550f459..0817892 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs @@ -1,27 +1,33 @@ -using System; -using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.GameObjects; -using PG.StarWarsGame.Engine.Xml.Parsers.File; +using PG.StarWarsGame.Engine.Xml.Parsers.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; +using System; namespace PG.StarWarsGame.Engine.Xml; internal sealed class PetroglyphXmlFileParserFactory(IServiceProvider serviceProvider) : IPetroglyphXmlFileParserFactory { - public IPetroglyphXmlFileContainerParser CreateFileParser(IXmlParserErrorReporter? errorReporter) where T : notnull + public IPetroglyphXmlFileContainerParser CreateFileContainerParser(IXmlParserErrorReporter? errorReporter) where T : XmlObject { if (typeof(T) == typeof(SfxEvent)) - return (IPetroglyphXmlFileContainerParser) new SfxEventFileParser(serviceProvider, errorReporter); + return new PetroglyphXmlFileContainerParser( + serviceProvider, (IPetroglyphXmlNamedElementParser)new SfxEventParser(serviceProvider, errorReporter), + errorReporter); if (typeof(T) == typeof(CommandBarComponentData)) - return (IPetroglyphXmlFileContainerParser)new CommandBarComponentFileParser(serviceProvider, errorReporter); + return new PetroglyphXmlFileContainerParser( + serviceProvider, (IPetroglyphXmlNamedElementParser)new CommandBarComponentParser(serviceProvider, errorReporter), + errorReporter); if (typeof(T) == typeof(GameObject)) - return (IPetroglyphXmlFileContainerParser)new GameObjectFileParser(serviceProvider, errorReporter); + return new PetroglyphXmlFileContainerParser( + serviceProvider, (IPetroglyphXmlNamedElementParser)new GameObjectParser(serviceProvider, errorReporter), + errorReporter); - throw new NotImplementedException($"Unable to get parser for type {typeof(T)}"); + throw new ParserNotFoundException(typeof(T)); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs index 8553f33..9ea4b34 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs @@ -4,5 +4,7 @@ namespace PG.StarWarsGame.Files.XML.Data; public class XmlFileList(IReadOnlyList files) { + public static readonly XmlFileList Empty = new([]); + public IReadOnlyList Files { get; } = files; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs index 34dbd77..690bb0c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs @@ -4,7 +4,9 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParserInfo where T : notnull +public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParserInfo where T : notnull { + IPetroglyphXmlNamedElementParser ElementParser { get; } + void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs index 31aa052..51850de 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs @@ -4,5 +4,5 @@ namespace PG.StarWarsGame.Files.XML.Parsers; public interface IPetroglyphXmlFileParser : IPetroglyphXmlParserInfo where T : notnull { - T? ParseFile(Stream xmlStream); + T ParseFile(Stream xmlStream); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs index 6ffba48..bf8de55 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs @@ -23,7 +23,7 @@ public abstract class PetroglyphXmlFileParserBase(IServiceProvider serviceProvid protected virtual bool LoadLineInfo => true; - protected XElement? GetRootElement(Stream xmlStream, out string fileName) + protected XElement GetRootElement(Stream xmlStream, out string fileName) { fileName = GetStrippedFileName(xmlStream.GetFilePath()); @@ -32,11 +32,13 @@ public abstract class PetroglyphXmlFileParserBase(IServiceProvider serviceProvid SkipLeadingWhiteSpace(fileName, xmlStream); - var xmlReader = XmlReader.Create(xmlStream, new XmlReaderSettings + var asciiStreamReader = new StreamReader(xmlStream, XmlFileConstants.XmlEncoding, false, 1024, leaveOpen: true); + using var xmlReader = XmlReader.Create(asciiStreamReader, new XmlReaderSettings { IgnoreWhitespace = true, IgnoreComments = true, - IgnoreProcessingInstructions = true + IgnoreProcessingInstructions = true, + CloseInput = true }, fileName); var options = LoadOptions.SetBaseUri; @@ -44,7 +46,7 @@ public abstract class PetroglyphXmlFileParserBase(IServiceProvider serviceProvid options |= LoadOptions.SetLineInfo; var doc = XDocument.Load(xmlReader, options); - return doc.Root; + return doc.Root ?? throw new XmlException("No root node found."); } private string GetStrippedFileName(string filePath) @@ -63,7 +65,7 @@ private string GetStrippedFileName(string filePath) private void SkipLeadingWhiteSpace(string fileName, Stream stream) { - using var r = new StreamReader(stream, Encoding.ASCII, false, 10, true); + using var r = new StreamReader(stream, XmlFileConstants.XmlEncoding, false, 10, true); var count = 0; while (true) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs new file mode 100644 index 0000000..faec508 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs @@ -0,0 +1,10 @@ +using System.Xml.Linq; +using AnakinRaW.CommonUtilities.Collections; +using PG.Commons.Hashing; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public interface IPetroglyphXmlNamedElementParser : IPetroglyphXmlParserInfo where T : notnull +{ + T Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs index d2b862a..af67fa4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs @@ -1,21 +1,34 @@ -using System; -using System.IO; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; +using System.IO; namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphXmlFileContainerParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? listener = null) +public sealed class PetroglyphXmlFileContainerParser( + IServiceProvider serviceProvider, + IPetroglyphXmlNamedElementParser elementParser, + IXmlParserErrorReporter? listener = null) : PetroglyphXmlFileParserBase(serviceProvider, listener), IPetroglyphXmlFileContainerParser where T : notnull { + public IPetroglyphXmlNamedElementParser ElementParser { get; } = + elementParser ?? throw new ArgumentNullException(nameof(elementParser)); + public void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries) { - var root = GetRootElement(xmlStream, out var fileName); - if (root is not null) - Parse(root, parsedEntries, fileName); - } + var root = GetRootElement(xmlStream, out _); + + if (!root.HasElements) + { + OnParseError(XmlParseErrorEventArgs.FromEmptyRoot(root)); + return; + } - protected abstract void Parse(XElement element, IFrugalValueListDictionary parsedElements, string fileName); + foreach (var xElement in root.Elements()) + { + var parsedElement = ElementParser.Parse(xElement, parsedEntries, out var entryCrc); + parsedEntries.Add(entryCrc, parsedElement); + } + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs index 986cfef..7a5e581 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs @@ -8,16 +8,10 @@ namespace PG.StarWarsGame.Files.XML.Parsers; public abstract class PetroglyphXmlFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileParserBase(serviceProvider, errorReporter), IPetroglyphXmlFileParser where T : notnull { - public T? ParseFile(Stream xmlStream) + public T ParseFile(Stream xmlStream) { var root = GetRootElement(xmlStream, out var fileName); - if (root is null) - { - var location = new XmlLocationInfo(fileName, 0); - OnParseError(new XmlParseErrorEventArgs(location, XmlParseErrorKind.EmptyRoot, - "Unable to get root node from XML file.")); - } - return root is null ? default : Parse(root, fileName); + return Parse(root, fileName); } protected abstract T Parse(XElement element, string fileName); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs index 1d2a5b3..34e5317 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -17,7 +18,7 @@ private CommaSeparatedStringKeyValueListParser() private protected override IList<(string key, string value)> DefaultValue => []; - protected internal override IList<(string key, string value)> ParseCore(string trimmedValue, XElement element) + protected internal override IList<(string key, string value)> ParseCore(ReadOnlySpan trimmedValue, XElement element) { var values = element.Value.Split(','); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs index 0fd6fc1..8551867 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -1,4 +1,5 @@ -using System.Xml.Linq; +using System; +using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -22,9 +23,9 @@ public sealed override T Parse(XElement element) if (tagName.Length >= 256) ErrorReporter?.Report(this, new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, "A tag name cannot be null or empty.")); - var value = element.Value.Trim(); + var value = element.Value.AsSpan().Trim(); return value.Length == 0 ? DefaultValue : ParseCore(value, element); } - protected internal abstract T ParseCore(string trimmedValue, XElement element); + protected internal abstract T ParseCore(ReadOnlySpan trimmedValue, XElement element); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs index 8672d1b..e2d5a7a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs @@ -1,4 +1,5 @@ -using System.Xml.Linq; +using System; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -12,7 +13,7 @@ private PetroglyphXmlBooleanParser() private protected override bool DefaultValue => false; - protected internal override bool ParseCore(string trimmedValue, XElement element) + protected internal override bool ParseCore(ReadOnlySpan trimmedValue, XElement element) { // Yes! The engine only checks if the values is exact 1 or starts with Tt or Yy // At least it's efficient, I guess... diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs index aa7ba4b..7bab7ff 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs @@ -1,5 +1,6 @@ -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -13,7 +14,7 @@ private PetroglyphXmlByteParser() private protected override byte DefaultValue => 0; - protected internal override byte ParseCore(string trimmedValue, XElement element) + protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XElement element) { var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs index d3b4e85..ecad15f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs @@ -28,13 +28,17 @@ public float ParseAtLeast(XElement element, float minValue) return corrected; } - protected internal override float ParseCore(string trimmedValue, XElement element) + protected internal override float ParseCore(ReadOnlySpan trimmedValue, XElement element) { // The engine always loads FP numbers a long double and then converts that result to float - if (!double.TryParse(trimmedValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue)) + if (!double.TryParse(trimmedValue +#if NETSTANDARD2_0 + .ToString() +#endif + , NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue)) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, - $"Expected double but got value '{trimmedValue}'.")); + $"Expected double but got value '{trimmedValue.ToString()}'.")); return 0.0f; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs index fd0c0ec..8dcf79f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -1,6 +1,7 @@ -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Utilities; +using System; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -14,15 +15,20 @@ private PetroglyphXmlIntegerParser() { } - protected internal override int ParseCore(string trimmedValue, XElement element) + protected internal override int ParseCore(ReadOnlySpan trimmedValue, XElement element) { // The engines uses the C++ function std::atoi which is a little more loose. // For example the value '123d' get parsed to 123, // whereas in C# int.TryParse returns (false, 0) - if (!int.TryParse(trimmedValue, out var i)) + if (!int.TryParse(trimmedValue +#if NETSTANDARD2_0 + .ToString() +#endif + + , out var i)) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, - $"Expected integer but got '{trimmedValue}'.")); + $"Expected integer but got '{trimmedValue.ToString()}'.")); return 0; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs index d06f660..00be8f9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -25,7 +25,7 @@ private PetroglyphXmlLooseStringListParser() { } - protected internal override IList ParseCore(string trimmedValue, XElement element) + protected internal override IList ParseCore(ReadOnlySpan trimmedValue, XElement element) { if (trimmedValue.Length > 0x2000) { @@ -34,7 +34,7 @@ protected internal override IList ParseCore(string trimmedValue, XElemen return DefaultValue; } - var entries = trimmedValue.Split(Separators, StringSplitOptions.RemoveEmptyEntries); + var entries = trimmedValue.ToString().Split(Separators, StringSplitOptions.RemoveEmptyEntries); return entries; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs index ede64a3..8241164 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs @@ -1,6 +1,7 @@ -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Utilities; +using System; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -14,7 +15,7 @@ private PetroglyphXmlMax100ByteParser() { } - protected internal override byte ParseCore(string trimmedValue, XElement element) + protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XElement element) { var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs index 1ecf10a..8f32e50 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs @@ -14,7 +14,7 @@ private PetroglyphXmlRgbaColorParser() { } - protected internal override Vector4Int ParseCore(string trimmedValue, XElement element) + protected internal override Vector4Int ParseCore(ReadOnlySpan trimmedValue, XElement element) { var values = PetroglyphXmlLooseStringListParser.Instance.ParseCore(trimmedValue, element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs index dcca294..a0dde7f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs @@ -1,4 +1,5 @@ -using System.Xml.Linq; +using System; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -12,8 +13,8 @@ private PetroglyphXmlStringParser() private protected override string DefaultValue => string.Empty; - protected internal override string ParseCore(string trimmedValue, XElement element) + protected internal override string ParseCore(ReadOnlySpan trimmedValue, XElement element) { - return trimmedValue; + return trimmedValue.ToString(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs index 7061cca..02b9f2f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs @@ -1,5 +1,6 @@ -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -13,7 +14,7 @@ private PetroglyphXmlUnsignedIntegerParser() { } - protected internal override uint ParseCore(string trimmedValue, XElement element) + protected internal override uint ParseCore(ReadOnlySpan trimmedValue, XElement element) { var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs index 94900b7..da22a09 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using System; +using System.Numerics; using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -17,7 +18,7 @@ private PetroglyphXmlVector2FParser() private protected override Vector2 DefaultValue => default; - protected internal override Vector2 ParseCore(string trimmedValue, XElement element) + protected internal override Vector2 ParseCore(ReadOnlySpan trimmedValue, XElement element) { var listOfValues = LooseStringListParser.Parse(element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileConstants.cs new file mode 100644 index 0000000..fb0de39 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileConstants.cs @@ -0,0 +1,10 @@ +using System.Text; + +namespace PG.StarWarsGame.Files.XML; + +public static class XmlFileConstants +{ + public const int MaxTagNameLength = 255; + + public static readonly Encoding XmlEncoding = Encoding.ASCII; +} \ No newline at end of file From f3da8f84e32919f2f36a9a70484db901b02aa4c6 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 12:43:17 +0100 Subject: [PATCH 04/15] started reorganization --- src/ModVerify.CliApp/Program.cs | 2 +- .../Audio/Sfx/SfxEventGameManager.cs | 2 +- .../CommandBar/CommandBarGameManager.cs | 1 + .../GameObjects/GameObjectTypeGameManager.cs | 1 + .../GuiDialogGameManager_Initialization.cs | 2 +- .../PG.StarWarsGame.Engine.csproj.DotSettings | 4 +- .../PetroglyphEngineServiceContribution.cs | 1 + .../Xml/{Parsers => }/EngineXmlParser.cs | 83 +++++++------------ .../EngineXmlParserErrorEventArgs.cs | 2 +- .../Xml/IPetroglyphXmlFileParserFactory.cs | 9 -- .../Xml/NamedXmlObject.cs | 3 +- .../GameConstantsParser.cs | 2 +- .../{File => FileObjects}/GuiDialogParser.cs | 2 +- .../IPetroglyphXmlFileParserFactory.cs | 8 ++ .../CommandBarComponentParser.cs | 21 +++-- .../GameObjectParser.cs | 17 ++-- .../{Data => NamedObjects}/SfxEventParser.cs | 26 ++++-- .../{ => Parsers}/ParserNotFoundException.cs | 2 +- .../Xml/Parsers/PetroglyphXmlParserFactory.cs | 28 +++++++ .../Tags/CommandBarComponentTags.cs | 0 .../Tags/ComponentTextureKeyExtensions.cs | 0 .../Xml/{ => Parsers}/Tags/SfxEventXmlTags.cs | 0 .../Xml/PetroglyphXmlParserFactory.cs | 33 -------- .../Xml/{Parsers => }/XmlObjectParser.cs | 22 +++-- .../Base/PetroglyphXmlFileParserBase.cs | 1 - 25 files changed, 138 insertions(+), 134 deletions(-) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{Parsers => }/EngineXmlParser.cs (86%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{Parsers => }/EngineXmlParserErrorEventArgs.cs (93%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{File => FileObjects}/GameConstantsParser.cs (90%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{File => FileObjects}/GuiDialogParser.cs (97%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{Data => NamedObjects}/CommandBarComponentParser.cs (97%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{Data => NamedObjects}/GameObjectParser.cs (94%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/{Data => NamedObjects}/SfxEventParser.cs (94%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{ => Parsers}/ParserNotFoundException.cs (89%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{ => Parsers}/Tags/CommandBarComponentTags.cs (100%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{ => Parsers}/Tags/ComponentTextureKeyExtensions.cs (100%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{ => Parsers}/Tags/SfxEventXmlTags.cs (100%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{Parsers => }/XmlObjectParser.cs (77%) diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index fd698cc..788d784 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -15,7 +15,6 @@ using Microsoft.Extensions.Logging; using PG.Commons; using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Xml.Parsers; using PG.StarWarsGame.Files.ALO; using PG.StarWarsGame.Files.MEG; using PG.StarWarsGame.Files.MTD; @@ -35,6 +34,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using AET.ModVerify.App.Reporting; +using PG.StarWarsGame.Engine.Xml; using Testably.Abstractions; using ILogger = Serilog.ILogger; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index 72fecbb..89645d4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -7,7 +7,7 @@ using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Localization; -using PG.StarWarsGame.Engine.Xml.Parsers; +using PG.StarWarsGame.Engine.Xml; namespace PG.StarWarsGame.Engine.Audio.Sfx; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 4af4a86..b9e5848 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Threading.Tasks; using AnakinRaW.CommonUtilities.Collections; +using PG.StarWarsGame.Engine.Xml; namespace PG.StarWarsGame.Engine.CommandBar; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 7650185..aed5848 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Engine.Xml.Parsers; using System; using System.Linq; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index 68fdabf..f4b3267 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GuiDialog.Xml; -using PG.StarWarsGame.Engine.Xml.Parsers.File; +using PG.StarWarsGame.Engine.Xml.Parsers; using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.Binary; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings index 57b1fa3..89ac95d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings @@ -4,4 +4,6 @@ True True True - True \ No newline at end of file + True + True + True \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs index 0132d10..69ce035 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs @@ -2,6 +2,7 @@ using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs similarity index 86% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs index c93c773..4353d74 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs @@ -1,22 +1,31 @@ -using AnakinRaW.CommonUtilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities; using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.Commons.Hashing; using PG.Commons.Services; -using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.IO; +using PG.StarWarsGame.Engine.Xml.Parsers; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -namespace PG.StarWarsGame.Engine.Xml.Parsers; +namespace PG.StarWarsGame.Engine.Xml; + + +public sealed record EngineXmlParseSettings +{ + public bool InvalidFilesListXmlFailsInitialization { get; init; } = true; + + public bool InvalidContainerXmlFailsInitialization { get; init; } = false; +} + public sealed class EngineXmlParser : ServiceBase, IPetroglyphXmlParserInfo { @@ -26,7 +35,12 @@ public sealed class EngineXmlParser : ServiceBase, IPetroglyphXmlParserInfo private readonly IXmlParserErrorReporter? _reporter; private readonly IPetroglyphXmlFileParserFactory _fileParserFactory; - public EngineXmlParser(IGameRepository gameRepository, IServiceProvider serviceProvider, IXmlParserErrorReporter? reporter) + public string Name { get; } + + public EngineXmlParser( + IGameRepository gameRepository, + IServiceProvider serviceProvider, + IXmlParserErrorReporter? reporter) : base(serviceProvider) { _gameRepository = gameRepository; @@ -35,8 +49,6 @@ public EngineXmlParser(IGameRepository gameRepository, IServiceProvider serviceP Name = GetType().FullName!; } - public string Name { get; } - public XmlFileList ParseFileList(string xmlFile) { Logger.LogDebug("Parsing container data '{XmlFile}'", xmlFile); @@ -77,13 +89,14 @@ public void ParseEntriesFromFileListXml( string xmlFile, string lookupPath, FrugalValueListDictionary entries, - Action? onFileParseAction = null) where T : XmlObject + Action? onFileParseAction = null) where T : NamedXmlObject { var container = ParseFileList(xmlFile); var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); - var parser = _fileParserFactory.CreateFileContainerParser(_reporter); + var parser = new PetroglyphXmlFileContainerParser(Services, + _fileParserFactory.CreateNamedXmlObjectParser(_reporter), _reporter); foreach (var file in xmlFiles) { @@ -129,48 +142,16 @@ public bool ParseEntriesFromContainerFile( } } - -internal partial class XmlTagMapperDatabase -{ - private readonly IServiceProvider _serviceProvider; - - public XmlTagMapperDatabase(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - SfxEventMap = CreateSfxEventMap(); - } -} - -internal partial class XmlTagMapperDatabase -{ - private XmlTagMapper CreateSfxEventMap() - { - return new SfxEventXmlTagMapper(_serviceProvider); - } - - private sealed class SfxEventXmlTagMapper(IServiceProvider serviceProvider) : XmlTagMapper(serviceProvider) - { - protected override void BuildMappings() - { - AddMapping( - "OverlapTestName", - PetroglyphXmlStringParser.Instance.Parse, - (obj, val) => obj.OverlapTestName = val); - } - } -} - - -internal partial class XmlTagMapperDatabase +public interface IXmlTagMapper where TObject : XmlObject { - public XmlTagMapper SfxEventMap { get; } + bool TryParseEntry(XElement element, TObject target); } -internal abstract class XmlTagMapper where TClass : notnull +public abstract class XmlTagMapper : IXmlTagMapper where TObject : XmlObject { private const int MaxTagLength = 256; - private delegate void ParserValueAction(TClass target, XElement element); + private delegate void ParserValueAction(TObject target, XElement element); private readonly Dictionary _tagMappings = new(); private readonly ICrc32HashingService _crcService; @@ -187,7 +168,7 @@ protected XmlTagMapper(IServiceProvider serviceProvider) protected abstract void BuildMappings(); - protected void AddMapping(string tagName, Func parser, Action setter) + protected void AddMapping(string tagName, Func parser, Action setter) { ThrowHelper.ThrowIfNullOrEmpty(tagName); if (tagName.Length >= MaxTagLength) @@ -208,7 +189,7 @@ protected void AddMapping(string tagName, Func parser, }; } - public bool TryParseEntry(XElement element, TClass target) + public bool TryParseEntry(XElement element, TObject target) { var tagName = element.Name.LocalName; if (tagName.Length >= MaxTagLength) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParserErrorEventArgs.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs similarity index 93% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParserErrorEventArgs.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs index d8ff77b..8026804 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/EngineXmlParserErrorEventArgs.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Xml; -namespace PG.StarWarsGame.Engine.Xml.Parsers; +namespace PG.StarWarsGame.Engine.Xml; public class EngineXmlParserErrorEventArgs(string file, XmlException? exception = null, bool isXmlFileList = false) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs deleted file mode 100644 index b3cf950..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Xml; - -public interface IPetroglyphXmlFileParserFactory -{ - IPetroglyphXmlFileContainerParser CreateFileContainerParser(IXmlParserErrorReporter? errorReporter) where T : XmlObject; -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs index 7cf9c4e..6118f99 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs @@ -1,11 +1,10 @@ using System; -using PG.Commons.Data; using PG.Commons.Hashing; using PG.StarWarsGame.Files.XML; namespace PG.StarWarsGame.Engine.Xml; -public abstract class NamedXmlObject(string name, Crc32 nameCrc, XmlLocationInfo location) : XmlObject(location), IHasCrc32 +public abstract class NamedXmlObject(string name, Crc32 nameCrc, XmlLocationInfo location) : XmlObject(location) { public Crc32 Crc32 { get; } = nameCrc; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameConstantsParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs similarity index 90% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameConstantsParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs index 8f2806f..8100e2e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameConstantsParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs @@ -4,7 +4,7 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -namespace PG.StarWarsGame.Engine.Xml.Parsers.File; +namespace PG.StarWarsGame.Engine.Xml.Parsers; internal class GameConstantsParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileParser(serviceProvider, errorReporter) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs similarity index 97% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs index 851aa9f..d8c1191 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs @@ -7,7 +7,7 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -namespace PG.StarWarsGame.Engine.Xml.Parsers.File; +namespace PG.StarWarsGame.Engine.Xml.Parsers; internal class GuiDialogParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileParser(serviceProvider, errorReporter) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs new file mode 100644 index 0000000..c821e46 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs @@ -0,0 +1,8 @@ +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Engine.Xml.Parsers; + +internal interface IPetroglyphXmlFileParserFactory +{ + NamedXmlObjectParser CreateNamedXmlObjectParser(IXmlParserErrorReporter? errorReporter) where T : NamedXmlObject; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs similarity index 97% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs index e8f2cd2..8d4a285 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs @@ -1,20 +1,19 @@ -using AnakinRaW.CommonUtilities.Collections; +using System; +using System.Collections.ObjectModel; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -using System; -using System.Collections.ObjectModel; -using System.Xml.Linq; using Crc32 = PG.Commons.Hashing.Crc32; -namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; +namespace PG.StarWarsGame.Engine.Xml.Parsers; internal class CommandBarComponentParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : NamedXmlObjectParser(serviceProvider, errorReporter) + : NamedXmlObjectParser(serviceProvider, new CommandBarComponentDataXmlTagMapper(serviceProvider), errorReporter) { - protected override CommandBarComponentData CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location) { return new CommandBarComponentData(name, nameCrc, location); @@ -353,4 +352,12 @@ protected override void ValidateValues(CommandBarComponentData xmlData, XElement $"CommandbarComponent name '{xmlData.Name}' is too long.")); } } + + private sealed class CommandBarComponentDataXmlTagMapper(IServiceProvider serviceProvider) + : XmlTagMapper(serviceProvider) + { + protected override void BuildMappings() + { + } + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/GameObjectParser.cs similarity index 94% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/GameObjectParser.cs index 2b9126b..e28ff37 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/GameObjectParser.cs @@ -1,13 +1,13 @@ -using AnakinRaW.CommonUtilities.Collections; +using System; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -using System; -using System.Xml.Linq; using Crc32 = PG.Commons.Hashing.Crc32; -namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; +namespace PG.StarWarsGame.Engine.Xml.Parsers; public static class GameObjectXmlTags { @@ -26,7 +26,7 @@ public static class GameObjectXmlTags } internal class GameObjectParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : NamedXmlObjectParser(serviceProvider, errorReporter) + : NamedXmlObjectParser(serviceProvider, new GameObjectXmlTagMapper(serviceProvider), errorReporter) { protected override GameObject CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location) { @@ -135,4 +135,11 @@ private static GameObjectType EstimateType(string tagName) _ => GameObjectType.Unknown }; } + + private sealed class GameObjectXmlTagMapper(IServiceProvider serviceProvider) : XmlTagMapper(serviceProvider) + { + protected override void BuildMappings() + { + } + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs similarity index 94% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs index c59bc40..0d78cef 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs @@ -1,21 +1,18 @@ -using AnakinRaW.CommonUtilities.Collections; -using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.ObjectModel; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities; -namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; +namespace PG.StarWarsGame.Engine.Xml.Parsers; internal class SfxEventParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : NamedXmlObjectParser(serviceProvider, errorReporter) + : NamedXmlObjectParser(serviceProvider, new SfxEventXmlTagMapper(serviceProvider), errorReporter) { protected override SfxEvent CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location) { @@ -194,4 +191,15 @@ protected override bool ParseTag(XElement tag, SfxEvent sfxEvent, in IReadOnlyFr default: return false; } } + + private sealed class SfxEventXmlTagMapper(IServiceProvider serviceProvider) : XmlTagMapper(serviceProvider) + { + protected override void BuildMappings() + { + AddMapping( + "OverlapTestName", + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.OverlapTestName = val); + } + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/ParserNotFoundException.cs similarity index 89% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/ParserNotFoundException.cs index fd4fba3..79b5d0c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/ParserNotFoundException.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/ParserNotFoundException.cs @@ -1,6 +1,6 @@ using System; -namespace PG.StarWarsGame.Engine.Xml; +namespace PG.StarWarsGame.Engine.Xml.Parsers; internal sealed class ParserNotFoundException : Exception { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs new file mode 100644 index 0000000..152617f --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs @@ -0,0 +1,28 @@ +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.GameObjects; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; + +namespace PG.StarWarsGame.Engine.Xml.Parsers; + +internal sealed class PetroglyphXmlFileParserFactory(IServiceProvider serviceProvider) : IPetroglyphXmlFileParserFactory +{ + public NamedXmlObjectParser CreateNamedXmlObjectParser(IXmlParserErrorReporter? errorReporter) where T : NamedXmlObject + { + if (typeof(T) == typeof(SfxEvent)) + return ChangeType(new SfxEventParser(serviceProvider, errorReporter)); + if (typeof(T) == typeof(CommandBarComponentData)) + return ChangeType(new CommandBarComponentParser(serviceProvider, errorReporter)); + if (typeof(T) == typeof(GameObject)) + return ChangeType(new GameObjectParser(serviceProvider, errorReporter)); + + + throw new ParserNotFoundException(typeof(T)); + } + + private static NamedXmlObjectParser ChangeType(object obj) where T : NamedXmlObject + { + return (NamedXmlObjectParser) obj; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/CommandBarComponentTags.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/CommandBarComponentTags.cs similarity index 100% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/CommandBarComponentTags.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/CommandBarComponentTags.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/ComponentTextureKeyExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/ComponentTextureKeyExtensions.cs similarity index 100% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/ComponentTextureKeyExtensions.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/ComponentTextureKeyExtensions.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/SfxEventXmlTags.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/SfxEventXmlTags.cs similarity index 100% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/SfxEventXmlTags.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/SfxEventXmlTags.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs deleted file mode 100644 index 0817892..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -using PG.StarWarsGame.Engine.Audio.Sfx; -using PG.StarWarsGame.Engine.CommandBar.Xml; -using PG.StarWarsGame.Engine.GameObjects; -using PG.StarWarsGame.Engine.Xml.Parsers.Data; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; -using System; - -namespace PG.StarWarsGame.Engine.Xml; - -internal sealed class PetroglyphXmlFileParserFactory(IServiceProvider serviceProvider) : IPetroglyphXmlFileParserFactory -{ - public IPetroglyphXmlFileContainerParser CreateFileContainerParser(IXmlParserErrorReporter? errorReporter) where T : XmlObject - { - if (typeof(T) == typeof(SfxEvent)) - return new PetroglyphXmlFileContainerParser( - serviceProvider, (IPetroglyphXmlNamedElementParser)new SfxEventParser(serviceProvider, errorReporter), - errorReporter); - - if (typeof(T) == typeof(CommandBarComponentData)) - return new PetroglyphXmlFileContainerParser( - serviceProvider, (IPetroglyphXmlNamedElementParser)new CommandBarComponentParser(serviceProvider, errorReporter), - errorReporter); - - if (typeof(T) == typeof(GameObject)) - return new PetroglyphXmlFileContainerParser( - serviceProvider, (IPetroglyphXmlNamedElementParser)new GameObjectParser(serviceProvider, errorReporter), - errorReporter); - - - throw new ParserNotFoundException(typeof(T)); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs similarity index 77% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs index 1e3ea3a..9e6f045 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs @@ -9,12 +9,13 @@ using PG.Commons.Hashing; using Crc32 = PG.Commons.Hashing.Crc32; -namespace PG.StarWarsGame.Engine.Xml.Parsers; +namespace PG.StarWarsGame.Engine.Xml; -internal abstract class XmlObjectParserBase(IXmlParserErrorReporter? errorReporter) - : PetroglyphXmlParserBase(errorReporter) - where TObject : XmlObject +public abstract class XmlObjectParserBase(IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter) + : PetroglyphXmlParserBase(errorReporter) where TObject : XmlObject { + protected readonly IXmlTagMapper TagMapper = tagMapper ?? throw new ArgumentNullException(nameof(tagMapper)); + protected virtual void ValidateValues(TObject namedXmlObject, XElement element) { } @@ -35,8 +36,11 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) } -internal abstract class NamedXmlObjectParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter) - : XmlObjectParserBase>(errorReporter), +public abstract class NamedXmlObjectParser( + IServiceProvider serviceProvider, + IXmlTagMapper tagMapper, + IXmlParserErrorReporter? errorReporter) + : XmlObjectParserBase>(tagMapper, errorReporter), IPetroglyphXmlNamedElementParser where TObject : NamedXmlObject { @@ -71,8 +75,8 @@ protected string GetXmlObjectName(XElement element, bool uppercaseName, out Crc3 } } -internal abstract class XmlObjectParser(IXmlParserErrorReporter? errorReporter = null) - : XmlObjectParserBase(errorReporter), IPetroglyphXmlElementParser +public abstract class XmlObjectParser(IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParserBase(tagMapper, errorReporter), IPetroglyphXmlElementParser where TObject : XmlObject { public TObject Parse(XElement element) @@ -94,7 +98,7 @@ protected sealed override bool ParseTag(XElement tag, TObject xmlObject, in Empt protected abstract bool ParseTag(XElement tag, TObject xmlObject); } -internal readonly struct EmptyParseState +public readonly struct EmptyParseState { public static readonly EmptyParseState Instance = new(); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs index bf8de55..875341c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.IO.Abstractions; -using System.Text; using System.Xml; using System.Xml.Linq; using Microsoft.Extensions.DependencyInjection; From 3217a58952fc7d8c7619f8c3addcbf0e9b07889c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 14:21:54 +0100 Subject: [PATCH 05/15] new xml error reporting --- .../Engine/GameEngineErrorCollection.cs | 3 +- .../Engine/IGameEngineErrorCollection.cs | 1 + .../Reporting/Engine/XmlParseErrorReporter.cs | 1 - .../GameEngineErrorReporterWrapper.cs | 18 +--- .../IGameEngineErrorReporter.cs | 8 +- .../ErrorReporting/XmlError.cs | 19 ---- .../PetroglyphStarWarsGameEngineService.cs | 1 + .../Xml/EngineXmlParser.cs | 28 ++++-- .../Parsers/FileObjects/GuiDialogParser.cs | 16 +++- .../NamedObjects/CommandBarComponentParser.cs | 7 +- .../Parsers/NamedObjects/SfxEventParser.cs | 89 ++++++++++--------- .../Xml/XmlObjectParser.cs | 14 ++- .../ErrorHandling/IXmlParserErrorProvider.cs | 6 -- .../ErrorHandling/IXmlParserErrorReporter.cs | 6 +- .../PrimitiveXmlErrorReporter.cs | 13 +-- .../ErrorHandling/XmlError.cs | 21 +++++ .../ErrorHandling/XmlErrorEventHandler.cs | 5 -- .../ErrorHandling/XmlErrorReporter.cs | 16 ++-- .../ErrorHandling/XmlParseErrorEventArgs.cs | 42 --------- .../Base/PetroglyphXmlFileParserBase.cs | 25 +++++- .../Parsers/Base/PetroglyphXmlParserBase.cs | 14 +-- .../PetroglyphXmlFileContainerParser.cs | 6 -- .../PetroglyphPrimitiveXmlParser.cs | 18 +++- .../Primitives/PetroglyphXmlByteParser.cs | 7 +- .../Primitives/PetroglyphXmlFloatParser.cs | 14 ++- .../Primitives/PetroglyphXmlIntegerParser.cs | 14 ++- .../PetroglyphXmlLooseStringListParser.cs | 7 +- .../PetroglyphXmlMax100ByteParser.cs | 29 ++++-- .../PetroglyphXmlUnsignedIntegerParser.cs | 7 +- .../Parsers/XmlFileListParser.cs | 17 +++- 30 files changed, 251 insertions(+), 221 deletions(-) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorProvider.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlError.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorEventArgs.cs diff --git a/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs b/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs index ba0abf5..8cb67c0 100644 --- a/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs +++ b/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Files.XML.ErrorHandling; namespace AET.ModVerify.Reporting.Engine; @@ -17,7 +18,7 @@ public sealed class GameEngineErrorCollection : IGameEngineErrorCollection, IGam public IEnumerable Asserts => _asserts.ToList(); - void IGameEngineErrorReporter.Report(XmlError error) + void IXmlParserErrorReporter.Report(XmlError error) { _xmlErrors.Add(error); } diff --git a/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs b/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs index adf734d..286d047 100644 --- a/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs +++ b/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Files.XML.ErrorHandling; namespace AET.ModVerify.Reporting.Engine; diff --git a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs index 2629f26..b29fce0 100644 --- a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs @@ -4,7 +4,6 @@ using AET.ModVerify.Utilities; using AET.ModVerify.Verifiers; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Files.XML.ErrorHandling; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs index adf715a..320a2c7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs @@ -1,6 +1,5 @@ using System; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.ErrorReporting; @@ -17,7 +16,7 @@ public GameEngineErrorReporterWrapper(IGameEngineErrorReporter? errorReporter) _errorReporter = errorReporter; } - public void Report(XmlError error) + public override void Report(XmlError error) { _errorReporter?.Report(error); } @@ -32,19 +31,4 @@ public void Assert(EngineAssert assert) { _errorReporter?.Assert(assert); } - - public override void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) - { - if (_errorReporter is null) - return; - - Report(new XmlError - { - FileLocation = error.Location, - Parser = parser, - Message = error.Message, - ErrorKind = error.ErrorKind, - Element = error.Element - }); - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs index 1294fd1..933d3b5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs @@ -1,9 +1,9 @@ -namespace PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Files.XML.ErrorHandling; -public interface IGameEngineErrorReporter -{ - void Report(XmlError error); +namespace PG.StarWarsGame.Engine.ErrorReporting; +public interface IGameEngineErrorReporter : IXmlParserErrorReporter +{ void Report(InitializationError error); void Assert(EngineAssert assert); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs deleted file mode 100644 index e6124ec..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.ErrorReporting; - -public sealed class XmlError -{ - public required XmlLocationInfo FileLocation { get; init; } - - public required IPetroglyphXmlParserInfo Parser { get; init; } - - public XElement? Element { get; init; } - - public required XmlParseErrorKind ErrorKind { get; init; } - - public required string Message { get; init; } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs index 02a8d9f..42a2416 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs @@ -51,6 +51,7 @@ public async Task InitializeAsync( void OnInitializationError(object sender, InitializationError e) { + _logger?.LogWarning("Engine initialization failed for {Manager}: {Message}", e.GameManager, e.Message); if (cancelOnInitializationError) cts.Cancel(); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs index 4353d74..b37a5f7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs @@ -56,7 +56,11 @@ public XmlFileList ParseFileList(string xmlFile) using var containerStream = _gameRepository.TryOpenFile(xmlFile); if (containerStream == null) { - _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) + { + Message = "Xml file not found", + ErrorKind = XmlParseErrorKind.MissingFile + }); Logger.LogWarning("Could not find XML file '{XmlFile}'", xmlFile); var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: true); @@ -75,8 +79,12 @@ public XmlFileList ParseFileList(string xmlFile) } catch (XmlException e) { - _reporter?.Report(containerParser, new XmlParseErrorEventArgs(new XmlLocationInfo(xmlFile, e.LineNumber), - XmlParseErrorKind.Unknown, e.Message)); + _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) + { + ErrorKind = XmlParseErrorKind.Unknown, + Message = e.Message, + }); + var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: true); XmlParseError?.Invoke(this, args); return XmlFileList.Empty; @@ -115,7 +123,11 @@ public bool ParseEntriesFromContainerFile( if (fileStream is null) { - _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) + { + Message = "Xml file not found", + ErrorKind = XmlParseErrorKind.MissingFile + }); Logger.LogWarning("Could not find XML file '{File}'", xmlFile); var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: false); @@ -132,9 +144,11 @@ public bool ParseEntriesFromContainerFile( } catch (XmlException e) { - _reporter?.Report(parser, - new XmlParseErrorEventArgs(new XmlLocationInfo(xmlFile, 0), XmlParseErrorKind.Unknown, e.Message)); - + _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) + { + ErrorKind = XmlParseErrorKind.Unknown, + Message = e.Message, + }); var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: false); XmlParseError?.Invoke(this, args); return args.Continue; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs index d8c1191..4c59922 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs @@ -22,8 +22,11 @@ private GuiDialogsXmlTextureData ParseTextures(XElement? element, string fileNam { if (element is null) { - OnParseError(new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, null), XmlParseErrorKind.MissingNode, - "Expected node is missing.")); + ErrorReporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(fileName, null)) + { + ErrorKind = XmlParseErrorKind.MissingNode, + Message = "Expected node is missing." + }); return new GuiDialogsXmlTextureData([], new XmlLocationInfo(fileName, null)); } @@ -36,8 +39,13 @@ private GuiDialogsXmlTextureData ParseTextures(XElement? element, string fileNam textures.Add(ParseTexture(texture)); if (textures.Count == 0) - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MissingNode, - "Textures must contain at least one child node.")); + { + ErrorReporter?.Report(new XmlError(this, element) + { + Message = "Textures must contain at least one child node.", + ErrorKind = XmlParseErrorKind.MissingNode + }); + } return new GuiDialogsXmlTextureData(textures, XmlLocationInfo.FromElement(element)) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs index 8d4a285..2c74d99 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs @@ -348,8 +348,11 @@ protected override void ValidateValues(CommandBarComponentData xmlData, XElement { if (xmlData.Name.Length > PGConstants.MaxCommandBarComponentName) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, - $"CommandbarComponent name '{xmlData.Name}' is too long.")); + ErrorReporter?.Report(new XmlError(this, element) + { + Message = $"CommandbarComponent name '{xmlData.Name}' is too long.", + ErrorKind = XmlParseErrorKind.TooLongData + }); } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs index 0d78cef..e59ef80 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs @@ -21,47 +21,47 @@ protected override SfxEvent CreateXmlObject(string name, Crc32 nameCrc, XElement protected override void ValidateValues(SfxEvent sfxEvent, XElement element) { - if (sfxEvent.Name.Length > PGConstants.MaxSFXEventName) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, - $"SFXEvent name '{sfxEvent.Name}' is too long.")); - } - - if (sfxEvent is { Is2D: true, Is3D: true }) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"SFXEvent '{sfxEvent.Name}' is defined as 2D and 3D.")); - } - - if (sfxEvent.MinVolume > sfxEvent.MaxVolume) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"{SfxEventXmlTags.MinVolume} should not be higher than {SfxEventXmlTags.MaxVolume} for SFXEvent '{sfxEvent.Name}'")); - } - - if (sfxEvent.MinPitch > sfxEvent.MaxPitch) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"{SfxEventXmlTags.MinPitch} should not be higher than {SfxEventXmlTags.MaxPitch} for SFXEvent '{sfxEvent.Name}'")); - } - - if (sfxEvent.MinPan2D > sfxEvent.MaxPan2D) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"{SfxEventXmlTags.MinPan2D} should not be higher than {SfxEventXmlTags.MaxPan2D} for SFXEvent '{sfxEvent.Name}'")); - } - - if (sfxEvent.MinPredelay > sfxEvent.MaxPredelay) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"{SfxEventXmlTags.MinPredelay} should not be higher than {SfxEventXmlTags.MaxPredelay} for SFXEvent '{sfxEvent.Name}'")); - } - - if (sfxEvent.MinPostdelay > sfxEvent.MaxPostdelay) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"{SfxEventXmlTags.MinPostdelay} should not be higher than {SfxEventXmlTags.MaxPostdelay} for SFXEvent '{sfxEvent.Name}'")); - } + //if (sfxEvent.Name.Length > PGConstants.MaxSFXEventName) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, + // $"SFXEvent name '{sfxEvent.Name}' is too long.")); + //} + + //if (sfxEvent is { Is2D: true, Is3D: true }) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + // $"SFXEvent '{sfxEvent.Name}' is defined as 2D and 3D.")); + //} + + //if (sfxEvent.MinVolume > sfxEvent.MaxVolume) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + // $"{SfxEventXmlTags.MinVolume} should not be higher than {SfxEventXmlTags.MaxVolume} for SFXEvent '{sfxEvent.Name}'")); + //} + + //if (sfxEvent.MinPitch > sfxEvent.MaxPitch) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + // $"{SfxEventXmlTags.MinPitch} should not be higher than {SfxEventXmlTags.MaxPitch} for SFXEvent '{sfxEvent.Name}'")); + //} + + //if (sfxEvent.MinPan2D > sfxEvent.MaxPan2D) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + // $"{SfxEventXmlTags.MinPan2D} should not be higher than {SfxEventXmlTags.MaxPan2D} for SFXEvent '{sfxEvent.Name}'")); + //} + + //if (sfxEvent.MinPredelay > sfxEvent.MaxPredelay) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + // $"{SfxEventXmlTags.MinPredelay} should not be higher than {SfxEventXmlTags.MaxPredelay} for SFXEvent '{sfxEvent.Name}'")); + //} + + //if (sfxEvent.MinPostdelay > sfxEvent.MaxPostdelay) + //{ + // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + // $"{SfxEventXmlTags.MinPostdelay} should not be higher than {SfxEventXmlTags.MaxPostdelay} for SFXEvent '{sfxEvent.Name}'")); + //} } protected override bool ParseTag(XElement tag, SfxEvent sfxEvent, in IReadOnlyFrugalValueListDictionary parsedEntries) @@ -82,8 +82,11 @@ protected override bool ParseTag(XElement tag, SfxEvent sfxEvent, in IReadOnlyFr sfxEvent.ApplyPreset(preset); else { - OnParseError(new XmlParseErrorEventArgs(tag, - XmlParseErrorKind.MissingReference, $"Cannot to find preset '{presetName}' for SFXEvent '{sfxEvent.Name}'")); + ErrorReporter?.Report(new XmlError(this, tag) + { + Message = $"Cannot to find preset '{presetName}' for SFXEvent '{sfxEvent.Name}'", + ErrorKind = XmlParseErrorKind.MissingReference + }); } return true; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs index 9e6f045..158f15c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs @@ -26,8 +26,11 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) { if (!ParseTag(tag, xmlObject, state)) { - OnParseError(new XmlParseErrorEventArgs(tag, XmlParseErrorKind.UnknownNode, - $"The node '{tag.Name}' is not supported.")); + ErrorReporter?.Report(new XmlError(this, element) + { + Message = $"The node '{tag.Name}' is not supported.", + ErrorKind = XmlParseErrorKind.UnknownNode + }); } } } @@ -67,8 +70,11 @@ protected string GetXmlObjectName(XElement element, bool uppercaseName, out Crc3 if (crc32 == default) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - "Name for XmlObject cannot be empty.")); + ErrorReporter?.Report(new XmlError(this, element) + { + Message = "Name for XmlObject cannot be empty.", + ErrorKind = XmlParseErrorKind.InvalidValue + }); } return name; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorProvider.cs deleted file mode 100644 index 2470187..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorProvider.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -public interface IXmlParserErrorProvider -{ - event XmlErrorEventHandler XmlParseError; -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs index d1dceed..bddb691 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs @@ -1,8 +1,6 @@ -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Files.XML.ErrorHandling; +namespace PG.StarWarsGame.Files.XML.ErrorHandling; public interface IXmlParserErrorReporter { - void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error); + void Report(XmlError error); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs index fda7012..4d4bce2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs @@ -1,13 +1,14 @@ using System; -using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Files.XML.ErrorHandling; -internal sealed class PrimitiveXmlErrorReporter : IXmlParserErrorReporter, IXmlParserErrorProvider +internal sealed class PrimitiveXmlErrorReporter : IXmlParserErrorReporter { - public event XmlErrorEventHandler? XmlParseError; + internal delegate void XmlErrorEventHandler(XmlError error); - private static readonly Lazy LazyInstance = new(() => new PrimitiveXmlErrorReporter()); + public event XmlErrorEventHandler? PrimitiveParseError; + + private static readonly Lazy LazyInstance = new(() => new PrimitiveXmlErrorReporter(), true); public static PrimitiveXmlErrorReporter Instance => LazyInstance.Value; @@ -15,8 +16,8 @@ private PrimitiveXmlErrorReporter() { } - public void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) + public void Report(XmlError error) { - XmlParseError?.Invoke(parser, error); + PrimitiveParseError?.Invoke(error); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlError.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlError.cs new file mode 100644 index 0000000..bcdac81 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlError.cs @@ -0,0 +1,21 @@ +using System; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.Parsers; + +namespace PG.StarWarsGame.Files.XML.ErrorHandling; + +public sealed class XmlError( + IPetroglyphXmlParserInfo parser, + XElement? element = null, + XmlLocationInfo? locationInfo = null) +{ + public XmlLocationInfo FileLocation { get; } = locationInfo ?? (element is not null ? XmlLocationInfo.FromElement(element) : default); + + public IPetroglyphXmlParserInfo Parser { get; } = parser ?? throw new ArgumentNullException(nameof(parser)); + + public XElement? Element { get; } = element; + + public required XmlParseErrorKind ErrorKind { get; init; } + + public required string Message { get; init; } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs deleted file mode 100644 index 9e4821c..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs +++ /dev/null @@ -1,5 +0,0 @@ -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -public delegate void XmlErrorEventHandler(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error); \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs index 5369633..dd8734b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs @@ -1,29 +1,25 @@ using AnakinRaW.CommonUtilities; -using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Files.XML.ErrorHandling; -public class XmlErrorReporter : DisposableObject, IXmlParserErrorReporter, IXmlParserErrorProvider +public class XmlErrorReporter : DisposableObject, IXmlParserErrorReporter { - public event XmlErrorEventHandler? XmlParseError; - public XmlErrorReporter() { - PrimitiveXmlErrorReporter.Instance.XmlParseError += OnPrimitiveError; + PrimitiveXmlErrorReporter.Instance.PrimitiveParseError += OnPrimitiveError; } - public virtual void Report(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) + public virtual void Report(XmlError error) { - XmlParseError?.Invoke(parser, error); } protected override void DisposeResources() { - PrimitiveXmlErrorReporter.Instance.XmlParseError -= OnPrimitiveError; + PrimitiveXmlErrorReporter.Instance.PrimitiveParseError -= OnPrimitiveError; } - private void OnPrimitiveError(IPetroglyphXmlParserInfo parser, XmlParseErrorEventArgs error) + private void OnPrimitiveError(XmlError error) { - Report(parser, error); + Report(error); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorEventArgs.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorEventArgs.cs deleted file mode 100644 index afaa2ce..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorEventArgs.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities; - -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -public class XmlParseErrorEventArgs : EventArgs -{ - public XmlLocationInfo Location { get; } - - public XElement? Element { get; } - - public XmlParseErrorKind ErrorKind { get; } - - public string Message { get; } - - public XmlParseErrorEventArgs(XElement element, XmlParseErrorKind errorKind, string message) - { - Element = element ?? throw new ArgumentNullException(nameof(element)); - Location = XmlLocationInfo.FromElement(element); - ErrorKind = errorKind; - Message = message; - } - - public XmlParseErrorEventArgs(XmlLocationInfo location, XmlParseErrorKind errorKind, string message) - { - Location = location; - Message = message; - ErrorKind = errorKind; - } - - public static XmlParseErrorEventArgs FromMissingFile(string file) - { - ThrowHelper.ThrowIfNullOrEmpty(file); - return new XmlParseErrorEventArgs(new XmlLocationInfo(file, null), XmlParseErrorKind.MissingFile, "XML file not found."); - } - - public static XmlParseErrorEventArgs FromEmptyRoot(XElement element) - { - return new XmlParseErrorEventArgs(element, XmlParseErrorKind.EmptyRoot, "XML file has an empty root node."); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs index 875341c..178fece 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs @@ -45,7 +45,22 @@ protected XElement GetRootElement(Stream xmlStream, out string fileName) options |= LoadOptions.SetLineInfo; var doc = XDocument.Load(xmlReader, options); - return doc.Root ?? throw new XmlException("No root node found."); + + var root = doc.Root; + + if (root is null) + throw new XmlException("No root node found."); + + if (!root.HasElements) + { + ErrorReporter?.Report(new XmlError(this, root) + { + ErrorKind = XmlParseErrorKind.EmptyRoot, + Message = "XML file has an empty root node.", + }); + } + + return root; } private string GetStrippedFileName(string filePath) @@ -77,8 +92,12 @@ private void SkipLeadingWhiteSpace(string fileName, Stream stream) if (count != 0) { - OnParseError(new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, 0), - XmlParseErrorKind.DataBeforeHeader, $"XML header is not the first entry of the XML file."));} + ErrorReporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(fileName, 0)) + { + ErrorKind = XmlParseErrorKind.DataBeforeHeader, + Message = "XML header is not the first entry of the XML file.", + }); + } stream.Position = count; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs index 698ccb2..4e05ac1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs @@ -40,22 +40,22 @@ protected bool GetNameAttributeValue(XElement element, out string value) protected bool GetAttributeValue(XElement element, string attribute, out string? value, string? defaultValue = null) { + // In this engine, this is actually case-sensitive var nameAttribute = element.Attributes() .FirstOrDefault(a => a.Name.LocalName == attribute); - + if (nameAttribute is null) { value = defaultValue; - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MissingAttribute, $"Missing attribute '{attribute}'")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.MissingAttribute, + Message = $"Missing attribute '{attribute}'", + }); return false; } value = nameAttribute.Value; return true; } - - protected virtual void OnParseError(XmlParseErrorEventArgs error) - { - ErrorReporter?.Report(this, error); - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs index af67fa4..9441c6f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs @@ -19,12 +19,6 @@ public void ParseFile(Stream xmlStream, IFrugalValueListDictionary par { var root = GetRootElement(xmlStream, out _); - if (!root.HasElements) - { - OnParseError(XmlParseErrorEventArgs.FromEmptyRoot(root)); - return; - } - foreach (var xElement in root.Elements()) { var parsedElement = ElementParser.Parse(xElement, parsedEntries, out var entryCrc); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs index 8551867..198b4de 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -17,11 +17,23 @@ public sealed override T Parse(XElement element) var tagName = element.Name.LocalName; if (string.IsNullOrEmpty(tagName)) { - ErrorReporter?.Report(this, new XmlParseErrorEventArgs(element, XmlParseErrorKind.EmptyNodeName, "A tag name cannot be null or empty.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.EmptyNodeName, + Message = "A tag name cannot be null or empty.", + }); + return DefaultValue; + } + + if (tagName.Length > XmlFileConstants.MaxTagNameLength) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.TooLongData, + Message = $"A tag name can be only {XmlFileConstants.MaxTagNameLength} chars long.", + }); return DefaultValue; } - if (tagName.Length >= 256) - ErrorReporter?.Report(this, new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, "A tag name cannot be null or empty.")); var value = element.Value.AsSpan().Trim(); return value.Length == 0 ? DefaultValue : ParseCore(value, element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs index 7bab7ff..2be5d48 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs @@ -21,8 +21,11 @@ protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XEle var asByte = (byte)intValue; if (intValue != asByte) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 255) but got value '{intValue}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected a byte value (0 - 255) but got value '{intValue}'.", + }); } return asByte; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs index ecad15f..86ef413 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs @@ -21,8 +21,11 @@ public float ParseAtLeast(XElement element, float minValue) var corrected = Math.Max(value, minValue); if (corrected != value) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected float to be at least {minValue} but got value '{value}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected float to be at least {minValue} but got value '{value}'.", + }); } return corrected; @@ -37,8 +40,11 @@ protected internal override float ParseCore(ReadOnlySpan trimmedValue, XEl #endif , NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue)) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, - $"Expected double but got value '{trimmedValue.ToString()}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.MalformedValue, + Message = $"Expected double but got value '{trimmedValue.ToString()}'.", + }); return 0.0f; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs index 8dcf79f..9faf905 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -27,8 +27,11 @@ protected internal override int ParseCore(ReadOnlySpan trimmedValue, XElem , out var i)) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, - $"Expected integer but got '{trimmedValue.ToString()}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.MalformedValue, + Message = $"Expected integer but got '{trimmedValue.ToString()}'.", + }); return 0; } @@ -41,8 +44,11 @@ public int ParseWithRange(XElement element, int minValue, int maxValue) var clamped = PGMath.Clamp(value, minValue, maxValue); if (value != clamped) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected integer between {minValue} and {maxValue} but got value '{value}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected integer between {minValue} and {maxValue} but got value '{value}'.", + }); } return clamped; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs index 00be8f9..d6f1e3e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -29,8 +29,11 @@ protected internal override IList ParseCore(ReadOnlySpan trimmedVa { if (trimmedValue.Length > 0x2000) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, - $"Input value is too long '{trimmedValue.Length}' at {XmlLocationInfo.FromElement(element)}")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.TooLongData, + Message = $"Input value is too long '{trimmedValue.Length}' at {XmlLocationInfo.FromElement(element)}", + }); return DefaultValue; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs index 8241164..5d23e6d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs @@ -25,15 +25,21 @@ protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XEle var asByte = (byte)intValue; if (intValue != asByte) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 255) but got value '{intValue}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected a byte value (0 - 255) but got value '{intValue}'.", + }); } // Add additional check, cause the PG implementation is broken, but we need to stay "bug-compatible". if (asByte > 100) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 100) but got value '{asByte}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected a byte value (0 - 100) but got value '{asByte}'.", + }); } return asByte; @@ -43,9 +49,11 @@ public byte ParseWithRange(XElement element, byte minValue, byte maxValue) { if (maxValue > 100) { - OnParseError(new XmlParseErrorEventArgs( - element, XmlParseErrorKind.InvalidValue, - $"The provided maxValue '{maxValue}' is above 100.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"The provided maxValue '{maxValue}' is above 100.", + }); } // TODO: Do we need to coerce maxValue??? @@ -55,8 +63,11 @@ public byte ParseWithRange(XElement element, byte minValue, byte maxValue) var clamped = PGMath.Clamp(value, minValue, maxValue); if (value != clamped) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected byte between {minValue} and {maxValue} but got value '{value}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected byte between {minValue} and {maxValue} but got value '{value}'.", + }); } return clamped; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs index 02b9f2f..6c63f7e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs @@ -21,8 +21,11 @@ protected internal override uint ParseCore(ReadOnlySpan trimmedValue, XEle var asUint = (uint)intValue; if (intValue != asUint) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected unsigned integer but got '{intValue}'.")); + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected unsigned integer but got '{intValue}'.", + }); } return asUint; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index c148da2..311bd8c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -7,6 +7,9 @@ namespace PG.StarWarsGame.Files.XML.Parsers; + +// TODO: To XmlObjectParser + public sealed class XmlFileListParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : PetroglyphXmlFileParser(serviceProvider, errorReporter) { @@ -23,15 +26,21 @@ protected override XmlFileList Parse(XElement element, string fileName) var file = PetroglyphXmlStringParser.Instance.Parse(child); if (file.Length == 0) { - ErrorReporter?.Report(this, - new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, "Empty value in tag.")); + ErrorReporter?.Report(new XmlError(this, child) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = "Empty value in tag", + }); } files.Add(file); } else { - ErrorReporter?.Report(this, new XmlParseErrorEventArgs(child, XmlParseErrorKind.UnknownNode, - $"Tag '<{tagName}>' is not supported. Only '' is supported.")); + ErrorReporter?.Report(new XmlError(this, child) + { + ErrorKind = XmlParseErrorKind.UnknownNode, + Message = $"Tag '<{tagName}>' is not supported. Only '' is supported.", + }); } } return new XmlFileList(new ReadOnlyCollection(files)); From e0ca66e75324ef96f20d8894a9b9485d4eac2c0c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 15:47:11 +0100 Subject: [PATCH 06/15] refactor xml lib --- src/ModVerify.CliApp/Program.cs | 2 +- src/ModVerify/DefaultGameVerifiersProvider.cs | 10 +- .../Verifiers/DuplicateNameFinder.cs | 1 + .../Audio/Sfx/SfxEvent.cs | 4 +- .../CommandBar/Xml/CommandBarComponentData.cs | 4 +- .../GameConstants/GameConstantsXml.cs | 7 +- .../GameObjects/GameObject.cs | 1 + .../GuiDialog/Xml/GuiDialogsXml.cs | 1 + .../GuiDialog/Xml/GuiDialogsXmlTextureData.cs | 2 +- .../GuiDialog/Xml/XmlComponentTextureData.cs | 2 +- .../Xml/EngineXmlParser.cs | 81 +------------ .../FileObjects/GameConstantsParser.cs | 7 +- .../Parsers/FileObjects/GuiDialogParser.cs | 4 +- .../IPetroglyphXmlFileParserFactory.cs | 4 +- .../Xml/Parsers/PetroglyphXmlParserFactory.cs | 2 + .../Xml/XmlObjectParser.cs | 110 ------------------ .../Data}/NamedXmlObject.cs | 3 +- .../Data/XmlFileList.cs | 7 +- .../Data}/XmlObject.cs | 6 +- .../Base/IPetroglyphXmlElementParser.cs | 8 -- .../Base/IPetroglyphXmlFileContainerParser.cs | 12 -- .../Parsers/Base/IPetroglyphXmlFileParser.cs | 8 -- .../Parsers/Base/IXmlTagMapper.cs | 9 ++ .../Parsers/Base/XmlObjectParserBase.cs | 36 ++++++ .../Parsers/EmptyParseState.cs | 6 + .../IPetroglyphXmlNamedElementParser.cs | 10 -- .../Parsers/NamedXmlObjectParser.cs | 53 +++++++++ .../Parsers/PetroglyphXmlElementParser.cs | 10 -- .../PetroglyphPrimitiveXmlParser.cs | 4 +- ...nerParser.cs => XmlContainerFileParser.cs} | 9 +- .../Parsers/XmlFileListParser.cs | 13 +-- ...glyphXmlFileParser.cs => XmlFileParser.cs} | 13 ++- .../Parsers/XmlObjectParser.cs | 31 +++++ .../Parsers/XmlTagMapper.cs | 70 +++++++++++ 34 files changed, 267 insertions(+), 283 deletions(-) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs rename src/PetroglyphTools/{PG.StarWarsGame.Engine/Xml => PG.StarWarsGame.Files.XML/Data}/NamedXmlObject.cs (80%) rename src/PetroglyphTools/{PG.StarWarsGame.Engine/Xml => PG.StarWarsGame.Files.XML/Data}/XmlObject.cs (53%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IXmlTagMapper.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/EmptyParseState.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs rename src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/{PetroglyphXmlFileContainerParser.cs => XmlContainerFileParser.cs} (71%) rename src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/{PetroglyphXmlFileParser.cs => XmlFileParser.cs} (50%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlTagMapper.cs diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 788d784..ba5bd31 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -52,7 +52,7 @@ private static Task Main(string[] args) internal class Program : SelfUpdateableAppLifecycle { private static readonly string EngineParserNamespace = typeof(EngineXmlParser).Namespace!; - private static readonly string ParserNamespace = typeof(PetroglyphXmlFileParser<>).Namespace!; + private static readonly string ParserNamespace = typeof(XmlFileParser<>).Namespace!; private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}"); diff --git a/src/ModVerify/DefaultGameVerifiersProvider.cs b/src/ModVerify/DefaultGameVerifiersProvider.cs index e4bb0cd..b84ff87 100644 --- a/src/ModVerify/DefaultGameVerifiersProvider.cs +++ b/src/ModVerify/DefaultGameVerifiersProvider.cs @@ -15,10 +15,10 @@ public IEnumerable GetVerifiers( IServiceProvider serviceProvider) { yield break; - //yield return new ReferencedModelsVerifier(database, settings, serviceProvider); - //yield return new DuplicateNameFinder(database, settings, serviceProvider); - //yield return new AudioFilesVerifier(database, settings, serviceProvider); - //yield return new GuiDialogsVerifier(database, settings, serviceProvider); - //yield return new CommandBarVerifier(database, settings, serviceProvider); + yield return new ReferencedModelsVerifier(database, settings, serviceProvider); + yield return new DuplicateNameFinder(database, settings, serviceProvider); + yield return new AudioFilesVerifier(database, settings, serviceProvider); + yield return new GuiDialogsVerifier(database, settings, serviceProvider); + yield return new CommandBarVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 0b3b585..600f090 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -10,6 +10,7 @@ using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.MTD.Data; using PG.StarWarsGame.Files.MTD.Files; +using PG.StarWarsGame.Files.XML.Data; namespace AET.ModVerify.Verifiers; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs index aed53f2..b9d32a0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.Audio.Sfx; @@ -163,7 +163,7 @@ internal SfxEvent(string name, Crc32 nameCrc, XmlLocationInfo location) { } - internal override void CoerceValues() + public override void CoerceValues() { AdjustMinMaxValues(ref _minVolume, ref _maxVolume); AdjustMinMaxValues(ref _minPitch, ref _maxPitch); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs index 057745e..40ab0c9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs @@ -4,8 +4,8 @@ using System.Numerics; using PG.Commons.Hashing; using PG.Commons.Numerics; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.CommandBar.Xml; @@ -128,7 +128,7 @@ public sealed class CommandBarComponentData(string name, Crc32 crc, XmlLocationI public Vector4Int? TextColor2 { get; internal set; } public Vector4Int? MaxBarColor { get; internal set; } = WhiteColor; - internal override void CoerceValues() + public override void CoerceValues() { base.CoerceValues(); if (AlternateFontNames.Count == 0) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs index e98ed52..5eae395 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs @@ -1,3 +1,6 @@ -namespace PG.StarWarsGame.Engine.GameConstants; +using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; -public class GameConstantsXml; \ No newline at end of file +namespace PG.StarWarsGame.Engine.GameConstants; + +public class GameConstantsXml(XmlLocationInfo location) : XmlObject(location); \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs index de7ed01..f531df4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs @@ -4,6 +4,7 @@ using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.GameObjects; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs index 72cc5a0..7621c15 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs @@ -1,5 +1,6 @@ using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.GuiDialog.Xml; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs index 6496417..c5e56b8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.GuiDialog.Xml; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs index f102457..6b91547 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs @@ -1,7 +1,7 @@ using System; using AnakinRaW.CommonUtilities.Collections; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.GuiDialog.Xml; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs index b37a5f7..1dc4ce1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Xml; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities; using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -65,7 +62,7 @@ public XmlFileList ParseFileList(string xmlFile) var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: true); XmlParseError?.Invoke(this, args); - return XmlFileList.Empty; + return XmlFileList.Empty(new XmlLocationInfo(xmlFile, null)); } XmlFileList? container; @@ -87,7 +84,7 @@ public XmlFileList ParseFileList(string xmlFile) var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: true); XmlParseError?.Invoke(this, args); - return XmlFileList.Empty; + return XmlFileList.Empty(new XmlLocationInfo(xmlFile, null)); } return container; @@ -103,7 +100,7 @@ public void ParseEntriesFromFileListXml( var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); - var parser = new PetroglyphXmlFileContainerParser(Services, + var parser = new XmlContainerFileParser(Services, _fileParserFactory.CreateNamedXmlObjectParser(_reporter), _reporter); foreach (var file in xmlFiles) @@ -116,8 +113,8 @@ public void ParseEntriesFromFileListXml( public bool ParseEntriesFromContainerFile( string xmlFile, - IPetroglyphXmlFileContainerParser parser, - IFrugalValueListDictionary entries) where T : notnull + XmlContainerFileParser parser, + IFrugalValueListDictionary entries) where T : NamedXmlObject { using var fileStream = _gameRepository.TryOpenFile(xmlFile); @@ -156,74 +153,6 @@ public bool ParseEntriesFromContainerFile( } } -public interface IXmlTagMapper where TObject : XmlObject -{ - bool TryParseEntry(XElement element, TObject target); -} - -public abstract class XmlTagMapper : IXmlTagMapper where TObject : XmlObject -{ - private const int MaxTagLength = 256; - - private delegate void ParserValueAction(TObject target, XElement element); - - private readonly Dictionary _tagMappings = new(); - private readonly ICrc32HashingService _crcService; - - protected XmlTagMapper(IServiceProvider serviceProvider) - { - if (serviceProvider == null) - throw new ArgumentNullException(nameof(serviceProvider)); - _crcService = serviceProvider.GetRequiredService(); - - // ReSharper disable once VirtualMemberCallInConstructor - BuildMappings(); - } - - protected abstract void BuildMappings(); - - protected void AddMapping(string tagName, Func parser, Action setter) - { - ThrowHelper.ThrowIfNullOrEmpty(tagName); - if (tagName.Length >= MaxTagLength) - throw new ArgumentOutOfRangeException( - $"Tag name '{tagName}' exceeds maximum length of {MaxTagLength} characters", nameof(tagName)); - - if (parser == null) - throw new ArgumentNullException(nameof(parser)); - if (setter == null) - throw new ArgumentNullException(nameof(setter)); - - var crc = GetCrc32(tagName); - - _tagMappings[crc] = (target, element) => - { - var value = parser(element); - setter(target, value); - }; - } - - public bool TryParseEntry(XElement element, TObject target) - { - var tagName = element.Name.LocalName; - if (tagName.Length >= MaxTagLength) - return false; - - var crc = GetCrc32(tagName); - - if (!_tagMappings.TryGetValue(crc, out var mapping)) - return false; - - mapping(target, element); - return true; - } - - private Crc32 GetCrc32(string tagName) - { - return _crcService.GetCrc32Upper(tagName, PGConstants.DefaultPGEncoding); - } -} - enum XmlDataType { DB_DATA_TYPE_BOOL = 0x0, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs index 8100e2e..4c7e0d9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GameConstantsParser.cs @@ -1,16 +1,17 @@ using System; using System.Xml.Linq; using PG.StarWarsGame.Engine.GameConstants; +using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers; internal class GameConstantsParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : - PetroglyphXmlFileParser(serviceProvider, errorReporter) + XmlFileParser(serviceProvider, errorReporter) { - protected override GameConstantsXml Parse(XElement element, string fileName) + protected override GameConstantsXml ParseRoot(XElement element, string fileName) { - return new GameConstantsXml(); + return new GameConstantsXml(new XmlLocationInfo(fileName, null)); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs index 4c59922..e735dc3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs @@ -10,9 +10,9 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; internal class GuiDialogParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : - PetroglyphXmlFileParser(serviceProvider, errorReporter) + XmlFileParser(serviceProvider, errorReporter) { - protected override GuiDialogsXml Parse(XElement element, string fileName) + protected override GuiDialogsXml ParseRoot(XElement element, string fileName) { var textures = ParseTextures(element.Element("Textures"), fileName); return new GuiDialogsXml(textures, XmlLocationInfo.FromElement(element)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs index c821e46..c940274 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IPetroglyphXmlFileParserFactory.cs @@ -1,4 +1,6 @@ -using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs index 152617f..736b729 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/PetroglyphXmlParserFactory.cs @@ -3,6 +3,8 @@ using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML.ErrorHandling; using System; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs deleted file mode 100644 index 158f15c..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObjectParser.cs +++ /dev/null @@ -1,110 +0,0 @@ -using AnakinRaW.CommonUtilities.Collections; -using PG.StarWarsGame.Files.XML; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; -using System; -using System.Text; -using System.Xml.Linq; -using Microsoft.Extensions.DependencyInjection; -using PG.Commons.Hashing; -using Crc32 = PG.Commons.Hashing.Crc32; - -namespace PG.StarWarsGame.Engine.Xml; - -public abstract class XmlObjectParserBase(IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter) - : PetroglyphXmlParserBase(errorReporter) where TObject : XmlObject -{ - protected readonly IXmlTagMapper TagMapper = tagMapper ?? throw new ArgumentNullException(nameof(tagMapper)); - - protected virtual void ValidateValues(TObject namedXmlObject, XElement element) - { - } - - protected void Parse(TObject xmlObject, XElement element, in TParseState state) - { - foreach (var tag in element.Elements()) - { - if (!ParseTag(tag, xmlObject, state)) - { - ErrorReporter?.Report(new XmlError(this, element) - { - Message = $"The node '{tag.Name}' is not supported.", - ErrorKind = XmlParseErrorKind.UnknownNode - }); - } - } - } - - protected abstract bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState); -} - - -public abstract class NamedXmlObjectParser( - IServiceProvider serviceProvider, - IXmlTagMapper tagMapper, - IXmlParserErrorReporter? errorReporter) - : XmlObjectParserBase>(tagMapper, errorReporter), - IPetroglyphXmlNamedElementParser - where TObject : NamedXmlObject -{ - protected readonly ICrc32HashingService HashingService = serviceProvider.GetRequiredService(); - - public TObject Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc) - { - var name = GetXmlObjectName(element, true, out nameCrc); - var namedXmlObject = CreateXmlObject(name, nameCrc, element, XmlLocationInfo.FromElement(element)); - Parse(namedXmlObject, element, parsedEntries); - ValidateValues(namedXmlObject, element); - namedXmlObject.CoerceValues(); - return namedXmlObject; - } - - protected abstract TObject CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location); - - protected string GetXmlObjectName(XElement element, bool uppercaseName, out Crc32 crc32) - { - GetNameAttributeValue(element, out var name); - crc32 = uppercaseName - ? HashingService.GetCrc32Upper(name.AsSpan(), Encoding.ASCII) - : HashingService.GetCrc32(name.AsSpan(), Encoding.ASCII); - - if (crc32 == default) - { - ErrorReporter?.Report(new XmlError(this, element) - { - Message = "Name for XmlObject cannot be empty.", - ErrorKind = XmlParseErrorKind.InvalidValue - }); - } - - return name; - } -} - -public abstract class XmlObjectParser(IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter = null) - : XmlObjectParserBase(tagMapper, errorReporter), IPetroglyphXmlElementParser - where TObject : XmlObject -{ - public TObject Parse(XElement element) - { - var xmlObject = CreateXmlObject(XmlLocationInfo.FromElement(element)); - Parse(xmlObject, element, EmptyParseState.Instance); - ValidateValues(xmlObject, element); - xmlObject.CoerceValues(); - return xmlObject; - } - - protected abstract TObject CreateXmlObject(XmlLocationInfo location); - - protected sealed override bool ParseTag(XElement tag, TObject xmlObject, in EmptyParseState parseState) - { - return ParseTag(tag, xmlObject); - } - - protected abstract bool ParseTag(XElement tag, TObject xmlObject); -} - -public readonly struct EmptyParseState -{ - public static readonly EmptyParseState Instance = new(); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/NamedXmlObject.cs similarity index 80% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/NamedXmlObject.cs index 6118f99..2222633 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/NamedXmlObject.cs @@ -1,8 +1,7 @@ using System; using PG.Commons.Hashing; -using PG.StarWarsGame.Files.XML; -namespace PG.StarWarsGame.Engine.Xml; +namespace PG.StarWarsGame.Files.XML.Data; public abstract class NamedXmlObject(string name, Crc32 nameCrc, XmlLocationInfo location) : XmlObject(location) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs index 9ea4b34..aef7f43 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileList.cs @@ -2,9 +2,12 @@ namespace PG.StarWarsGame.Files.XML.Data; -public class XmlFileList(IReadOnlyList files) +public class XmlFileList(IReadOnlyList files, XmlLocationInfo location) : XmlObject(location) { - public static readonly XmlFileList Empty = new([]); + public static XmlFileList Empty(XmlLocationInfo location) + { + return new XmlFileList([], location); + } public IReadOnlyList Files { get; } = files; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs similarity index 53% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObject.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs index 71840fe..090d4d9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObject.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs @@ -1,12 +1,10 @@ -using PG.StarWarsGame.Files.XML; - -namespace PG.StarWarsGame.Engine.Xml; +namespace PG.StarWarsGame.Files.XML.Data; public abstract class XmlObject(XmlLocationInfo location) { public XmlLocationInfo Location { get; } = location; - internal virtual void CoerceValues() + public virtual void CoerceValues() { } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs deleted file mode 100644 index f5a6e95..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Xml.Linq; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlElementParser : IPetroglyphXmlParserInfo where T : notnull -{ - T Parse(XElement element); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs deleted file mode 100644 index 690bb0c..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.IO; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParserInfo where T : notnull -{ - IPetroglyphXmlNamedElementParser ElementParser { get; } - - void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs deleted file mode 100644 index 51850de..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.IO; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlFileParser : IPetroglyphXmlParserInfo where T : notnull -{ - T ParseFile(Stream xmlStream); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IXmlTagMapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IXmlTagMapper.cs new file mode 100644 index 0000000..5db7353 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IXmlTagMapper.cs @@ -0,0 +1,9 @@ +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.Data; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public interface IXmlTagMapper where T : XmlObject +{ + bool TryParseEntry(XElement element, T target); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs new file mode 100644 index 0000000..877ceab --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs @@ -0,0 +1,36 @@ +using System; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class XmlObjectParserBase(IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter) + : PetroglyphXmlParserBase(errorReporter) where TObject : XmlObject +{ + protected readonly IXmlTagMapper XmlTagMapper = tagMapper ?? throw new ArgumentNullException(nameof(tagMapper)); + + protected virtual void ValidateValues(TObject namedXmlObject, XElement element) + { + } + + protected void Parse(TObject xmlObject, XElement element, in TParseState state) + { + foreach (var tag in element.Elements()) + { + if (!ParseTag(tag, xmlObject, state)) + { + ErrorReporter?.Report(new XmlError(this, element) + { + Message = $"The node '{tag.Name}' is not supported.", + ErrorKind = XmlParseErrorKind.UnknownNode + }); + } + } + } + + protected virtual bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState) + { + return XmlTagMapper.TryParseEntry(tag, xmlObject); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/EmptyParseState.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/EmptyParseState.cs new file mode 100644 index 0000000..c8eac60 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/EmptyParseState.cs @@ -0,0 +1,6 @@ +namespace PG.StarWarsGame.Files.XML.Parsers; + +public readonly struct EmptyParseState +{ + public static readonly EmptyParseState Instance = new(); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs deleted file mode 100644 index faec508..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlNamedElementParser.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlNamedElementParser : IPetroglyphXmlParserInfo where T : notnull -{ - T Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs new file mode 100644 index 0000000..4b206af --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs @@ -0,0 +1,53 @@ +using System; +using System.Text; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities.Collections; +using Microsoft.Extensions.DependencyInjection; +using PG.Commons.Hashing; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class NamedXmlObjectParser( + IServiceProvider serviceProvider, + IXmlTagMapper tagMapper, + IXmlParserErrorReporter? errorReporter) + : XmlObjectParserBase>(tagMapper, errorReporter) + where TObject : NamedXmlObject +{ + protected virtual bool UpperCaseNameForCrc => true; + + protected readonly ICrc32HashingService HashingService = serviceProvider.GetRequiredService(); + + public TObject Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc) + { + var name = GetXmlObjectName(element, out nameCrc); + var namedXmlObject = CreateXmlObject(name, nameCrc, element, XmlLocationInfo.FromElement(element)); + Parse(namedXmlObject, element, parsedEntries); + ValidateValues(namedXmlObject, element); + namedXmlObject.CoerceValues(); + return namedXmlObject; + } + + protected abstract TObject CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location); + + private string GetXmlObjectName(XElement element, out Crc32 crc32) + { + GetNameAttributeValue(element, out var name); + crc32 = UpperCaseNameForCrc + ? HashingService.GetCrc32Upper(name.AsSpan(), Encoding.ASCII) + : HashingService.GetCrc32(name.AsSpan(), Encoding.ASCII); + + if (crc32 == default) + { + ErrorReporter?.Report(new XmlError(this, element) + { + Message = $"Name for XML object of type {typeof(TObject).Name} cannot be empty.", + ErrorKind = XmlParseErrorKind.InvalidValue + }); + } + + return name; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs deleted file mode 100644 index 5fa2c6d..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public abstract class PetroglyphXmlElementParser(IXmlParserErrorReporter? errorReporter = null) - : PetroglyphXmlParserBase(errorReporter), IPetroglyphXmlElementParser where T : notnull -{ - public abstract T Parse(XElement element); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs index 198b4de..ddd3b24 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -4,7 +4,7 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphPrimitiveXmlParser : PetroglyphXmlElementParser where T : notnull +public abstract class PetroglyphPrimitiveXmlParser : PetroglyphXmlParserBase where T : notnull { private protected abstract T DefaultValue { get; } @@ -12,7 +12,7 @@ private protected PetroglyphPrimitiveXmlParser() : base(PrimitiveXmlErrorReporte { } - public sealed override T Parse(XElement element) + public T Parse(XElement element) { var tagName = element.Name.LocalName; if (string.IsNullOrEmpty(tagName)) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlContainerFileParser.cs similarity index 71% rename from src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlContainerFileParser.cs index 9441c6f..12f0f5d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlContainerFileParser.cs @@ -3,16 +3,17 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; using System; using System.IO; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Files.XML.Parsers; -public sealed class PetroglyphXmlFileContainerParser( +public sealed class XmlContainerFileParser( IServiceProvider serviceProvider, - IPetroglyphXmlNamedElementParser elementParser, + NamedXmlObjectParser elementParser, IXmlParserErrorReporter? listener = null) - : PetroglyphXmlFileParserBase(serviceProvider, listener), IPetroglyphXmlFileContainerParser where T : notnull + : PetroglyphXmlFileParserBase(serviceProvider, listener) where T : NamedXmlObject { - public IPetroglyphXmlNamedElementParser ElementParser { get; } = + public NamedXmlObjectParser ElementParser { get; } = elementParser ?? throw new ArgumentNullException(nameof(elementParser)); public void ParseFile(Stream xmlStream, IFrugalValueListDictionary parsedEntries) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index 311bd8c..d96d497 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -7,15 +7,10 @@ namespace PG.StarWarsGame.Files.XML.Parsers; - -// TODO: To XmlObjectParser - public sealed class XmlFileListParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : - PetroglyphXmlFileParser(serviceProvider, errorReporter) -{ - protected override bool LoadLineInfo => false; - - protected override XmlFileList Parse(XElement element, string fileName) + XmlFileParser(serviceProvider, errorReporter) +{ + protected override XmlFileList ParseRoot(XElement element, string fileName) { var files = new List(); foreach (var child in element.Elements()) @@ -43,6 +38,6 @@ protected override XmlFileList Parse(XElement element, string fileName) }); } } - return new XmlFileList(new ReadOnlyCollection(files)); + return new XmlFileList(new ReadOnlyCollection(files), new XmlLocationInfo(fileName, null)); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileParser.cs similarity index 50% rename from src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileParser.cs index 7a5e581..8890a61 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileParser.cs @@ -1,18 +1,19 @@ -using System; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; using System.IO; using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphXmlFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) - : PetroglyphXmlFileParserBase(serviceProvider, errorReporter), IPetroglyphXmlFileParser where T : notnull +public abstract class XmlFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlFileParserBase(serviceProvider, errorReporter) where T : XmlObject { public T ParseFile(Stream xmlStream) { var root = GetRootElement(xmlStream, out var fileName); - return Parse(root, fileName); + return ParseRoot(root, fileName); } - protected abstract T Parse(XElement element, string fileName); + protected abstract T ParseRoot(XElement element, string fileName); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs new file mode 100644 index 0000000..c716a6e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs @@ -0,0 +1,31 @@ +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System.Xml.Linq; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class XmlObjectParser(IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParserBase(tagMapper, errorReporter) + where TObject : XmlObject +{ + public TObject Parse(XElement element) + { + var xmlObject = CreateXmlObject(XmlLocationInfo.FromElement(element)); + Parse(xmlObject, element, EmptyParseState.Instance); + ValidateValues(xmlObject, element); + xmlObject.CoerceValues(); + return xmlObject; + } + + protected abstract TObject CreateXmlObject(XmlLocationInfo location); + + protected sealed override bool ParseTag(XElement tag, TObject xmlObject, in EmptyParseState parseState) + { + return ParseTag(tag, xmlObject); + } + + protected virtual bool ParseTag(XElement tag, TObject xmlObject) + { + return XmlTagMapper.TryParseEntry(tag, xmlObject); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlTagMapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlTagMapper.cs new file mode 100644 index 0000000..366ccaf --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlTagMapper.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using AnakinRaW.CommonUtilities; +using Microsoft.Extensions.DependencyInjection; +using PG.Commons.Hashing; +using PG.StarWarsGame.Files.XML.Data; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class XmlTagMapper : IXmlTagMapper where TObject : XmlObject +{ + private delegate void ParserValueAction(TObject target, XElement element); + + private readonly Dictionary _tagMappings = new(); + private readonly ICrc32HashingService _crcService; + + protected XmlTagMapper(IServiceProvider serviceProvider) + { + if (serviceProvider == null) + throw new ArgumentNullException(nameof(serviceProvider)); + _crcService = serviceProvider.GetRequiredService(); + + // ReSharper disable once VirtualMemberCallInConstructor + BuildMappings(); + } + + protected abstract void BuildMappings(); + + protected void AddMapping(string tagName, Func parser, Action setter) + { + ThrowHelper.ThrowIfNullOrEmpty(tagName); + if (tagName.Length >= XmlFileConstants.MaxTagNameLength) + throw new ArgumentOutOfRangeException( + $"Tag name '{tagName}' exceeds maximum length of {XmlFileConstants.MaxTagNameLength} characters", nameof(tagName)); + + if (parser == null) + throw new ArgumentNullException(nameof(parser)); + if (setter == null) + throw new ArgumentNullException(nameof(setter)); + + var crc = GetCrc32(tagName); + + _tagMappings[crc] = (target, element) => + { + var value = parser(element); + setter(target, value); + }; + } + + public bool TryParseEntry(XElement element, TObject target) + { + var tagName = element.Name.LocalName; + if (tagName.Length >= XmlFileConstants.MaxTagNameLength) + return false; + + var crc = GetCrc32(tagName); + + if (!_tagMappings.TryGetValue(crc, out var mapping)) + return false; + + mapping(target, element); + return true; + } + + private Crc32 GetCrc32(string tagName) + { + return _crcService.GetCrc32Upper(tagName, XmlFileConstants.XmlEncoding); + } +} \ No newline at end of file From 37353f8bd2ff1e1bc69140c791838852b3d04c6c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 16:16:56 +0100 Subject: [PATCH 07/15] refactor --- .../Audio/Sfx/SfxEventGameManager.cs | 45 +++------ .../CommandBar/CommandBarGameManager.cs | 48 +++------- .../GameEngineErrorReporterWrapper.cs | 18 +--- .../GameObjects/GameObjectTypeGameManager.cs | 87 +++++++---------- .../Xml/EngineXmlParserErrorEventArgs.cs | 25 ----- .../PetroglyphStarWarsGameXmlParseSettings.cs | 10 ++ ....cs => PetroglyphStarWarsGameXmlParser.cs} | 93 ++++++++++++------- 7 files changed, 131 insertions(+), 195 deletions(-) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/{EngineXmlParser.cs => PetroglyphStarWarsGameXmlParser.cs} (75%) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index 89645d4..46741bc 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -24,41 +24,20 @@ protected override async Task InitializeCoreAsync(CancellationToken token) Logger?.LogInformation("Parsing SFXEvents..."); - var contentParser = new EngineXmlParser(GameRepository, ServiceProvider, ErrorReporter); - contentParser.XmlParseError += OnParseError; - try - { - await Task.Run(() => contentParser.ParseEntriesFromFileListXml( - "DATA\\XML\\SFXEventFiles.XML", - "DATA\\XML", - NamedEntries, - VerifyFilePathLength), - token); - } - finally - { - contentParser.XmlParseError -= OnParseError; - } - } - - private void OnParseError(object sender, EngineXmlParserErrorEventArgs e) - { - if (e.ErrorInXmlFileList || e.HasException) - { - e.Continue = false; - ErrorReporter.Report(new InitializationError + var contentParser = new PetroglyphStarWarsGameXmlParser(GameRepository, + new PetroglyphStarWarsGameXmlParseSettings { GameManager = ToString(), - Message = GetMessage(e) - }); - } - } - - private static string GetMessage(EngineXmlParserErrorEventArgs errorEventArgs) - { - if (errorEventArgs.HasException) - return $"Error while parsing SFXEvent XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; - return "Could not find SFXEventFiles.xml"; + InvalidContainerXmlFailsInitialization = true, + InvalidFilesListXmlFailsInitialization = true + }, ServiceProvider, ErrorReporter); + + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( + "DATA\\XML\\SFXEventFiles.XML", + "DATA\\XML", + NamedEntries, + VerifyFilePathLength), + token); } private void VerifyFilePathLength(string filePath) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index b9e5848..4ad531f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -8,7 +8,6 @@ using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; -using PG.StarWarsGame.Engine.Xml.Parsers; using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.MTD.Files; using PG.StarWarsGame.Files.MTD.Services; @@ -75,24 +74,21 @@ protected override async Task InitializeCoreAsync(CancellationToken token) { Logger?.LogInformation("Creating command bar components..."); - var contentParser = new EngineXmlParser(GameRepository, ServiceProvider, ErrorReporter); - contentParser.XmlParseError += OnParseError; + var contentParser = new PetroglyphStarWarsGameXmlParser(GameRepository, new PetroglyphStarWarsGameXmlParseSettings + { + GameManager = ToString(), + InvalidContainerXmlFailsInitialization = true, + InvalidFilesListXmlFailsInitialization = true + }, ServiceProvider, ErrorReporter); var parsedCommandBarComponents = new FrugalValueListDictionary(); - try - { - await Task.Run(() => contentParser.ParseEntriesFromFileListXml( - "DATA\\XML\\CommandBarComponentFiles.XML", - ".\\DATA\\XML", - parsedCommandBarComponents, - VerifyFilePathLength), - token); - } - finally - { - contentParser.XmlParseError -= OnParseError; - } + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( + "DATA\\XML\\CommandBarComponentFiles.XML", + ".\\DATA\\XML", + parsedCommandBarComponents, + VerifyFilePathLength), + token); // Create Scene // Create Camera @@ -259,26 +255,6 @@ private void SetComponentGroup(IEnumerable components) } } - private void OnParseError(object sender, EngineXmlParserErrorEventArgs e) - { - if (e.ErrorInXmlFileList || e.HasException) - { - e.Continue = false; - ErrorReporter.Report(new InitializationError - { - GameManager = ToString(), - Message = GetMessage(e) - }); - } - } - - private static string GetMessage(EngineXmlParserErrorEventArgs errorEventArgs) - { - if (errorEventArgs.HasException) - return $"Error while parsing CommandBar XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; - return "Could not find CommandBarComponentFiles.xml"; - } - private void VerifyFilePathLength(string filePath) { if (filePath.Length > PGConstants.MaxCommandBarDatabaseFileName) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs index 320a2c7..a8280cc 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs @@ -3,32 +3,24 @@ namespace PG.StarWarsGame.Engine.ErrorReporting; -internal sealed class GameEngineErrorReporterWrapper : XmlErrorReporter, IGameEngineErrorReporter +internal sealed class GameEngineErrorReporterWrapper(IGameEngineErrorReporter? errorReporter) + : XmlErrorReporter, IGameEngineErrorReporter { internal event EventHandler? InitializationError; - private readonly IGameEngineErrorReporter? _errorReporter; - - public GameEngineErrorReporterWrapper(IGameEngineErrorReporter? errorReporter) - { - if (errorReporter is null) - return; - _errorReporter = errorReporter; - } - public override void Report(XmlError error) { - _errorReporter?.Report(error); + errorReporter?.Report(error); } public void Report(InitializationError error) { InitializationError?.Invoke(this, error); - _errorReporter?.Report(error); + errorReporter?.Report(error); } public void Assert(EngineAssert assert) { - _errorReporter?.Assert(assert); + errorReporter?.Assert(assert); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index aed5848..7652704 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -22,48 +22,47 @@ protected override async Task InitializeCoreAsync(CancellationToken token) private void ParseGameObjectDatabases() { - var parser = new EngineXmlParser(GameRepository, ServiceProvider, ErrorReporter); - parser.XmlParseError += OnParseError; - try - { - var xmlFileList = parser.ParseFileList(@"DATA\XML\GAMEOBJECTFILES.XML").Files - .Select(x => - { - var filePath = FileSystem.Path.Combine(@".\DATA\XML\", x); - VerifyFilePathLength(filePath); - return filePath; - }).ToList(); + var parser = new PetroglyphStarWarsGameXmlParser(GameRepository, + new PetroglyphStarWarsGameXmlParseSettings + { + GameManager = ToString(), + InvalidFilesListXmlFailsInitialization = true, + InvalidContainerXmlFailsInitialization = false, + }, ServiceProvider, ErrorReporter); + + var xmlFileList = parser.ParseFileList(@"DATA\XML\GAMEOBJECTFILES.XML").Files + .Select(x => + { + var filePath = FileSystem.Path.Combine(@".\DATA\XML\", x); + VerifyFilePathLength(filePath); + return filePath; + }).ToList(); + + //var gameObjectFileParser = new GameObjectFileParser(serviceProvider, errorReporter); - //var gameObjectFileParser = new GameObjectFileParser(serviceProvider, errorReporter); - - var allLoaded = false; - for (var passNumber = 0; !allLoaded && passNumber < 10; passNumber++) + var allLoaded = false; + for (var passNumber = 0; !allLoaded && passNumber < 10; passNumber++) + { + foreach (var gameObjectXmlFile in xmlFileList) { - foreach (var gameObjectXmlFile in xmlFileList) + if (passNumber == 0) + { + //ParseSingleGameObjectFile(gameObjectXmlFile, parser, gameObjectFileParser); + } + else { - if (passNumber == 0) - { - //ParseSingleGameObjectFile(gameObjectXmlFile, parser, gameObjectFileParser); - } - else - { - } } + } - //GameObjectTypeClass::Static_Post_Load_Fixup(); - //SFXEventReferenceClass::Static_Post_Load_Fixup(); - //SpeechEventReferenceClass::Static_Post_Load_Fixup(); - //MusicEventReferenceClass::Static_Post_Load_Fixup(); - //FactionReferenceClass::Static_Post_Load_Fixup(); - //... - } - } - finally - { - parser.XmlParseError -= OnParseError; + //GameObjectTypeClass::Static_Post_Load_Fixup(); + //SFXEventReferenceClass::Static_Post_Load_Fixup(); + //SpeechEventReferenceClass::Static_Post_Load_Fixup(); + //MusicEventReferenceClass::Static_Post_Load_Fixup(); + //FactionReferenceClass::Static_Post_Load_Fixup(); + //... } } @@ -72,26 +71,6 @@ private void ParseGameObjectDatabases() // engineParser.ParseEntriesFromContainerFile(gameObjectFileParser, file, NamedEntries); //} - - private void OnParseError(object sender, EngineXmlParserErrorEventArgs e) - { - if (e.ErrorInXmlFileList) - { - ErrorReporter.Report(new InitializationError - { - GameManager = ToString(), - Message = GetMessage(e) - }); - } - } - - private static string GetMessage(EngineXmlParserErrorEventArgs errorEventArgs) - { - if (errorEventArgs.HasException) - return $"Error while parsing XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; - return "Could not find GameObjectFiles.xml"; - } - private void VerifyFilePathLength(string filePath) { if (filePath.Length > PGConstants.MaxGameObjectDatabaseFileName) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs deleted file mode 100644 index 8026804..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParserErrorEventArgs.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Xml; - -namespace PG.StarWarsGame.Engine.Xml; - -public class EngineXmlParserErrorEventArgs(string file, XmlException? exception = null, bool isXmlFileList = false) -{ - public bool Continue - { - get; - // Once this is set to false, there is no way back. - set => field &= value; - } = true; - - public bool ErrorInXmlFileList { get; } = isXmlFileList; - - public string File { get; } = file; - - [MemberNotNullWhen(true, nameof(Exception))] - public bool HasException => Exception is not null; - - public bool IsFileNotFound => !HasException; - - public XmlException? Exception { get; } = exception; -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs new file mode 100644 index 0000000..c3827fa --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs @@ -0,0 +1,10 @@ +namespace PG.StarWarsGame.Engine.Xml; + +public sealed record PetroglyphStarWarsGameXmlParseSettings +{ + public required string GameManager { get; init; } + + public bool InvalidFilesListXmlFailsInitialization { get; init; } = true; + + public bool InvalidContainerXmlFailsInitialization { get; init; } = false; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs similarity index 75% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs index 1dc4ce1..7e27965 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EngineXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using PG.Commons.Hashing; using PG.Commons.Services; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Xml.Parsers; using PG.StarWarsGame.Files.XML; @@ -15,32 +16,24 @@ namespace PG.StarWarsGame.Engine.Xml; - -public sealed record EngineXmlParseSettings +public sealed class PetroglyphStarWarsGameXmlParser : ServiceBase, IPetroglyphXmlParserInfo { - public bool InvalidFilesListXmlFailsInitialization { get; init; } = true; - - public bool InvalidContainerXmlFailsInitialization { get; init; } = false; -} - - -public sealed class EngineXmlParser : ServiceBase, IPetroglyphXmlParserInfo -{ - public event EventHandler? XmlParseError; - private readonly IGameRepository _gameRepository; - private readonly IXmlParserErrorReporter? _reporter; + private readonly PetroglyphStarWarsGameXmlParseSettings _settings; + private readonly IGameEngineErrorReporter _reporter; private readonly IPetroglyphXmlFileParserFactory _fileParserFactory; public string Name { get; } - public EngineXmlParser( - IGameRepository gameRepository, + public PetroglyphStarWarsGameXmlParser( + IGameRepository gameRepository, + PetroglyphStarWarsGameXmlParseSettings settings, IServiceProvider serviceProvider, - IXmlParserErrorReporter? reporter) + IGameEngineErrorReporter reporter) : base(serviceProvider) { _gameRepository = gameRepository; + _settings = settings; _reporter = reporter; _fileParserFactory = serviceProvider.GetRequiredService(); Name = GetType().FullName!; @@ -53,15 +46,25 @@ public XmlFileList ParseFileList(string xmlFile) using var containerStream = _gameRepository.TryOpenFile(xmlFile); if (containerStream == null) { - _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) + var message = $"Could not find XML file '{xmlFile}'"; + + Logger.LogWarning(message); + + _reporter.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) { - Message = "Xml file not found", + Message = message, ErrorKind = XmlParseErrorKind.MissingFile }); - Logger.LogWarning("Could not find XML file '{XmlFile}'", xmlFile); + + if (_settings.InvalidFilesListXmlFailsInitialization) + { + _reporter.Report(new InitializationError + { + GameManager = _settings.GameManager, + Message = message, + }); + } - var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: true); - XmlParseError?.Invoke(this, args); return XmlFileList.Empty(new XmlLocationInfo(xmlFile, null)); } @@ -76,14 +79,21 @@ public XmlFileList ParseFileList(string xmlFile) } catch (XmlException e) { - _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) + _reporter.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) { ErrorKind = XmlParseErrorKind.Unknown, Message = e.Message, }); - var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: true); - XmlParseError?.Invoke(this, args); + if (_settings.InvalidFilesListXmlFailsInitialization) + { + _reporter.Report(new InitializationError + { + GameManager = _settings.GameManager, + Message = e.Message, + }); + } + return XmlFileList.Empty(new XmlLocationInfo(xmlFile, null)); } @@ -120,16 +130,25 @@ public bool ParseEntriesFromContainerFile( if (fileStream is null) { - _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) + var message = $"Could not find XML file '{xmlFile}'"; + Logger.LogWarning(message); + + _reporter.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) { - Message = "Xml file not found", + Message = message, ErrorKind = XmlParseErrorKind.MissingFile }); - Logger.LogWarning("Could not find XML file '{File}'", xmlFile); + + if (_settings.InvalidContainerXmlFailsInitialization) + { + _reporter.Report(new InitializationError + { + GameManager = _settings.GameManager, + Message = message, + }); + } - var args = new EngineXmlParserErrorEventArgs(xmlFile, isXmlFileList: false); - XmlParseError?.Invoke(this, args); - return args.Continue; + return _settings.InvalidContainerXmlFailsInitialization; } Logger.LogDebug("Parsing File '{File}'", xmlFile); @@ -141,14 +160,20 @@ public bool ParseEntriesFromContainerFile( } catch (XmlException e) { - _reporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) + _reporter.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) { ErrorKind = XmlParseErrorKind.Unknown, Message = e.Message, }); - var args = new EngineXmlParserErrorEventArgs(xmlFile, e, isXmlFileList: false); - XmlParseError?.Invoke(this, args); - return args.Continue; + if (_settings.InvalidContainerXmlFailsInitialization) + { + _reporter.Report(new InitializationError + { + GameManager = _settings.GameManager, + Message = e.Message, + }); + } + return _settings.InvalidContainerXmlFailsInitialization; } } } From 5b44a6878f083641db844a5c7e64e1c3882390be Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 16:17:07 +0100 Subject: [PATCH 08/15] improve error reports --- src/ModVerify.CliApp/Program.cs | 2 +- src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index ba5bd31..e5316f4 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -51,7 +51,7 @@ private static Task Main(string[] args) internal class Program : SelfUpdateableAppLifecycle { - private static readonly string EngineParserNamespace = typeof(EngineXmlParser).Namespace!; + private static readonly string EngineParserNamespace = typeof(PetroglyphStarWarsGameXmlParser).Namespace!; private static readonly string ParserNamespace = typeof(XmlFileParser<>).Namespace!; private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; private static readonly CompiledExpression PrintToConsoleExpression = SerilogExpression.Compile($"EventId.Id = {ModVerifyConstants.ConsoleEventIdValue}"); diff --git a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs index b29fce0..9607cf1 100644 --- a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs @@ -28,7 +28,8 @@ protected override ErrorData CreateError(XmlError error) var context = new List { - strippedFileName + $"Parser: {error.Parser.Name}", + $"File: {strippedFileName}" }; var xmlElement = error.Element; From 572787518d78088cdb193dd0ac8a8b1b06df657c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 16:47:25 +0100 Subject: [PATCH 09/15] unify checks and filelist parser behaves like engine --- .../GameObjects/GameObjectTypeGameManager.cs | 1 - .../Parsers/Base/PetroglyphXmlParserBase.cs | 26 +++++++++++++++++ .../Parsers/Base/XmlObjectParserBase.cs | 2 +- .../PetroglyphPrimitiveXmlParser.cs | 21 +------------- .../Parsers/XmlFileListParser.cs | 29 ++++++++++--------- 5 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 7652704..5830262 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -2,7 +2,6 @@ using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Xml; -using PG.StarWarsGame.Engine.Xml.Parsers; using System; using System.Linq; using System.Threading; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs index 4e05ac1..2a6a1e2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs @@ -15,6 +15,32 @@ public override string ToString() return Name; } + protected bool IsTagValid(XElement element) + { + var tagName = element.Name.LocalName; + if (string.IsNullOrEmpty(tagName)) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.EmptyNodeName, + Message = "A tag name cannot be null or empty.", + }); + return false; + } + + if (tagName.Length > XmlFileConstants.MaxTagNameLength) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.TooLongData, + Message = $"A tag name can be only {XmlFileConstants.MaxTagNameLength} chars long.", + }); + return false; + } + + return true; + } + protected PetroglyphXmlParserBase(IXmlParserErrorReporter? errorReporter) { Name = GetType().FullName!; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs index 877ceab..ff59d8a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs @@ -31,6 +31,6 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) protected virtual bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState) { - return XmlTagMapper.TryParseEntry(tag, xmlObject); + return IsTagValid(tag) && XmlTagMapper.TryParseEntry(tag, xmlObject); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs index ddd3b24..4df9467 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -14,27 +14,8 @@ private protected PetroglyphPrimitiveXmlParser() : base(PrimitiveXmlErrorReporte public T Parse(XElement element) { - var tagName = element.Name.LocalName; - if (string.IsNullOrEmpty(tagName)) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.EmptyNodeName, - Message = "A tag name cannot be null or empty.", - }); + if (IsTagValid(element)) return DefaultValue; - } - - if (tagName.Length > XmlFileConstants.MaxTagNameLength) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.TooLongData, - Message = $"A tag name can be only {XmlFileConstants.MaxTagNameLength} chars long.", - }); - return DefaultValue; - } - var value = element.Value.AsSpan().Trim(); return value.Length == 0 ? DefaultValue : ParseCore(value, element); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index d96d497..641f59e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -16,27 +16,30 @@ protected override XmlFileList ParseRoot(XElement element, string fileName) foreach (var child in element.Elements()) { var tagName = GetTagName(child); - if (tagName == "File") + if (tagName != "File") { - var file = PetroglyphXmlStringParser.Instance.Parse(child); - if (file.Length == 0) + ErrorReporter?.Report(new XmlError(this, child) { - ErrorReporter?.Report(new XmlError(this, child) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = "Empty value in tag", - }); - } - files.Add(file); + ErrorKind = XmlParseErrorKind.UnknownNode, + Message = $"Tag '<{tagName}>' is not supported. Only '' is supported.", + }); } - else + + // NB: There intentionally is not else branch here, because that's how the engine behaves. + // It checks whether the tag is called "File" and reports an assert if not. + // However, it still consumes the value and treats it as file. + + var file = PetroglyphXmlStringParser.Instance.Parse(child); + if (file.Length == 0) { ErrorReporter?.Report(new XmlError(this, child) { - ErrorKind = XmlParseErrorKind.UnknownNode, - Message = $"Tag '<{tagName}>' is not supported. Only '' is supported.", + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = "Empty value in tag", }); } + + files.Add(file); } return new XmlFileList(new ReadOnlyCollection(files), new XmlLocationInfo(fileName, null)); } From 6696329355904f9dae0a1b32d8a9c5d48d56e4c5 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 17:42:57 +0100 Subject: [PATCH 10/15] fix parsers --- .../Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs index 4df9467..2cece9c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -14,7 +14,7 @@ private protected PetroglyphPrimitiveXmlParser() : base(PrimitiveXmlErrorReporte public T Parse(XElement element) { - if (IsTagValid(element)) + if (!IsTagValid(element)) return DefaultValue; var value = element.Value.AsSpan().Trim(); return value.Length == 0 ? DefaultValue : ParseCore(value, element); From be101eefb9b942246b8806d345b31de64097fa7c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 17 Feb 2026 18:57:59 +0100 Subject: [PATCH 11/15] support tags with child nodes --- .../Reporting/Engine/XmlParseErrorReporter.cs | 2 ++ src/ModVerify/Verifiers/DuplicateNameFinder.cs | 1 - src/ModVerify/Verifiers/VerifierErrorCodes.cs | 1 + .../ErrorHandling/XmlParseErrorKind.cs | 6 +++++- .../Parsers/Base/PetroglyphXmlParserBase.cs | 9 +++++++++ .../Parsers/Base/XmlObjectParserBase.cs | 8 ++++++++ .../Parsers/NamedXmlObjectParser.cs | 14 +++++++------- 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs index 9607cf1..83c3bbe 100644 --- a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs @@ -75,6 +75,7 @@ private static VerificationSeverity GetSeverityFromError(XmlParseErrorKind xmlEr XmlParseErrorKind.DataBeforeHeader => VerificationSeverity.Information, XmlParseErrorKind.MissingNode => VerificationSeverity.Critical, XmlParseErrorKind.UnknownNode => VerificationSeverity.Information, + XmlParseErrorKind.TagHasElements => VerificationSeverity.Warning, _ => VerificationSeverity.Warning }; } @@ -94,6 +95,7 @@ private static string GetIdFromError(XmlParseErrorKind xmlErrorErrorKind) XmlParseErrorKind.DataBeforeHeader => VerifierErrorCodes.XmlDataBeforeHeader, XmlParseErrorKind.MissingNode => VerifierErrorCodes.XmlMissingNode, XmlParseErrorKind.UnknownNode => VerifierErrorCodes.XmlUnsupportedTag, + XmlParseErrorKind.TagHasElements => VerifierErrorCodes.XmlElementsInTag, _ => throw new ArgumentOutOfRangeException(nameof(xmlErrorErrorKind), xmlErrorErrorKind, null) }; } diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 600f090..c6b3089 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -7,7 +7,6 @@ using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.MTD.Data; using PG.StarWarsGame.Files.MTD.Files; using PG.StarWarsGame.Files.XML.Data; diff --git a/src/ModVerify/Verifiers/VerifierErrorCodes.cs b/src/ModVerify/Verifiers/VerifierErrorCodes.cs index 57d3209..fdb4d2b 100644 --- a/src/ModVerify/Verifiers/VerifierErrorCodes.cs +++ b/src/ModVerify/Verifiers/VerifierErrorCodes.cs @@ -36,4 +36,5 @@ public static class VerifierErrorCodes public const string XmlDataBeforeHeader = "XML08"; public const string XmlMissingNode = "XML09"; public const string XmlUnsupportedTag = "XML10"; + public const string XmlElementsInTag = "XML11"; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs index aa3f8d2..861e441 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs @@ -50,5 +50,9 @@ public enum XmlParseErrorKind /// /// The XML tag name is null or empty. /// - EmptyNodeName + EmptyNodeName = 11, + /// + /// The XML tag has child elements. + /// + TagHasElements = 12 } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs index 2a6a1e2..af9fdb1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs @@ -17,6 +17,15 @@ public override string ToString() protected bool IsTagValid(XElement element) { + if (element.HasElements) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.TagHasElements, + Message = "A tag cannot have elements.", + }); + return false; + } var tagName = element.Name.LocalName; if (string.IsNullOrEmpty(tagName)) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs index ff59d8a..f4eb0af 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs @@ -10,6 +10,8 @@ public abstract class XmlObjectParserBase(IXmlTagMapper XmlTagMapper = tagMapper ?? throw new ArgumentNullException(nameof(tagMapper)); + protected virtual bool IgnoreEmptyValue => true; + protected virtual void ValidateValues(TObject namedXmlObject, XElement element) { } @@ -18,6 +20,12 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) { foreach (var tag in element.Elements()) { + if (string.IsNullOrEmpty(tag.Value) && IgnoreEmptyValue) + continue; + + if (tag.HasElements) + Parse(xmlObject, tag, in state); + if (!ParseTag(tag, xmlObject, state)) { ErrorReporter?.Report(new XmlError(this, element) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs index 4b206af..a31f2aa 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs @@ -9,18 +9,18 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class NamedXmlObjectParser( +public abstract class NamedXmlObjectParser( IServiceProvider serviceProvider, - IXmlTagMapper tagMapper, + IXmlTagMapper tagMapper, IXmlParserErrorReporter? errorReporter) - : XmlObjectParserBase>(tagMapper, errorReporter) - where TObject : NamedXmlObject + : XmlObjectParserBase>(tagMapper, errorReporter) + where T : NamedXmlObject { protected virtual bool UpperCaseNameForCrc => true; protected readonly ICrc32HashingService HashingService = serviceProvider.GetRequiredService(); - public TObject Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc) + public T Parse(XElement element, IReadOnlyFrugalValueListDictionary parsedEntries, out Crc32 nameCrc) { var name = GetXmlObjectName(element, out nameCrc); var namedXmlObject = CreateXmlObject(name, nameCrc, element, XmlLocationInfo.FromElement(element)); @@ -30,7 +30,7 @@ public TObject Parse(XElement element, IReadOnlyFrugalValueListDictionary Date: Sat, 21 Feb 2026 10:28:14 +0100 Subject: [PATCH 12/15] huge parser refactoring --- src/ModVerify/ModVerify.csproj | 2 +- .../Reporting/Engine/XmlParseErrorReporter.cs | 2 + .../GuiDialogs/GuiDialogsVerifier.cs | 2 + src/ModVerify/Verifiers/VerifierErrorCodes.cs | 1 + .../Audio/Sfx/SfxEvent.cs | 16 +- .../Audio/Sfx/SfxEventGameManager.cs | 2 +- .../CommandBar/CommandBarGameManager.cs | 2 +- .../CommandBar/Xml/CommandBarComponentData.cs | 3 +- .../PG.StarWarsGame.Engine/GameManagerBase.cs | 1 + .../GameObjects/GameObject.cs | 1 - .../GameObjects/GameObjectTypeGameManager.cs | 2 +- .../GuiDialog/GuiDialogGameManager.cs | 39 +- .../GuiDialogGameManager_Initialization.cs | 87 ++-- .../Xml/EnumConversionDictionary.cs | 58 +++ .../Parsers/FileObjects/GuiDialogParser.cs | 135 +++++- .../NamedObjects/CommandBarComponentParser.cs | 112 ++++- .../Parsers/NamedObjects/SfxEventParser.cs | 431 +++++++++++------- .../Parsers/Tags/CommandBarComponentTags.cs | 108 ----- .../Tags/ComponentTextureKeyExtensions.cs | 114 ----- .../Xml/Parsers/Tags/SfxEventXmlTags.cs | 41 -- .../PetroglyphStarWarsGameXmlParseSettings.cs | 2 +- .../Xml/PetroglyphStarWarsGameXmlParser.cs | 103 ++--- .../PG.StarWarsGame.Files.ALO.csproj | 2 +- .../PG.StarWarsGame.Files.ChunkFiles.csproj | 2 +- .../Data/XmlObject.cs | 4 - .../ErrorHandling/XmlParseErrorKind.cs | 6 +- .../PG.StarWarsGame.Files.XML.csproj | 2 +- .../Parsers/Base/XmlObjectParserBase.cs | 28 +- .../Parsers/NamedXmlObjectParser.cs | 8 +- .../CommaSeparatedStringKeyValueListParser.cs | 3 +- .../Primitives/PetroglyphNumberParser.cs | 88 ++++ .../PetroglyphPrimitiveXmlParser.cs | 8 +- .../Primitives/PetroglyphXmlBooleanParser.cs | 6 +- .../Primitives/PetroglyphXmlByteParser.cs | 14 +- .../PetroglyphXmlBytePercentParser.cs | 45 ++ .../Primitives/PetroglyphXmlFloatParser.cs | 32 +- .../Primitives/PetroglyphXmlIntegerParser.cs | 28 +- .../PetroglyphXmlLooseStringListParser.cs | 1 + .../PetroglyphXmlMax100ByteParser.cs | 74 --- .../PetroglyphXmlRgbaColorParser.cs | 1 + .../Primitives/PetroglyphXmlSByteParser.cs | 41 ++ .../Primitives/PetroglyphXmlStringParser.cs | 6 +- .../PetroglyphXmlUnsignedIntegerParser.cs | 10 +- .../Primitives/PetroglyphXmlVector2FParser.cs | 6 +- .../Parsers/XmlFileListParser.cs | 4 +- .../Parsers/XmlObjectParser.cs | 3 +- .../Utilities/PGMath.cs | 58 ++- .../XElementExtensions.cs | 15 + 48 files changed, 1025 insertions(+), 734 deletions(-) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EnumConversionDictionary.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/CommandBarComponentTags.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/ComponentTextureKeyExtensions.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/SfxEventXmlTags.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphNumberParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBytePercentParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlSByteParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/XElementExtensions.cs diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index aecf306..948719f 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net10.0 AlamoEngineTools.ModVerify AET.ModVerify AET.ModVerify diff --git a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs index 83c3bbe..9548ad4 100644 --- a/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Engine/XmlParseErrorReporter.cs @@ -76,6 +76,7 @@ private static VerificationSeverity GetSeverityFromError(XmlParseErrorKind xmlEr XmlParseErrorKind.MissingNode => VerificationSeverity.Critical, XmlParseErrorKind.UnknownNode => VerificationSeverity.Information, XmlParseErrorKind.TagHasElements => VerificationSeverity.Warning, + XmlParseErrorKind.UnexceptedElementName => VerificationSeverity.Information, _ => VerificationSeverity.Warning }; } @@ -96,6 +97,7 @@ private static string GetIdFromError(XmlParseErrorKind xmlErrorErrorKind) XmlParseErrorKind.MissingNode => VerifierErrorCodes.XmlMissingNode, XmlParseErrorKind.UnknownNode => VerifierErrorCodes.XmlUnsupportedTag, XmlParseErrorKind.TagHasElements => VerifierErrorCodes.XmlElementsInTag, + XmlParseErrorKind.UnexceptedElementName => VerifierErrorCodes.XmlUnexceptedElementName, _ => throw new ArgumentOutOfRangeException(nameof(xmlErrorErrorKind), xmlErrorErrorKind, null) }; } diff --git a/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs b/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs index 435c5de..320815e 100644 --- a/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs +++ b/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs @@ -52,6 +52,8 @@ private void VerifyGuiTextures() }; components.AddRange(GameEngine.GuiDialogManager.Components); + // TODO: Verify no double definitions for textures and components exit + foreach (var component in components) VerifyGuiComponentTexturesExist(component); diff --git a/src/ModVerify/Verifiers/VerifierErrorCodes.cs b/src/ModVerify/Verifiers/VerifierErrorCodes.cs index fdb4d2b..a526715 100644 --- a/src/ModVerify/Verifiers/VerifierErrorCodes.cs +++ b/src/ModVerify/Verifiers/VerifierErrorCodes.cs @@ -37,4 +37,5 @@ public static class VerifierErrorCodes public const string XmlMissingNode = "XML09"; public const string XmlUnsupportedTag = "XML10"; public const string XmlElementsInTag = "XML11"; + public const string XmlUnexceptedElementName = "XML12"; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs index b9d32a0..d384007 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs @@ -25,11 +25,11 @@ public sealed class SfxEvent : NamedXmlObject public const byte MaxPan2dValue = 100; public const byte MinPriorityValue = 1; public const byte MaxPriorityValue = 5; - public const byte MaxProbability = 100; - public const sbyte MinMaxInstances = 0; - public const sbyte InfinitivePlayCount = -1; - public const float MinLoopSeconds = 0.0f; - public const float MinVolumeSaturation = 0.0f; + public const byte MaxProbabilityValue = 100; + public const sbyte MinMaxInstancesValue = 0; + public const sbyte InfinitivePlayCountValue = -1; + public const float MinLoopSecondsValue = 0.0f; + public const float MinVolumeSaturationValue = 0.0f; // Default values which are not the default value of the type public const byte DefaultPriority = 3; @@ -163,7 +163,7 @@ internal SfxEvent(string name, Crc32 nameCrc, XmlLocationInfo location) { } - public override void CoerceValues() + internal void FixupValues() { AdjustMinMaxValues(ref _minVolume, ref _maxVolume); AdjustMinMaxValues(ref _minPitch, ref _maxPitch); @@ -187,6 +187,8 @@ public override void CoerceValues() */ public void ApplyPreset(SfxEvent preset) { + Preset = preset; + Is3D = preset.Is3D; Is2D = preset.Is2D; IsGui = preset.IsGui; @@ -194,8 +196,6 @@ public void ApplyPreset(SfxEvent preset) IsUnitResponseVo = preset.IsUnitResponseVo; IsAmbientVo = preset.IsAmbientVo; IsLocalized = preset.IsLocalized; - Preset = preset; - UsePresetName = preset.Name; PlaySequentially = preset.PlaySequentially; PreSamples = preset.PreSamples; Samples = preset.Samples; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index 46741bc..a9034ef 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -28,7 +28,7 @@ protected override async Task InitializeCoreAsync(CancellationToken token) new PetroglyphStarWarsGameXmlParseSettings { GameManager = ToString(), - InvalidContainerXmlFailsInitialization = true, + InvalidObjectXmlFailsInitialization = true, InvalidFilesListXmlFailsInitialization = true }, ServiceProvider, ErrorReporter); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 4ad531f..8b5d175 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -77,7 +77,7 @@ protected override async Task InitializeCoreAsync(CancellationToken token) var contentParser = new PetroglyphStarWarsGameXmlParser(GameRepository, new PetroglyphStarWarsGameXmlParseSettings { GameManager = ToString(), - InvalidContainerXmlFailsInitialization = true, + InvalidObjectXmlFailsInitialization = true, InvalidFilesListXmlFailsInitialization = true }, ServiceProvider, ErrorReporter); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs index 40ab0c9..82168a3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs @@ -128,9 +128,8 @@ public sealed class CommandBarComponentData(string name, Crc32 crc, XmlLocationI public Vector4Int? TextColor2 { get; internal set; } public Vector4Int? MaxBarColor { get; internal set; } = WhiteColor; - public override void CoerceValues() + internal void FixupValues() { - base.CoerceValues(); if (AlternateFontNames.Count == 0) return; var newFontNames = new string[AlternateFontNames.Count]; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs index 607121d..c49438b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; using System.Threading; using System.Threading.Tasks; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs index f531df4..1d05bdd 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.Data; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 5830262..e82bb02 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -26,7 +26,7 @@ private void ParseGameObjectDatabases() { GameManager = ToString(), InvalidFilesListXmlFailsInitialization = true, - InvalidContainerXmlFailsInitialization = false, + InvalidObjectXmlFailsInitialization = false, }, ServiceProvider, ErrorReporter); var xmlFileList = parser.ParseFileList(@"DATA\XML\GAMEOBJECTFILES.XML").Files diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index 879c16f..5b96451 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.Commons.Hashing; @@ -19,15 +20,24 @@ internal partial class GuiDialogGameManager(GameRepository repository, GameEngin private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); - // Unlike other strings for this game, the component's (aka gadget) name is case-sensitive. - private readonly Dictionary> _perComponentTextures = new(StringComparer.Ordinal); - private readonly Dictionary _defaultTextures = new(); - private ReadOnlyDictionary _defaultTexturesRo = null!; - private bool _megaTextureExists; private string? _megaTextureFileName; - + [field: MaybeNull, AllowNull] + private ReadOnlyDictionary> PerComponentTextures + { + get + { + ThrowIfNotInitialized(); + return field!; + } + set + { + ThrowIfAlreadyInitialized(); + field = value; + } + } + public IMtdFile? MtdFile { get @@ -61,7 +71,7 @@ public IReadOnlyCollection Components get { ThrowIfNotInitialized(); - return _perComponentTextures.Keys; + return PerComponentTextures.Keys!; } } @@ -70,14 +80,19 @@ public IReadOnlyDictionary DefaultTextu get { ThrowIfNotInitialized(); - return _defaultTexturesRo; + return field!; + } + internal set + { + ThrowIfAlreadyInitialized(); + field = value; } } public IReadOnlyDictionary GetTextureEntries(string component, out bool componentExist) { - if (!_perComponentTextures.TryGetValue(component, out var textures)) + if (!PerComponentTextures.TryGetValue(component, out var textures)) { Logger?.LogDebug("The component '{Component}' has no overrides. Using default textures.", component); componentExist = false; @@ -85,15 +100,15 @@ public IReadOnlyDictionary GetTextureEn } componentExist = true; - return new ReadOnlyDictionary(textures); + return textures; } public bool TryGetTextureEntry(string component, GuiComponentType key, out ComponentTextureEntry texture) { - if (!_perComponentTextures.TryGetValue(component, out var textures)) + if (!PerComponentTextures.TryGetValue(component, out var textures)) { Logger?.LogDebug("The component '{Component}' has no overrides. Using default textures.", component); - textures = _defaultTextures; + textures = DefaultTextureEntries; } return textures.TryGetValue(key, out texture); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index f4b3267..2bac738 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading; @@ -8,8 +7,8 @@ using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GuiDialog.Xml; +using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Engine.Xml.Parsers; -using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.Binary; namespace PG.StarWarsGame.Engine.GuiDialog; @@ -22,24 +21,14 @@ protected override Task InitializeCoreAsync(CancellationToken token) { return Task.Run(() => { - var guiDialogParser = new GuiDialogParser(ServiceProvider, ErrorReporter); - - _defaultTexturesRo = new ReadOnlyDictionary(_defaultTextures); - - Logger?.LogInformation("Parsing GuiDialogs..."); - using var fileStream = GameRepository.TryOpenFile("DATA\\XML\\GUIDIALOGS.XML"); - - if (fileStream is null) - { - ErrorReporter.Report(new InitializationError + var engineParser = new PetroglyphStarWarsGameXmlParser(GameRepository, + new PetroglyphStarWarsGameXmlParseSettings { GameManager = ToString(), - Message = "Unable to find GuiDialogs.xml" - }); - return; - } + InvalidObjectXmlFailsInitialization = true + }, ServiceProvider, ErrorReporter); - var guiDialogs = guiDialogParser.ParseFile(fileStream); + var guiDialogs = engineParser.ParseFile("DATA\\XML\\GUIDIALOGS.XML", new GuiDialogParser(ServiceProvider, ErrorReporter)); if (guiDialogs is null) { ErrorReporter.Report(new InitializationError @@ -50,10 +39,8 @@ protected override Task InitializeCoreAsync(CancellationToken token) return; } - GuiDialogsXml = guiDialogs; - InitializeTextures(guiDialogs.TextureData); - + GuiDialogsXml = guiDialogs; }, token); } @@ -62,7 +49,8 @@ private void InitializeTextures(GuiDialogsXmlTextureData textureData) InitializeMegaTextures(textureData); var textures = textureData.Textures; - + + IReadOnlyDictionary defaultTextures; if (textures.Count == 0) { ErrorReporter.Report(new InitializationError @@ -70,47 +58,70 @@ private void InitializeTextures(GuiDialogsXmlTextureData textureData) GameManager = ToString(), Message = "No Textures defined in GuiDialogs.xml" }); + + defaultTextures = new ReadOnlyDictionary( + new Dictionary()); } else { - var defaultCandidate = textures.First(); - // Regardless of its name, the game treats the first entry as default. - var defaultTextures = InitializeComponentTextures(defaultCandidate, true, out var invalidKeys); - foreach (var entry in defaultTextures) - _defaultTextures.Add(entry.Key, entry.Value); + var defaultCandidate = textures.First(); + defaultTextures = InitializeComponentTextures(defaultCandidate, null, out var invalidKeys); + ReportInvalidComponent(in invalidKeys); } + var perComponentTextures = new Dictionary>(); + foreach (var componentTextureData in textures.Skip(1)) { // The game only uses the *first* entry. - if (_perComponentTextures.ContainsKey(componentTextureData.Component)) + if (perComponentTextures.ContainsKey(componentTextureData.Component)) continue; - _perComponentTextures.Add(componentTextureData.Component, InitializeComponentTextures(componentTextureData, false, out var invalidKeys)); + perComponentTextures.Add( + componentTextureData.Component, + InitializeComponentTextures(componentTextureData, defaultTextures, out var invalidKeys)); + ReportInvalidComponent(in invalidKeys); } + + DefaultTextureEntries = defaultTextures; + PerComponentTextures = + new ReadOnlyDictionary>( + perComponentTextures); } - private Dictionary InitializeComponentTextures(XmlComponentTextureData textureData, bool isDefaultComponent, out FrugalList invalidKeys) + private ReadOnlyDictionary InitializeComponentTextures( + XmlComponentTextureData textureData, + IReadOnlyDictionary? defaultTextures, + out FrugalList invalidKeys) { - invalidKeys = new FrugalList(); + invalidKeys = []; var result = new Dictionary(); + var isDefaultComponent = defaultTextures is null; + if (!isDefaultComponent) { - // This assumes that _defaultTextures is already filled - foreach (var key in _defaultTextures.Keys) - result.Add(key, _defaultTextures[key]); + foreach (var key in defaultTextures!.Keys) + result.Add(key, defaultTextures[key]); } - + if (textureData.Textures.Count == 0) + { + ErrorReporter.Report(new InitializationError + { + GameManager = ToString(), + Message = $"No Textures defined for component '{textureData.Component}' in GuiDialogs.xml" + }); + } + foreach (var keyText in textureData.Textures.Keys) { - if (!ComponentTextureKeyExtensions.TryConvertToKey(keyText.AsSpan(), out var key)) + if (!GuiDialogParser.ComponentTypeDictionary.TryStringToEnum(keyText, out var key)) { invalidKeys.Add(keyText); continue; @@ -119,8 +130,8 @@ private Dictionary InitializeComponentT var textureValue = textureData.Textures.GetLastValue(keyText); result[key] = new ComponentTextureEntry(key, textureValue, !isDefaultComponent); } - - return result; + + return new ReadOnlyDictionary(result); } private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EnumConversionDictionary.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EnumConversionDictionary.cs new file mode 100644 index 0000000..275fea8 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/EnumConversionDictionary.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace PG.StarWarsGame.Engine.Xml; + +public sealed class EnumConversionDictionary : IReadOnlyCollection> where T : struct, Enum +{ + // This is the value the engine would give you. + internal const string StringNotFoundDummy = "-BAD VALUE-"; + + // Most consumers call the method TryStringToEnum. Thus, we optimize the class for this case + // and accept performance penalties for the EnumToString case. + private readonly IReadOnlyDictionary _dictionary; + + public int Count => _dictionary.Count; + + public EnumConversionDictionary(IEnumerable> entries) + { + var dictionary = new Dictionary(); + var values = dictionary.Values; + foreach (var entry in entries) + { + if (values.Contains(entry.Value)) + throw new InvalidOperationException($"Enum value {entry.Value} already exists!"); + dictionary.Add(entry.Key.ToUpperInvariant(), entry.Value); + } + _dictionary = dictionary; + } + + public bool TryStringToEnum(string key, out T enumValue) + { + key = key.ToUpperInvariant(); + return _dictionary.TryGetValue(key, out enumValue); + } + + public string EnumToString(T enumValue) + { + foreach (var keyValuePair in _dictionary) + { + if (EqualityComparer.Default.Equals(enumValue, keyValuePair.Value)) + return keyValuePair.Key; + } + + return StringNotFoundDummy; + } + + public IEnumerator> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs index e735dc3..152ec83 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/FileObjects/GuiDialogParser.cs @@ -1,11 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; +using AnakinRaW.CommonUtilities.Collections; +using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; +using System; +using System.Collections.Generic; +using System.Xml.Linq; namespace PG.StarWarsGame.Engine.Xml.Parsers; @@ -14,7 +15,14 @@ internal class GuiDialogParser(IServiceProvider serviceProvider, IXmlParserError { protected override GuiDialogsXml ParseRoot(XElement element, string fileName) { - var textures = ParseTextures(element.Element("Textures"), fileName); + using var elementsEnumerator = element.Elements().GetEnumerator(); + + var texturesExist = elementsEnumerator.MoveNext(); + var textures = ParseTextures(texturesExist + ? elementsEnumerator.Current + : null!, + fileName); + return new GuiDialogsXml(textures, XmlLocationInfo.FromElement(element)); } @@ -24,12 +32,21 @@ private GuiDialogsXmlTextureData ParseTextures(XElement? element, string fileNam { ErrorReporter?.Report(new XmlError(this, locationInfo: new XmlLocationInfo(fileName, null)) { - ErrorKind = XmlParseErrorKind.MissingNode, - Message = "Expected node is missing." + ErrorKind = XmlParseErrorKind.MissingNode, + Message = "Unable to read textures for GUI." }); return new GuiDialogsXmlTextureData([], new XmlLocationInfo(fileName, null)); } + if (element.Name != "Textures") + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.UnexceptedElementName, + Message = "Unable to read textures for GUI." + }); + } + var textures = new List(); GetAttributeValue(element, "File", out var megaTexture); @@ -42,7 +59,7 @@ private GuiDialogsXmlTextureData ParseTextures(XElement? element, string fileNam { ErrorReporter?.Report(new XmlError(this, element) { - Message = "Textures must contain at least one child node.", + Message = "Missing default texture specifications in GUI XML file!", ErrorKind = XmlParseErrorKind.MissingNode }); } @@ -64,4 +81,106 @@ private XmlComponentTextureData ParseTexture(XElement texture) return new XmlComponentTextureData(componentId, textures, XmlLocationInfo.FromElement(texture)); } + + + internal static readonly EnumConversionDictionary ComponentTypeDictionary = new([ + new("Button_Left", GuiComponentType.ButtonLeft), + new("Button_Middle", GuiComponentType.ButtonMiddle), + new("Button_Right", GuiComponentType.ButtonRight), + new("Button_Left_Mouse_Over", GuiComponentType.ButtonLeftMouseOver), + new("Button_Middle_Mouse_Over", GuiComponentType.ButtonMiddleMouseOver), + new("Button_Right_Mouse_Over", GuiComponentType.ButtonRightMouseOver), + new("Button_Left_Pressed", GuiComponentType.ButtonLeftPressed), + new("Button_Middle_Pressed", GuiComponentType.ButtonMiddlePressed), + new("Button_Right_Pressed", GuiComponentType.ButtonRightPressed), + new("Button_Left_Disabled", GuiComponentType.ButtonLeftDisabled), + new("Button_Middle_Disabled", GuiComponentType.ButtonMiddleDisabled), + new("Button_Right_Disabled", GuiComponentType.ButtonRightDisabled), + + new("Check_Off", GuiComponentType.CheckOff), + new("Check_On", GuiComponentType.CheckOn), + + new("Dial_Left", GuiComponentType.DialLeft), + new("Dial_Middle", GuiComponentType.DialMiddle), + new("Dial_Right", GuiComponentType.DialRight), + new("Dial_Plus", GuiComponentType.DialPlus), + new("Dial_Plus_Mouse_Over", GuiComponentType.DialPlusMouseOver), + new("Dial_Plus_Pressed", GuiComponentType.DialPlusPressed), + new("Dial_Minus", GuiComponentType.DialMinus), + new("Dial_Minus_Mouse_Over", GuiComponentType.DialMinusMouseOver), + new("Dial_Minus_Pressed", GuiComponentType.DialMinusPressed), + new("Dial_Tab", GuiComponentType.DialTab), + + new("Frame_Bottom", GuiComponentType.FrameBottom), + new("Frame_Bottom_Left", GuiComponentType.FrameBottomLeft), + new("Frame_Bottom_Right", GuiComponentType.FrameBottomRight), + new("Frame_Background", GuiComponentType.FrameBackground), + new("Frame_Left", GuiComponentType.FrameLeft), + new("Frame_Right", GuiComponentType.FrameRight), + new("Frame_Top", GuiComponentType.FrameTop), + new("Frame_Top_Left", GuiComponentType.FrameTopLeft), + new("Frame_Top_Right", GuiComponentType.FrameTopRight), + new("Frame_Top_Transition_Left", GuiComponentType.FrameTopTransitionLeft), + new("Frame_Top_Transition_Right", GuiComponentType.FrameTopTransitionRight), + new("Frame_Bottom_Transition_Left", GuiComponentType.FrameBottomTransitionLeft), + new("Frame_Bottom_Transition_Right", GuiComponentType.FrameBottomTransitionRight), + new("Frame_Left_Transition_Top", GuiComponentType.FrameLeftTransitionTop), + new("Frame_Left_Transition_Bottom", GuiComponentType.FrameLeftTransitionBottom), + new("Frame_Right_Transition_Top", GuiComponentType.FrameRightTransitionTop), + new("Frame_Right_Transition_Bottom", GuiComponentType.FrameRightTransitionBottom), + + new("Radio_Off", GuiComponentType.RadioOff), + new("Radio_On", GuiComponentType.RadioOn), + new("Radio_Disabled", GuiComponentType.RadioDisabled), + new("Radio_Mouse_Over", GuiComponentType.RadioMouseOver), + + new("Scroll_Down_Button", GuiComponentType.ScrollDownButton), + new("Scroll_Down_Button_Pressed", GuiComponentType.ScrollDownButtonPressed), + new("Scroll_Down_Button_Mouse_Over", GuiComponentType.ScrollDownButtonMouseOver), + new("Scroll_Down_Button_Disabled", GuiComponentType.ScrollDownButtonDisabled), + new("Scroll_Middle", GuiComponentType.ScrollMiddle), + new("Scroll_Middle_Disabled", GuiComponentType.ScrollMiddleDisabled), + new("Scroll_Tab", GuiComponentType.ScrollTab), + new("Scroll_Tab_Disabled", GuiComponentType.ScrollTabDisabled), + new("Scroll_Up_Button", GuiComponentType.ScrollUpButton), + new("Scroll_Up_Button_Pressed", GuiComponentType.ScrollUpButtonPressed), + new("Scroll_Up_Button_Mouse_Over", GuiComponentType.ScrollUpButtonMouseOver), + new("Scroll_Up_Button_Disabled", GuiComponentType.ScrollUpButtonDisabled), + + new("Trackbar_Scroll_Down_Button", GuiComponentType.TrackbarScrollDownButton), + new("Trackbar_Scroll_Down_Button_Pressed", GuiComponentType.TrackbarScrollDownButtonPressed), + new("Trackbar_Scroll_Down_Button_Mouse_Over", GuiComponentType.TrackbarScrollDownButtonMouseOver), + new("Trackbar_Scroll_Down_Button_Disabled", GuiComponentType.TrackbarScrollDownButtonDisabled), + new("Trackbar_Scroll_Middle", GuiComponentType.TrackbarScrollMiddle), + new("Trackbar_Scroll_Middle_Disabled", GuiComponentType.TrackbarScrollMiddleDisabled), + new("Trackbar_Scroll_Tab", GuiComponentType.TrackbarScrollTab), + new("Trackbar_Scroll_Tab_Disabled", GuiComponentType.TrackbarScrollTabDisabled), + new("Trackbar_Scroll_Up_Button", GuiComponentType.TrackbarScrollUpButton), + new("Trackbar_Scroll_Up_Button_Pressed", GuiComponentType.TrackbarScrollUpButtonPressed), + new("Trackbar_Scroll_Up_Button_Mouse_Over", GuiComponentType.TrackbarScrollUpButtonMouseOver), + new("Trackbar_Scroll_Up_Button_Disabled", GuiComponentType.TrackbarScrollUpButtonDisabled), + + new("Small_Frame_Bottom", GuiComponentType.SmallFrameBottom), + new("Small_Frame_Bottom_Left", GuiComponentType.SmallFrameBottomLeft), + new("Small_Frame_Bottom_Right", GuiComponentType.SmallFrameBottomRight), + new("Small_Frame_Left", GuiComponentType.SmallFrameMiddleLeft), + new("Small_Frame_Right", GuiComponentType.SmallFrameMiddleRight), + new("Small_Frame_Top", GuiComponentType.SmallFrameTop), + new("Small_Frame_Top_Left", GuiComponentType.SmallFrameTopLeft), + new("Small_Frame_Top_Right", GuiComponentType.SmallFrameTopRight), + new("Small_Frame_Background", GuiComponentType.SmallFrameBackground), + + new("Combo_Box_Popdown_Button", GuiComponentType.ComboboxPopdown), + new("Combo_Box_Popdown_Button_Pressed", GuiComponentType.ComboboxPopdownPressed), + new("Combo_Box_Popdown_Button_Mouse_Over", GuiComponentType.ComboboxPopdownMouseOver), + new("Combo_Box_Text_Box", GuiComponentType.ComboboxTextBox), + new("Combo_Box_Left_Cap", GuiComponentType.ComboboxLeftCap), + + new("Progress_Bar_Left", GuiComponentType.ProgressLeft), + new("Progress_Bar_Middle_Off", GuiComponentType.ProgressMiddleOff), + new("Progress_Bar_Middle_On", GuiComponentType.ProgressMiddleOn), + new("Progress_Bar_Right", GuiComponentType.ProgressRight), + + new("Scanlines", GuiComponentType.Scanlines), + ]); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs index 2c74d99..be3dcc0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs @@ -3,7 +3,6 @@ using System.Xml.Linq; using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.CommandBar.Xml; -using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; @@ -344,7 +343,7 @@ protected override bool ParseTag(XElement tag, CommandBarComponentData component } } - protected override void ValidateValues(CommandBarComponentData xmlData, XElement element) + protected override void ValidateAndFixupValues(CommandBarComponentData xmlData, XElement element) { if (xmlData.Name.Length > PGConstants.MaxCommandBarComponentName) { @@ -354,6 +353,8 @@ protected override void ValidateValues(CommandBarComponentData xmlData, XElement ErrorKind = XmlParseErrorKind.TooLongData }); } + + xmlData.FixupValues(); } private sealed class CommandBarComponentDataXmlTagMapper(IServiceProvider serviceProvider) @@ -363,4 +364,111 @@ protected override void BuildMappings() { } } + + internal static class CommandBarComponentTags + { + public const string SelectedTextureName = "Selected_Texture_Name"; + public const string BlankTextureName = "Blank_Texture_Name"; + public const string IconTextureName = "Icon_Texture_Name"; + public const string IconAlternateTextureName = "Icon_Alternate_Texture_Name"; + public const string MouseOverTextureName = "Mouse_Over_Texture_Name"; + public const string DisabledTextureName = "Disabled_Texture_Name"; + public const string FlashTextureName = "Flash_Texture_Name"; + public const string BarTextureName = "Bar_Texture_Name"; + public const string BarOverlayName = "Bar_Overlay_Name"; + public const string BuildTextureName = "Build_Texture_Name"; + public const string ModelName = "Model_Name"; + public const string BoneName = "Bone_Name"; + public const string CursorTextureName = "Cursor_Texture_Name"; + public const string FontName = "Font_Name"; + public const string AlternateFontName = "Alternate_Font_Name"; + public const string TooltipText = "Tooltip_Text"; + public const string ClickSfx = "Click_SFX"; + public const string MouseOverSfx = "Mouse_Over_SFX"; + public const string LowerEffectTextureName = "Lower_Effect_Texture_Name"; + public const string UpperEffectTextureName = "Upper_Effect_Texture_Name"; + public const string OverlayTextureName = "Overlay_Texture_Name"; + public const string Overlay2TextureName = "Overlay2_Texture_Name"; + public const string RightClickSfx = "Right_Click_SFX"; + public const string Type = "Type"; + public const string Group = "Group"; + public const string DragAndDrop = "Drag_And_Drop"; + public const string DragSelect = "Drag_Select"; + public const string Receptor = "Receptor"; + public const string Toggle = "Toggle"; + public const string Tab = "Tab"; + public const string AssociatedText = "Associated_Text"; + public const string Hidden = "Hidden"; + public const string Scale = "Scale"; + public const string Color = "Color"; + public const string TextColor = "Text_Color"; + public const string TextColor2 = "Text_Color2"; + public const string Size = "Size"; + public const string ClearColor = "Clear_Color"; + public const string Disabled = "Disabled"; + public const string SwapTexture = "Swap_Texture"; + public const string BaseLayer = "Base_Layer"; + public const string DrawAdditive = "Draw_Additive"; + public const string TextOffset = "Text_Offset"; + public const string TextOffset2 = "Text_Offset2"; + public const string Offset = "Offset"; + public const string DefaultOffset = "Default_Offset"; + public const string DefaultOffsetWidescreen = "Default_Offset_Widescreen"; + public const string IconOffset = "Icon_Offset"; + public const string MouseOverOffset = "Mouse_Over_Offset"; + public const string DisabledOffset = "Disabled_Offset"; + public const string BuildDialOffset = "Build_Dial_Offset"; + public const string BuildDial2Offset = "Build_Dial2_Offset"; + public const string LowerEffectOffset = "Lower_Effect_Offset"; + public const string UpperEffectOffset = "Upper_Effect_Offset"; + public const string OverlayOffset = "Overlay_Offset"; + public const string Overlay2Offset = "Overlay2_Offset"; + public const string Editable = "Editable"; + public const string MaxTextLength = "Max_Text_Length"; + public const string BlinkRate = "Blink_Rate"; + public const string FontPointSize = "Font_Point_Size"; + public const string TextOutline = "Text_Outline"; + public const string MaxTextWidth = "Max_Text_Width"; + public const string Stackable = "Stackable"; + public const string ModelOffsetX = "Model_Offset_X"; + public const string ModelOffsetY = "Model_Offset_Y"; + public const string ScaleModelX = "Scale_Model_X"; + public const string ScaleModelY = "Scale_Model_Y"; + public const string Collideable = "Collideable"; + public const string TextEmboss = "Text_Emboss"; + public const string ShouldGhost = "Should_Ghost"; + public const string GhostBaseOnly = "Ghost_Base_Only"; + public const string MaxBarLevel = "Max_Bar_Level"; + public const string MaxBarColor = "Max_Bar_Color"; + public const string CrossFade = "Cross_Fade"; + public const string LeftJustified = "Left_Justified"; + public const string RightJustified = "Right_Justified"; + public const string NoShell = "No_Shell"; + public const string SnapDrag = "Snap_Drag"; + public const string SnapLocation = "Snap_Location"; + public const string BlinkDuration = "Blink_Duration"; + public const string ScaleDuration = "Scale_Duration"; + public const string OffsetRender = "Offset_Render"; + public const string BlinkFade = "Blink_Fade"; + public const string NoHiddenCollision = "No_Hidden_Collision"; + public const string ManualOffset = "Manual_Offset"; + public const string SelectedAlpha = "Selected_Alpha"; + public const string PixelAlign = "Pixel_Align"; + public const string CanDragStack = "Can_Drag_Stack"; + public const string CanAnimate = "Can_Animate"; + public const string AnimFps = "Anim_FPS"; + public const string LoopAnim = "Loop_Anim"; + public const string SmoothBar = "Smooth_Bar"; + public const string OutlinedBar = "Outlined_Bar"; + public const string DragBack = "Drag_Back"; + public const string LowerEffectAdditive = "Lower_Effect_Additive"; + public const string UpperEffectAdditive = "Upper_Effect_Additive"; + public const string ClickShift = "Click_Shift"; + public const string TutorialScene = "Tutorial_Scene"; + public const string DialogScene = "Dialog_Scene"; + public const string ShouldRenderAtDragPos = "Should_Render_At_Drag_Pos"; + public const string DisableDarken = "Disable_Darken"; + public const string AnimateBack = "Animate_Back"; + public const string AnimateUpperEffect = "Animate_Upper_Effect"; + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs index e59ef80..484b075 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/SfxEventParser.cs @@ -1,10 +1,10 @@ using System; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Xml.Linq; using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Audio.Sfx; -using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; @@ -19,190 +19,287 @@ protected override SfxEvent CreateXmlObject(string name, Crc32 nameCrc, XElement return new SfxEvent(name, nameCrc, location); } - protected override void ValidateValues(SfxEvent sfxEvent, XElement element) + protected override void ValidateAndFixupValues(SfxEvent sfxEvent, XElement element) { - //if (sfxEvent.Name.Length > PGConstants.MaxSFXEventName) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, - // $"SFXEvent name '{sfxEvent.Name}' is too long.")); - //} - - //if (sfxEvent is { Is2D: true, Is3D: true }) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - // $"SFXEvent '{sfxEvent.Name}' is defined as 2D and 3D.")); - //} - - //if (sfxEvent.MinVolume > sfxEvent.MaxVolume) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - // $"{SfxEventXmlTags.MinVolume} should not be higher than {SfxEventXmlTags.MaxVolume} for SFXEvent '{sfxEvent.Name}'")); - //} - - //if (sfxEvent.MinPitch > sfxEvent.MaxPitch) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - // $"{SfxEventXmlTags.MinPitch} should not be higher than {SfxEventXmlTags.MaxPitch} for SFXEvent '{sfxEvent.Name}'")); - //} - - //if (sfxEvent.MinPan2D > sfxEvent.MaxPan2D) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - // $"{SfxEventXmlTags.MinPan2D} should not be higher than {SfxEventXmlTags.MaxPan2D} for SFXEvent '{sfxEvent.Name}'")); - //} - - //if (sfxEvent.MinPredelay > sfxEvent.MaxPredelay) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - // $"{SfxEventXmlTags.MinPredelay} should not be higher than {SfxEventXmlTags.MaxPredelay} for SFXEvent '{sfxEvent.Name}'")); - //} - - //if (sfxEvent.MinPostdelay > sfxEvent.MaxPostdelay) - //{ - // OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - // $"{SfxEventXmlTags.MinPostdelay} should not be higher than {SfxEventXmlTags.MaxPostdelay} for SFXEvent '{sfxEvent.Name}'")); - //} + if (sfxEvent.Name.Length > PGConstants.MaxSFXEventName) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.TooLongData, + Message = $"SFXEvent name '{sfxEvent.Name}' is too long." + }); + } + + if (sfxEvent.Is2D == sfxEvent.Is3D) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"SFXEvent '{sfxEvent.Name}' has the same value for is2D and is3D." + }); + } + + if (sfxEvent.MinVolume > sfxEvent.MaxVolume) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"{SfxEventXmlTags.MinVolume} should not be higher than {SfxEventXmlTags.MaxVolume} for SFXEvent '{sfxEvent.Name}'" + }); + } + + if (sfxEvent.MinPitch > sfxEvent.MaxPitch) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"{SfxEventXmlTags.MinPitch} should not be higher than {SfxEventXmlTags.MaxPitch} for SFXEvent '{sfxEvent.Name}'" + }); + } + + if (sfxEvent.MinPan2D > sfxEvent.MaxPan2D) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"{SfxEventXmlTags.MinPan2D} should not be higher than {SfxEventXmlTags.MaxPan2D} for SFXEvent '{sfxEvent.Name}'" + }); + } + + if (sfxEvent.MinPredelay > sfxEvent.MaxPredelay) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"{SfxEventXmlTags.MinPredelay} should not be higher than {SfxEventXmlTags.MaxPredelay} for SFXEvent '{sfxEvent.Name}'" + }); + } + + if (sfxEvent.MinPostdelay > sfxEvent.MaxPostdelay) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"{SfxEventXmlTags.MinPostdelay} should not be higher than {SfxEventXmlTags.MaxPostdelay} for SFXEvent '{sfxEvent.Name}'" + }); + } + + sfxEvent.FixupValues(); } - protected override bool ParseTag(XElement tag, SfxEvent sfxEvent, in IReadOnlyFrugalValueListDictionary parsedEntries) + protected override bool ParseTag(XElement tag, SfxEvent sfxEvent, + in IReadOnlyFrugalValueListDictionary parsedEntries) { - switch (tag.Name.LocalName) + if (tag.Name.LocalName == SfxEventXmlTags.UsePreset) { - case SfxEventXmlTags.OverlapTest: - sfxEvent.OverlapTestName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.ChainedSfxEvent: - sfxEvent.ChainedSfxEventName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.UsePreset: + var presetName = PetroglyphXmlStringParser.Instance.Parse(tag); + + Debug.Assert(!string.IsNullOrEmpty(presetName)); + + var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.DefaultPGEncoding); + if (presetNameCrc != default && parsedEntries.TryGetFirstValue(presetNameCrc, out var preset)) + sfxEvent.ApplyPreset(preset); + else { - var presetName = PetroglyphXmlStringParser.Instance.Parse(tag); - var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.DefaultPGEncoding); - if (presetNameCrc != default && parsedEntries.TryGetFirstValue(presetNameCrc, out var preset)) - sfxEvent.ApplyPreset(preset); - else + ErrorReporter?.Report(new XmlError(this, tag) { - ErrorReporter?.Report(new XmlError(this, tag) - { - Message = $"Cannot to find preset '{presetName}' for SFXEvent '{sfxEvent.Name}'", - ErrorKind = XmlParseErrorKind.MissingReference - }); - } - return true; + Message = $"Cannot to find preset '{presetName}' for SFXEvent '{sfxEvent.Name}'", + ErrorKind = XmlParseErrorKind.MissingReference + }); } - case SfxEventXmlTags.IsPreset: - sfxEvent.IsPreset = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.Is3D: - sfxEvent.Is3D = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.Is2D: - sfxEvent.Is2D = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.IsGui: - sfxEvent.IsGui = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.IsHudVo: - sfxEvent.IsHudVo = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.IsUnitResponseVo: - sfxEvent.IsUnitResponseVo = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.IsAmbientVo: - sfxEvent.IsAmbientVo = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.Localize: - sfxEvent.IsLocalized = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.PlaySequentially: - sfxEvent.PlaySequentially = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.KillsPreviousObjectSFX: - sfxEvent.KillsPreviousObjectsSfx = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - - case SfxEventXmlTags.Samples: - sfxEvent.Samples = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case SfxEventXmlTags.PreSamples: - sfxEvent.PreSamples = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case SfxEventXmlTags.PostSamples: - sfxEvent.PostSamples = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case SfxEventXmlTags.TextID: - sfxEvent.LocalizedTextIDs = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - - case SfxEventXmlTags.Priority: - sfxEvent.Priority = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinPriorityValue, SfxEvent.MaxPriorityValue); - return true; - case SfxEventXmlTags.MinPitch: - sfxEvent.MinPitch = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); - return true; - case SfxEventXmlTags.MaxPitch: - sfxEvent.MaxPitch = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); - return true; - case SfxEventXmlTags.MinPan2D: - sfxEvent.MinPan2D = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); - return true; - case SfxEventXmlTags.MaxPan2D: - sfxEvent.MaxPan2D = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); - return true; - case SfxEventXmlTags.PlayCount: - sfxEvent.PlayCount = (sbyte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.InfinitivePlayCount, sbyte.MaxValue); - return true; - case SfxEventXmlTags.MaxInstances: - sfxEvent.MaxInstances = (sbyte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinMaxInstances, sbyte.MaxValue); - return true; - - case SfxEventXmlTags.Probability: - sfxEvent.Probability = PetroglyphXmlMax100ByteParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxProbability); - return true; - case SfxEventXmlTags.MinVolume: - sfxEvent.MinVolume = PetroglyphXmlMax100ByteParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); - return true; - case SfxEventXmlTags.MaxVolume: - sfxEvent.MaxVolume = PetroglyphXmlMax100ByteParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); - return true; - - case SfxEventXmlTags.MinPredelay: - sfxEvent.MinPredelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.MaxPredelay: - sfxEvent.MaxPredelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.MinPostdelay: - sfxEvent.MinPostdelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - case SfxEventXmlTags.MaxPostdelay: - sfxEvent.MaxPostdelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - - case SfxEventXmlTags.LoopFadeInSeconds: - sfxEvent.LoopFadeInSeconds = PetroglyphXmlFloatParser.Instance.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); - return true; - case SfxEventXmlTags.LoopFadeOutSeconds: - sfxEvent.LoopFadeOutSeconds = PetroglyphXmlFloatParser.Instance.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); - return true; - case SfxEventXmlTags.VolumeSaturationDistance: - // I think it was planned at some time to support -1.0 and >= 0.0, since you don't get a warning when -1.0 is coded - // but the Engine coerces anything < 0.0 to 0.0. - sfxEvent.VolumeSaturationDistance = PetroglyphXmlFloatParser.Instance.ParseAtLeast(tag, SfxEvent.MinVolumeSaturation); - return true; - default: return false; + // Set the preset value regardless whether we found the SFXEvent or not. + sfxEvent.UsePresetName = presetName; + + return true; } + + return base.ParseTag(tag, sfxEvent, parsedEntries); } - private sealed class SfxEventXmlTagMapper(IServiceProvider serviceProvider) : XmlTagMapper(serviceProvider) + + internal sealed class SfxEventXmlTagMapper(IServiceProvider serviceProvider) + : XmlTagMapper(serviceProvider) { protected override void BuildMappings() { AddMapping( - "OverlapTestName", + SfxEventXmlTags.IsPreset, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.IsPreset = val); + AddMapping( + SfxEventXmlTags.UsePreset, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.UsePresetName = val); + AddMapping( + SfxEventXmlTags.Samples, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.Samples = new ReadOnlyCollection(val)); + AddMapping( + SfxEventXmlTags.PreSamples, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.PreSamples = new ReadOnlyCollection(val)); + AddMapping( + SfxEventXmlTags.PostSamples, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.PostSamples = new ReadOnlyCollection(val)); + AddMapping( + SfxEventXmlTags.TextID, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.LocalizedTextIDs = new ReadOnlyCollection(val)); + AddMapping( + SfxEventXmlTags.PlaySequentially, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.PlaySequentially = val); + AddMapping( + SfxEventXmlTags.Priority, + x => PetroglyphXmlByteParser.Instance.ParseClamped(x, SfxEvent.MinPriorityValue, SfxEvent.MaxPriorityValue), + (obj, val) => obj.Priority = val); + AddMapping( + SfxEventXmlTags.Probability, + x => PetroglyphXmlBytePercentParser.Instance.ParseAtMost(x, SfxEvent.MaxProbabilityValue), + (obj, val) => obj.Probability = val); + AddMapping( + SfxEventXmlTags.PlayCount, + x => PetroglyphXmlSByteParser.Instance.ParseAtLeast(x, SfxEvent.InfinitivePlayCountValue), + (obj, val) => obj.PlayCount = val); + AddMapping( + SfxEventXmlTags.LoopFadeInSeconds, + x => PetroglyphXmlFloatParser.Instance.ParseAtLeast(x, SfxEvent.MinLoopSecondsValue), + (obj, val) => obj.LoopFadeInSeconds = val); + AddMapping( + SfxEventXmlTags.LoopFadeOutSeconds, + x => PetroglyphXmlFloatParser.Instance.ParseAtLeast(x, SfxEvent.MinLoopSecondsValue), + (obj, val) => obj.LoopFadeOutSeconds = val); + AddMapping( + SfxEventXmlTags.MaxInstances, + x => PetroglyphXmlSByteParser.Instance.ParseAtLeast(x, SfxEvent.MinMaxInstancesValue), + (obj, val) => obj.MaxInstances = val); + AddMapping( + SfxEventXmlTags.MinVolume, + x => PetroglyphXmlBytePercentParser.Instance.ParseAtMost(x, SfxEvent.MaxVolumeValue), + (obj, val) => obj.MinVolume = val); + AddMapping( + SfxEventXmlTags.MaxVolume, + x => PetroglyphXmlBytePercentParser.Instance.ParseAtMost(x, SfxEvent.MaxVolumeValue), + (obj, val) => obj.MaxVolume = val); + AddMapping( + SfxEventXmlTags.MinPitch, + x => PetroglyphXmlByteParser.Instance.ParseClamped(x, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue), + (obj, val) => obj.MinPitch = val); + AddMapping( + SfxEventXmlTags.MaxPitch, + x => PetroglyphXmlByteParser.Instance.ParseClamped(x, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue), + (obj, val) => obj.MaxPitch = val); + AddMapping( + SfxEventXmlTags.MinPan2D, + x => PetroglyphXmlByteParser.Instance.ParseAtMost(x, SfxEvent.MaxPan2dValue), + (obj, val) => obj.MinPan2D = val); + AddMapping( + SfxEventXmlTags.MaxPan2D, + x => PetroglyphXmlByteParser.Instance.ParseAtMost(x, SfxEvent.MaxPan2dValue), + (obj, val) => obj.MaxPan2D = val); + AddMapping( + SfxEventXmlTags.MinPredelay, + PetroglyphXmlUnsignedIntegerParser.Instance.Parse, + (obj, val) => obj.MinPredelay = val); + AddMapping( + SfxEventXmlTags.MaxPredelay, + PetroglyphXmlUnsignedIntegerParser.Instance.Parse, + (obj, val) => obj.MaxPredelay = val); + AddMapping( + SfxEventXmlTags.MinPostdelay, + PetroglyphXmlUnsignedIntegerParser.Instance.Parse, + (obj, val) => obj.MinPostdelay = val); + AddMapping( + SfxEventXmlTags.MaxPostdelay, + PetroglyphXmlUnsignedIntegerParser.Instance.Parse, + (obj, val) => obj.MaxPostdelay = val); + AddMapping( + SfxEventXmlTags.VolumeSaturationDistance, + // I think it was planned at some time to support -1.0 and >= 0.0, since you don't get a warning when -1.0 is coded + // but the Engine coerces anything < 0.0 to 0.0. + x => PetroglyphXmlFloatParser.Instance.ParseAtLeast(x, SfxEvent.MinVolumeSaturationValue), + (obj, val) => obj.VolumeSaturationDistance = val); + AddMapping( + SfxEventXmlTags.KillsPreviousObjectSFX, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.KillsPreviousObjectsSfx = val); + AddMapping( + SfxEventXmlTags.OverlapTest, PetroglyphXmlStringParser.Instance.Parse, (obj, val) => obj.OverlapTestName = val); + AddMapping( + SfxEventXmlTags.Localize, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.IsLocalized = val); + AddMapping( + SfxEventXmlTags.Is2D, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Is2D = val); + AddMapping( + SfxEventXmlTags.Is3D, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Is3D = val); + AddMapping( + SfxEventXmlTags.IsGui, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.IsGui = val); + AddMapping( + SfxEventXmlTags.IsHudVo, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.IsHudVo = val); + AddMapping( + SfxEventXmlTags.IsUnitResponseVo, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.IsUnitResponseVo = val); + AddMapping( + SfxEventXmlTags.IsAmbientVo, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.IsAmbientVo = val); + AddMapping( + SfxEventXmlTags.ChainedSfxEvent, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.ChainedSfxEventName = val); } } + + internal static class SfxEventXmlTags + { + internal const string PresetXRef = "XREF_PRESET"; + internal const string IsPreset = "Is_Preset"; + internal const string UsePreset = "Use_Preset"; + internal const string Samples = "Samples"; + internal const string PreSamples = "Pre_Samples"; + internal const string PostSamples = "Post_Samples"; + internal const string TextID = "Text_ID"; + internal const string PlaySequentially = "Play_Sequentially"; + internal const string Priority = "Priority"; + internal const string Probability = "Probability"; + internal const string PlayCount = "Play_Count"; + internal const string LoopFadeInSeconds = "Loop_Fade_In_Seconds"; + internal const string LoopFadeOutSeconds = "Loop_Fade_Out_Seconds"; + internal const string MaxInstances = "Max_Instances"; + internal const string MinVolume = "Min_Volume"; + internal const string MaxVolume = "Max_Volume"; + internal const string MinPitch = "Min_Pitch"; + internal const string MaxPitch = "Max_Pitch"; + internal const string MinPan2D = "Min_Pan2D"; + internal const string MaxPan2D = "Max_Pan2D"; + internal const string MinPredelay = "Min_Predelay"; + internal const string MaxPredelay = "Max_Predelay"; + internal const string MinPostdelay = "Min_Postdelay"; + internal const string MaxPostdelay = "Max_Postdelay"; + internal const string VolumeSaturationDistance = "Volume_Saturation_Distance"; + internal const string KillsPreviousObjectSFX = "Kills_Previous_Object_SFX"; + internal const string OverlapTest = "Overlap_Test"; + internal const string Localize = "Localize"; + internal const string Is2D = "Is_2D"; + internal const string Is3D = "Is_3D"; + internal const string IsGui = "Is_GUI"; + internal const string IsHudVo = "Is_HUD_VO"; + internal const string IsUnitResponseVo = "Is_Unit_Response_VO"; + internal const string IsAmbientVo = "Is_Ambient_VO"; + internal const string ChainedSfxEvent = "Chained_SFXEvent"; + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/CommandBarComponentTags.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/CommandBarComponentTags.cs deleted file mode 100644 index bc5498b..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/CommandBarComponentTags.cs +++ /dev/null @@ -1,108 +0,0 @@ -namespace PG.StarWarsGame.Engine.Xml.Tags; - -public static class CommandBarComponentTags -{ - public const string SelectedTextureName = "Selected_Texture_Name"; - public const string BlankTextureName = "Blank_Texture_Name"; - public const string IconTextureName = "Icon_Texture_Name"; - public const string IconAlternateTextureName = "Icon_Alternate_Texture_Name"; - public const string MouseOverTextureName = "Mouse_Over_Texture_Name"; - public const string DisabledTextureName = "Disabled_Texture_Name"; - public const string FlashTextureName = "Flash_Texture_Name"; - public const string BarTextureName = "Bar_Texture_Name"; - public const string BarOverlayName = "Bar_Overlay_Name"; - public const string BuildTextureName = "Build_Texture_Name"; - public const string ModelName = "Model_Name"; - public const string BoneName = "Bone_Name"; - public const string CursorTextureName = "Cursor_Texture_Name"; - public const string FontName = "Font_Name"; - public const string AlternateFontName = "Alternate_Font_Name"; - public const string TooltipText = "Tooltip_Text"; - public const string ClickSfx = "Click_SFX"; - public const string MouseOverSfx = "Mouse_Over_SFX"; - public const string LowerEffectTextureName = "Lower_Effect_Texture_Name"; - public const string UpperEffectTextureName = "Upper_Effect_Texture_Name"; - public const string OverlayTextureName = "Overlay_Texture_Name"; - public const string Overlay2TextureName = "Overlay2_Texture_Name"; - public const string RightClickSfx = "Right_Click_SFX"; - public const string Type = "Type"; - public const string Group = "Group"; - public const string DragAndDrop = "Drag_And_Drop"; - public const string DragSelect = "Drag_Select"; - public const string Receptor = "Receptor"; - public const string Toggle = "Toggle"; - public const string Tab = "Tab"; - public const string AssociatedText = "Associated_Text"; - public const string Hidden = "Hidden"; - public const string Scale = "Scale"; - public const string Color = "Color"; - public const string TextColor = "Text_Color"; - public const string TextColor2 = "Text_Color2"; - public const string Size = "Size"; - public const string ClearColor = "Clear_Color"; - public const string Disabled = "Disabled"; - public const string SwapTexture = "Swap_Texture"; - public const string BaseLayer = "Base_Layer"; - public const string DrawAdditive = "Draw_Additive"; - public const string TextOffset = "Text_Offset"; - public const string TextOffset2 = "Text_Offset2"; - public const string Offset = "Offset"; - public const string DefaultOffset = "Default_Offset"; - public const string DefaultOffsetWidescreen = "Default_Offset_Widescreen"; - public const string IconOffset = "Icon_Offset"; - public const string MouseOverOffset = "Mouse_Over_Offset"; - public const string DisabledOffset = "Disabled_Offset"; - public const string BuildDialOffset = "Build_Dial_Offset"; - public const string BuildDial2Offset = "Build_Dial2_Offset"; - public const string LowerEffectOffset = "Lower_Effect_Offset"; - public const string UpperEffectOffset = "Upper_Effect_Offset"; - public const string OverlayOffset = "Overlay_Offset"; - public const string Overlay2Offset = "Overlay2_Offset"; - public const string Editable = "Editable"; - public const string MaxTextLength = "Max_Text_Length"; - public const string BlinkRate = "Blink_Rate"; - public const string FontPointSize = "Font_Point_Size"; - public const string TextOutline = "Text_Outline"; - public const string MaxTextWidth = "Max_Text_Width"; - public const string Stackable = "Stackable"; - public const string ModelOffsetX = "Model_Offset_X"; - public const string ModelOffsetY = "Model_Offset_Y"; - public const string ScaleModelX = "Scale_Model_X"; - public const string ScaleModelY = "Scale_Model_Y"; - public const string Collideable = "Collideable"; - public const string TextEmboss = "Text_Emboss"; - public const string ShouldGhost = "Should_Ghost"; - public const string GhostBaseOnly = "Ghost_Base_Only"; - public const string MaxBarLevel = "Max_Bar_Level"; - public const string MaxBarColor = "Max_Bar_Color"; - public const string CrossFade = "Cross_Fade"; - public const string LeftJustified = "Left_Justified"; - public const string RightJustified = "Right_Justified"; - public const string NoShell = "No_Shell"; - public const string SnapDrag = "Snap_Drag"; - public const string SnapLocation = "Snap_Location"; - public const string BlinkDuration = "Blink_Duration"; - public const string ScaleDuration = "Scale_Duration"; - public const string OffsetRender = "Offset_Render"; - public const string BlinkFade = "Blink_Fade"; - public const string NoHiddenCollision = "No_Hidden_Collision"; - public const string ManualOffset = "Manual_Offset"; - public const string SelectedAlpha = "Selected_Alpha"; - public const string PixelAlign = "Pixel_Align"; - public const string CanDragStack = "Can_Drag_Stack"; - public const string CanAnimate = "Can_Animate"; - public const string AnimFps = "Anim_FPS"; - public const string LoopAnim = "Loop_Anim"; - public const string SmoothBar = "Smooth_Bar"; - public const string OutlinedBar = "Outlined_Bar"; - public const string DragBack = "Drag_Back"; - public const string LowerEffectAdditive = "Lower_Effect_Additive"; - public const string UpperEffectAdditive = "Upper_Effect_Additive"; - public const string ClickShift = "Click_Shift"; - public const string TutorialScene = "Tutorial_Scene"; - public const string DialogScene = "Dialog_Scene"; - public const string ShouldRenderAtDragPos = "Should_Render_At_Drag_Pos"; - public const string DisableDarken = "Disable_Darken"; - public const string AnimateBack = "Animate_Back"; - public const string AnimateUpperEffect = "Animate_Upper_Effect"; -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/ComponentTextureKeyExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/ComponentTextureKeyExtensions.cs deleted file mode 100644 index c072c90..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/ComponentTextureKeyExtensions.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using PG.StarWarsGame.Engine.GuiDialog; - -namespace PG.StarWarsGame.Engine.Xml.Tags; - -internal static class ComponentTextureKeyExtensions -{ - public static bool TryConvertToKey(ReadOnlySpan keyValue, out GuiComponentType key) - { - key = keyValue switch - { - "Button_Left" => GuiComponentType.ButtonLeft, - "Button_Middle" => GuiComponentType.ButtonMiddle, - "Button_Right" => GuiComponentType.ButtonRight, - "Button_Left_Mouse_Over" => GuiComponentType.ButtonLeftMouseOver, - "Button_Middle_Mouse_Over" => GuiComponentType.ButtonMiddleMouseOver, - "Button_Right_Mouse_Over" => GuiComponentType.ButtonRightMouseOver, - "Button_Left_Pressed" => GuiComponentType.ButtonLeftPressed, - "Button_Middle_Pressed" => GuiComponentType.ButtonMiddlePressed, - "Button_Right_Pressed" => GuiComponentType.ButtonRightPressed, - "Button_Left_Disabled" => GuiComponentType.ButtonLeftDisabled, - "Button_Middle_Disabled" => GuiComponentType.ButtonMiddleDisabled, - "Button_Right_Disabled" => GuiComponentType.ButtonRightDisabled, - - "Check_Off" => GuiComponentType.CheckOff, - "Check_On" => GuiComponentType.CheckOn, - - "Dial_Left" => GuiComponentType.DialLeft, - "Dial_Middle" => GuiComponentType.DialMiddle, - "Dial_Right" => GuiComponentType.DialRight, - "Dial_Plus" => GuiComponentType.DialPlus, - "Dial_Plus_Mouse_Over" => GuiComponentType.DialPlusMouseOver, - "Dial_Plus_Pressed" => GuiComponentType.DialPlusPressed, - "Dial_Minus" => GuiComponentType.DialMinus, - "Dial_Minus_Mouse_Over" => GuiComponentType.DialMinusMouseOver, - "Dial_Minus_Pressed" => GuiComponentType.DialMinusPressed, - "Dial_Tab" => GuiComponentType.DialTab, - - "Frame_Bottom" => GuiComponentType.FrameBottom, - "Frame_Bottom_Left" => GuiComponentType.FrameBottomLeft, - "Frame_Bottom_Right" => GuiComponentType.FrameBottomRight, - "Frame_Background" => GuiComponentType.FrameBackground, - "Frame_Left" => GuiComponentType.FrameLeft, - "Frame_Right" => GuiComponentType.FrameRight, - "Frame_Top" => GuiComponentType.FrameTop, - "Frame_Top_Left" => GuiComponentType.FrameTopLeft, - "Frame_Top_Right" => GuiComponentType.FrameTopRight, - "Frame_Top_Transition_Left" => GuiComponentType.FrameTopTransitionLeft, - "Frame_Top_Transition_Right" => GuiComponentType.FrameTopTransitionRight, - "Frame_Bottom_Transition_Left" => GuiComponentType.FrameBottomTransitionLeft, - "Frame_Bottom_Transition_Right" => GuiComponentType.FrameBottomTransitionRight, - "Frame_Left_Transition_Top" => GuiComponentType.FrameLeftTransitionTop, - "Frame_Left_Transition_Bottom" => GuiComponentType.FrameLeftTransitionBottom, - "Frame_Right_Transition_Top" => GuiComponentType.FrameRightTransitionTop, - "Frame_Right_Transition_Bottom" => GuiComponentType.FrameRightTransitionBottom, - - "Radio_Off" => GuiComponentType.RadioOff, - "Radio_On" => GuiComponentType.RadioOn, - "Radio_Disabled" => GuiComponentType.RadioDisabled, - "Radio_Mouse_Over" => GuiComponentType.RadioMouseOver, - - "Scroll_Down_Button" => GuiComponentType.ScrollDownButton, - "Scroll_Down_Button_Pressed" => GuiComponentType.ScrollDownButtonPressed, - "Scroll_Down_Button_Mouse_Over" => GuiComponentType.ScrollDownButtonMouseOver, - "Scroll_Down_Button_Disabled" => GuiComponentType.ScrollDownButtonDisabled, - "Scroll_Middle" => GuiComponentType.ScrollMiddle, - "Scroll_Middle_Disabled" => GuiComponentType.ScrollMiddleDisabled, - "Scroll_Tab" => GuiComponentType.ScrollTab, - "Scroll_Tab_Disabled" => GuiComponentType.ScrollTabDisabled, - "Scroll_Up_Button" => GuiComponentType.ScrollUpButton, - "Scroll_Up_Button_Pressed" => GuiComponentType.ScrollUpButtonPressed, - "Scroll_Up_Button_Mouse_Over" => GuiComponentType.ScrollUpButtonMouseOver, - "Scroll_Up_Button_Disabled" => GuiComponentType.ScrollUpButtonDisabled, - - "Trackbar_Scroll_Down_Button" => GuiComponentType.TrackbarScrollDownButton, - "Trackbar_Scroll_Down_Button_Pressed" => GuiComponentType.TrackbarScrollDownButtonPressed, - "Trackbar_Scroll_Down_Button_Mouse_Over" => GuiComponentType.TrackbarScrollDownButtonMouseOver, - "Trackbar_Scroll_Down_Button_Disabled" => GuiComponentType.TrackbarScrollDownButtonDisabled, - "Trackbar_Scroll_Middle" => GuiComponentType.TrackbarScrollMiddle, - "Trackbar_Scroll_Middle_Disabled" => GuiComponentType.TrackbarScrollMiddleDisabled, - "Trackbar_Scroll_Tab" => GuiComponentType.TrackbarScrollTab, - "Trackbar_Scroll_Tab_Disabled" => GuiComponentType.TrackbarScrollTabDisabled, - "Trackbar_Scroll_Up_Button" => GuiComponentType.TrackbarScrollUpButton, - "Trackbar_Scroll_Up_Button_Pressed" => GuiComponentType.TrackbarScrollUpButtonPressed, - "Trackbar_Scroll_Up_Button_Mouse_Over" => GuiComponentType.TrackbarScrollUpButtonMouseOver, - "Trackbar_Scroll_Up_Button_Disabled" => GuiComponentType.TrackbarScrollUpButtonDisabled, - - "Small_Frame_Bottom" => GuiComponentType.SmallFrameBottom, - "Small_Frame_Bottom_Left" => GuiComponentType.SmallFrameBottomLeft, - "Small_Frame_Bottom_Right" => GuiComponentType.SmallFrameBottomRight, - "Small_Frame_Left" => GuiComponentType.SmallFrameMiddleLeft, - "Small_Frame_Right" => GuiComponentType.SmallFrameMiddleRight, - "Small_Frame_Top" => GuiComponentType.SmallFrameTop, - "Small_Frame_Top_Left" => GuiComponentType.SmallFrameTopLeft, - "Small_Frame_Top_Right" => GuiComponentType.SmallFrameTopRight, - "Small_Frame_Background" => GuiComponentType.SmallFrameBackground, - - "Combo_Box_Popdown_Button" => GuiComponentType.ComboboxPopdown, - "Combo_Box_Popdown_Button_Pressed" => GuiComponentType.ComboboxPopdownPressed, - "Combo_Box_Popdown_Button_Mouse_Over" => GuiComponentType.ComboboxPopdownMouseOver, - "Combo_Box_Text_Box" => GuiComponentType.ComboboxTextBox, - "Combo_Box_Left_Cap" => GuiComponentType.ComboboxLeftCap, - - "Progress_Bar_Left" => GuiComponentType.ProgressLeft, - "Progress_Bar_Middle_Off" => GuiComponentType.ProgressMiddleOff, - "Progress_Bar_Middle_On" => GuiComponentType.ProgressMiddleOn, - "Progress_Bar_Right" => GuiComponentType.ProgressRight, - - "Scanlines" => GuiComponentType.Scanlines, - _ => (GuiComponentType)int.MaxValue - }; - return (int)key != int.MaxValue; - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/SfxEventXmlTags.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/SfxEventXmlTags.cs deleted file mode 100644 index 959da6b..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Tags/SfxEventXmlTags.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace PG.StarWarsGame.Engine.Xml.Tags; - -public static class SfxEventXmlTags -{ - internal const string PresetXRef = "XREF_PRESET"; - - public const string IsPreset = "Is_Preset"; - public const string UsePreset = "Use_Preset"; - public const string Samples = "Samples"; - public const string PreSamples = "Pre_Samples"; - public const string PostSamples = "Post_Samples"; - public const string TextID = "Text_ID"; - public const string PlaySequentially = "Play_Sequentially"; - public const string Priority = "Priority"; - public const string Probability = "Probability"; - public const string PlayCount = "Play_Count"; - public const string LoopFadeInSeconds = "Loop_Fade_In_Seconds"; - public const string LoopFadeOutSeconds = "Loop_Fade_Out_Seconds"; - public const string MaxInstances = "Max_Instances"; - public const string MinVolume = "Min_Volume"; - public const string MaxVolume = "Max_Volume"; - public const string MinPitch = "Min_Pitch"; - public const string MaxPitch = "Max_Pitch"; - public const string MinPan2D = "Min_Pan2D"; - public const string MaxPan2D = "Max_Pan2D"; - public const string MinPredelay = "Min_Predelay"; - public const string MaxPredelay = "Max_Predelay"; - public const string MinPostdelay = "Min_Postdelay"; - public const string MaxPostdelay = "Max_Postdelay"; - public const string VolumeSaturationDistance = "Volume_Saturation_Distance"; - public const string KillsPreviousObjectSFX = "Kills_Previous_Object_SFX"; - public const string OverlapTest = "Overlap_Test"; - public const string Localize = "Localize"; - public const string Is2D = "Is_2D"; - public const string Is3D = "Is_3D"; - public const string IsGui = "Is_GUI"; - public const string IsHudVo = "Is_HUD_VO"; - public const string IsUnitResponseVo = "Is_Unit_Response_VO"; - public const string IsAmbientVo = "Is_Ambient_VO"; - public const string ChainedSfxEvent = "Chained_SFXEvent"; -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs index c3827fa..3952f25 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParseSettings.cs @@ -6,5 +6,5 @@ public sealed record PetroglyphStarWarsGameXmlParseSettings public bool InvalidFilesListXmlFailsInitialization { get; init; } = true; - public bool InvalidContainerXmlFailsInitialization { get; init; } = false; + public bool InvalidObjectXmlFailsInitialization { get; init; } = false; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs index 7e27965..7beb801 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphStarWarsGameXmlParser.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using System.Xml; using AnakinRaW.CommonUtilities.Collections; @@ -39,72 +40,23 @@ public PetroglyphStarWarsGameXmlParser( Name = GetType().FullName!; } - public XmlFileList ParseFileList(string xmlFile) + public T? ParseFile(string xmlFile, XmlFileParser parser) where T : XmlObject { - Logger.LogDebug("Parsing container data '{XmlFile}'", xmlFile); - - using var containerStream = _gameRepository.TryOpenFile(xmlFile); - if (containerStream == null) - { - var message = $"Could not find XML file '{xmlFile}'"; - - Logger.LogWarning(message); - - _reporter.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, null)) - { - Message = message, - ErrorKind = XmlParseErrorKind.MissingFile - }); - - if (_settings.InvalidFilesListXmlFailsInitialization) - { - _reporter.Report(new InitializationError - { - GameManager = _settings.GameManager, - Message = message, - }); - } - - return XmlFileList.Empty(new XmlLocationInfo(xmlFile, null)); - } - - XmlFileList? container; - var containerParser = new XmlFileListParser(Services, _reporter); - - try - { - container = containerParser.ParseFile(containerStream); - if (container is null) - throw new XmlException($"Unable to parse XML container file '{xmlFile}'."); - } - catch (XmlException e) - { - _reporter.Report(new XmlError(this, locationInfo: new XmlLocationInfo(xmlFile, e.LineNumber)) - { - ErrorKind = XmlParseErrorKind.Unknown, - Message = e.Message, - }); - - if (_settings.InvalidFilesListXmlFailsInitialization) - { - _reporter.Report(new InitializationError - { - GameManager = _settings.GameManager, - Message = e.Message, - }); - } - - return XmlFileList.Empty(new XmlLocationInfo(xmlFile, null)); - } + return ParseCore(xmlFile, parser.ParseFile, () => null); + } - return container; + public XmlFileList ParseFileList(string xmlFile) + { + return ParseCore(xmlFile, + stream => new XmlFileListParser(Services, _reporter).ParseFile(stream), + () => XmlFileList.Empty(new XmlLocationInfo(xmlFile, null))); } public void ParseEntriesFromFileListXml( string xmlFile, string lookupPath, FrugalValueListDictionary entries, - Action? onFileParseAction = null) where T : NamedXmlObject + Action? onParseContainerAction = null) where T : NamedXmlObject { var container = ParseFileList(xmlFile); @@ -115,17 +67,28 @@ public void ParseEntriesFromFileListXml( foreach (var file in xmlFiles) { - onFileParseAction?.Invoke(file); - if (!ParseEntriesFromContainerFile(file, parser, entries)) + onParseContainerAction?.Invoke(file); + if (!ParseObjectsFromContainerFile(file, parser, entries)) return; } } - - public bool ParseEntriesFromContainerFile( + + public bool ParseObjectsFromContainerFile( string xmlFile, XmlContainerFileParser parser, IFrugalValueListDictionary entries) where T : NamedXmlObject { + return ParseCore(xmlFile, stream => + { + parser.ParseFile(stream, entries); + return true; + }, () => _settings.InvalidObjectXmlFailsInitialization); + } + + private T ParseCore(string xmlFile, Func parseAction, Func invalidFileAction) + { + Logger.LogDebug("Parsing file '{XmlFile}'", xmlFile); + using var fileStream = _gameRepository.TryOpenFile(xmlFile); if (fileStream is null) @@ -138,8 +101,8 @@ public bool ParseEntriesFromContainerFile( Message = message, ErrorKind = XmlParseErrorKind.MissingFile }); - - if (_settings.InvalidContainerXmlFailsInitialization) + + if (_settings.InvalidObjectXmlFailsInitialization) { _reporter.Report(new InitializationError { @@ -148,15 +111,12 @@ public bool ParseEntriesFromContainerFile( }); } - return _settings.InvalidContainerXmlFailsInitialization; + return invalidFileAction(); } - Logger.LogDebug("Parsing File '{File}'", xmlFile); - try { - parser.ParseFile(fileStream, entries); - return true; + return parseAction(fileStream); } catch (XmlException e) { @@ -165,7 +125,7 @@ public bool ParseEntriesFromContainerFile( ErrorKind = XmlParseErrorKind.Unknown, Message = e.Message, }); - if (_settings.InvalidContainerXmlFailsInitialization) + if (_settings.InvalidObjectXmlFailsInitialization) { _reporter.Report(new InitializationError { @@ -173,7 +133,8 @@ public bool ParseEntriesFromContainerFile( Message = e.Message, }); } - return _settings.InvalidContainerXmlFailsInitialization; + + return invalidFileAction(); } } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj index 052190c..a92efb1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/PG.StarWarsGame.Files.ALO.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net10.0 PG.StarWarsGame.Files.ALO PG.StarWarsGame.Files.ALO AlamoEngineTools.PG.StarWarsGame.Files.ALO diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj index 5328887..9fe818c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net10.0 PG.StarWarsGame.Files.ChunkFiles PG.StarWarsGame.Files.ChunkFiles AlamoEngineTools.PG.StarWarsGame.Files.ChunkFiles diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs index 090d4d9..41cde2c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlObject.cs @@ -3,8 +3,4 @@ public abstract class XmlObject(XmlLocationInfo location) { public XmlLocationInfo Location { get; } = location; - - public virtual void CoerceValues() - { - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs index 861e441..6308a74 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs @@ -54,5 +54,9 @@ public enum XmlParseErrorKind /// /// The XML tag has child elements. /// - TagHasElements = 12 + TagHasElements = 12, + /// + /// The name of the XML Element (not the value of the attribute "Name") has an unexpected name. + /// + UnexceptedElementName = 13, } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj index 5feadba..961034b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net10.0 PG.StarWarsGame.Files.XML PG.StarWarsGame.Files.XML AlamoEngineTools.PG.StarWarsGame.Files.XML diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs index f4eb0af..dd9ac3d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/XmlObjectParserBase.cs @@ -12,7 +12,7 @@ public abstract class XmlObjectParserBase(IXmlTagMapper true; - protected virtual void ValidateValues(TObject namedXmlObject, XElement element) + protected virtual void ValidateAndFixupValues(TObject namedXmlObject, XElement element) { } @@ -20,19 +20,23 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) { foreach (var tag in element.Elements()) { - if (string.IsNullOrEmpty(tag.Value) && IgnoreEmptyValue) - continue; - - if (tag.HasElements) - Parse(xmlObject, tag, in state); - - if (!ParseTag(tag, xmlObject, state)) + if (!tag.HasElements) { - ErrorReporter?.Report(new XmlError(this, element) + if (string.IsNullOrEmpty(tag.PGValue) && IgnoreEmptyValue) + continue; + + if (!ParseTag(tag, xmlObject, state)) { - Message = $"The node '{tag.Name}' is not supported.", - ErrorKind = XmlParseErrorKind.UnknownNode - }); + ErrorReporter?.Report(new XmlError(this, tag) + { + Message = $"The node '{tag.Name}' is not supported.", + ErrorKind = XmlParseErrorKind.UnknownNode + }); + } + } + else + { + Parse(xmlObject, tag, in state); } } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs index a31f2aa..140dcd0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/NamedXmlObjectParser.cs @@ -1,5 +1,4 @@ using System; -using System.Text; using System.Xml.Linq; using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.DependencyInjection; @@ -25,8 +24,7 @@ public T Parse(XElement element, IReadOnlyFrugalValueListDictionary pa var name = GetXmlObjectName(element, out nameCrc); var namedXmlObject = CreateXmlObject(name, nameCrc, element, XmlLocationInfo.FromElement(element)); Parse(namedXmlObject, element, parsedEntries); - ValidateValues(namedXmlObject, element); - namedXmlObject.CoerceValues(); + ValidateAndFixupValues(namedXmlObject, element); return namedXmlObject; } @@ -36,8 +34,8 @@ private string GetXmlObjectName(XElement element, out Crc32 crc32) { GetNameAttributeValue(element, out var name); crc32 = UpperCaseNameForCrc - ? HashingService.GetCrc32Upper(name.AsSpan(), Encoding.ASCII) - : HashingService.GetCrc32(name.AsSpan(), Encoding.ASCII); + ? HashingService.GetCrc32Upper(name.AsSpan(), XmlFileConstants.XmlEncoding) + : HashingService.GetCrc32(name.AsSpan(), XmlFileConstants.XmlEncoding); if (crc32 == default) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs index 34e5317..ff175ed 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs @@ -17,10 +17,11 @@ private CommaSeparatedStringKeyValueListParser() } private protected override IList<(string key, string value)> DefaultValue => []; + internal override int EngineDataTypeId => throw new NotImplementedException(); protected internal override IList<(string key, string value)> ParseCore(ReadOnlySpan trimmedValue, XElement element) { - var values = element.Value.Split(','); + var values = element.PGValue.Split(','); // Cases: Empty tag or invalid value (e.g, terrain only, wrong separator, etc.) if (values.Length <= 1) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphNumberParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphNumberParser.cs new file mode 100644 index 0000000..e93b0a6 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphNumberParser.cs @@ -0,0 +1,88 @@ +using System; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Utilities; +#if NET7_0_OR_GREATER +using System.Numerics; +#endif + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class PetroglyphNumberParser : PetroglyphPrimitiveXmlParser where T : struct, IEquatable, IComparable +#if NET10_0_OR_GREATER + , INumber, IMinMaxValue +#endif +{ + +#if NET7_0_OR_GREATER + protected virtual T MaxValue => T.MaxValue; + + protected virtual T MinValue => T.MinValue; +#else + protected abstract T MaxValue { get; } + + protected abstract T MinValue { get; } + +#endif + + public T ParseAtLeast(XElement element, T minValue) + { + if (minValue.CompareTo(MinValue) < 0 || minValue.CompareTo(MaxValue) > 0) + throw new ArgumentOutOfRangeException(nameof(minValue), "minValue is out of range."); + + var value = Parse(element); + var corrected = PGMath.Max(value, minValue); + if (!corrected.Equals(value)) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected value to be at least {minValue} but got value '{value}'.", + }); + } + + return corrected; + } + + public T ParseAtMost(XElement element, T maxValue) + { + if (maxValue.CompareTo(MinValue) < 0 || maxValue.CompareTo(MaxValue) > 0) + throw new ArgumentOutOfRangeException(nameof(maxValue), "maxValue is out of range."); + + var value = Parse(element); + var corrected = PGMath.Min(value, maxValue); + if (!corrected.Equals(value)) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected value to be at least {maxValue} but got value '{value}'.", + }); + } + + return corrected; + } + + + public T ParseClamped(XElement element, T minValue, T maxValue) + { + if (minValue.CompareTo(MinValue) < 0 || minValue.CompareTo(MaxValue) > 0) + throw new ArgumentOutOfRangeException(nameof(minValue), "minValue is out of range."); + if (maxValue.CompareTo(MinValue) < 0 || maxValue.CompareTo(MaxValue) > 0) + throw new ArgumentOutOfRangeException(nameof(maxValue), "maxValue is out of range."); + if (minValue.CompareTo(maxValue) > 0) + throw new ArgumentException("minValue must be less than or equal to maxValue."); + + var value = Parse(element); + var clamped = PGMath.Clamp(value, minValue, maxValue); + if (!value.Equals(clamped)) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected integer between {minValue} and {maxValue} but got value '{value}'.", + }); + } + return clamped; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs index 2cece9c..48c53c3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -1,6 +1,6 @@ -using System; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -8,6 +8,8 @@ public abstract class PetroglyphPrimitiveXmlParser : PetroglyphXmlParserBase { private protected abstract T DefaultValue { get; } + internal abstract int EngineDataTypeId { get; } + private protected PetroglyphPrimitiveXmlParser() : base(PrimitiveXmlErrorReporter.Instance) { } @@ -16,7 +18,7 @@ public T Parse(XElement element) { if (!IsTagValid(element)) return DefaultValue; - var value = element.Value.AsSpan().Trim(); + var value = element.PGValue.AsSpan().Trim(); return value.Length == 0 ? DefaultValue : ParseCore(value, element); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs index e2d5a7a..b52554d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs @@ -7,12 +7,14 @@ public sealed class PetroglyphXmlBooleanParser : PetroglyphPrimitiveXmlParser false; + + internal override int EngineDataTypeId => 0x0; + private PetroglyphXmlBooleanParser() { } - private protected override bool DefaultValue => false; - protected internal override bool ParseCore(ReadOnlySpan trimmedValue, XElement element) { // Yes! The engine only checks if the values is exact 1 or starts with Tt or Yy diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs index 2be5d48..56bcb5c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs @@ -4,16 +4,24 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public sealed class PetroglyphXmlByteParser : PetroglyphPrimitiveXmlParser +public sealed class PetroglyphXmlByteParser : PetroglyphNumberParser { public static readonly PetroglyphXmlByteParser Instance = new(); + private protected override byte DefaultValue => 0; + + internal override int EngineDataTypeId => 0x2; + +#if !NET7_0_OR_GREATER + protected override byte MaxValue => byte.MaxValue; + + protected override byte MinValue => byte.MinValue; +#endif + private PetroglyphXmlByteParser() { } - private protected override byte DefaultValue => 0; - protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XElement element) { var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBytePercentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBytePercentParser.cs new file mode 100644 index 0000000..e419da4 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBytePercentParser.cs @@ -0,0 +1,45 @@ +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; +using System.Xml.Linq; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public sealed class PetroglyphXmlBytePercentParser : PetroglyphNumberParser +{ + public static readonly PetroglyphXmlBytePercentParser Instance = new(); + + private protected override byte DefaultValue => 0; + + internal override int EngineDataTypeId => 0x4; + + protected override byte MaxValue => 100; + +#if !NET7_0_OR_GREATER + protected override byte MinValue => byte.MinValue; +#endif + + private PetroglyphXmlBytePercentParser() + { + } + + protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XElement element) + { + var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); + + if (intValue > MaxValue) + intValue = MaxValue; + + var asByte = (byte)intValue; + // Add additional check (> 100), cause the PG implementation is broken, but we need to stay "bug-compatible". + if (intValue != asByte) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected a byte value (0 - 100) but got value '{asByte}'.", + }); + } + + return asByte; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs index 86ef413..c8d0335 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs @@ -1,36 +1,28 @@ -using System; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System; using System.Globalization; using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers; -public sealed class PetroglyphXmlFloatParser : PetroglyphPrimitiveXmlParser +public sealed class PetroglyphXmlFloatParser : PetroglyphNumberParser { public static readonly PetroglyphXmlFloatParser Instance = new(); private protected override float DefaultValue => 0.0f; - private PetroglyphXmlFloatParser() - { - } + internal override int EngineDataTypeId => 0x8; - public float ParseAtLeast(XElement element, float minValue) - { - var value = Parse(element); - var corrected = Math.Max(value, minValue); - if (corrected != value) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = $"Expected float to be at least {minValue} but got value '{value}'.", - }); - } +#if !NET7_0_OR_GREATER + protected override float MaxValue => float.MaxValue; - return corrected; - } + protected override float MinValue => float.MinValue; +#endif + private PetroglyphXmlFloatParser() + { + } + protected internal override float ParseCore(ReadOnlySpan trimmedValue, XElement element) { // The engine always loads FP numbers a long double and then converts that result to float diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs index 9faf905..485c89f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -1,16 +1,23 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Utilities; using System; using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; -public sealed class PetroglyphXmlIntegerParser : PetroglyphPrimitiveXmlParser +public sealed class PetroglyphXmlIntegerParser : PetroglyphNumberParser { public static readonly PetroglyphXmlIntegerParser Instance = new(); private protected override int DefaultValue => 0; + internal override int EngineDataTypeId => 0x6; + +#if !NET7_0_OR_GREATER + protected override int MaxValue => int.MaxValue; + + protected override int MinValue => int.MinValue; +#endif + private PetroglyphXmlIntegerParser() { } @@ -32,24 +39,9 @@ protected internal override int ParseCore(ReadOnlySpan trimmedValue, XElem ErrorKind = XmlParseErrorKind.MalformedValue, Message = $"Expected integer but got '{trimmedValue.ToString()}'.", }); - return 0; + return DefaultValue; } return i; } - - public int ParseWithRange(XElement element, int minValue, int maxValue) - { - var value = Parse(element); - var clamped = PGMath.Clamp(value, minValue, maxValue); - if (value != clamped) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = $"Expected integer between {minValue} and {maxValue} but got value '{value}'.", - }); - } - return clamped; - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs index d6f1e3e..47a0603 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -20,6 +20,7 @@ public sealed class PetroglyphXmlLooseStringListParser : PetroglyphPrimitiveXmlP public static readonly PetroglyphXmlLooseStringListParser Instance = new(); private protected override IList DefaultValue => []; + internal override int EngineDataTypeId => 0x18; private PetroglyphXmlLooseStringListParser() { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs deleted file mode 100644 index 5d23e6d..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ /dev/null @@ -1,74 +0,0 @@ -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Utilities; -using System; -using System.Xml.Linq; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public sealed class PetroglyphXmlMax100ByteParser : PetroglyphPrimitiveXmlParser -{ - public static readonly PetroglyphXmlMax100ByteParser Instance = new(); - - private protected override byte DefaultValue => 0; - - private PetroglyphXmlMax100ByteParser() - { - } - - protected internal override byte ParseCore(ReadOnlySpan trimmedValue, XElement element) - { - var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); - - if (intValue > 100) - intValue = 100; - - var asByte = (byte)intValue; - if (intValue != asByte) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = $"Expected a byte value (0 - 255) but got value '{intValue}'.", - }); - } - - // Add additional check, cause the PG implementation is broken, but we need to stay "bug-compatible". - if (asByte > 100) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = $"Expected a byte value (0 - 100) but got value '{asByte}'.", - }); - } - - return asByte; - } - - public byte ParseWithRange(XElement element, byte minValue, byte maxValue) - { - if (maxValue > 100) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = $"The provided maxValue '{maxValue}' is above 100.", - }); - } - - // TODO: Do we need to coerce maxValue??? - - var value = Parse(element); - - var clamped = PGMath.Clamp(value, minValue, maxValue); - if (value != clamped) - { - ErrorReporter?.Report(new XmlError(this, element) - { - ErrorKind = XmlParseErrorKind.InvalidValue, - Message = $"Expected byte between {minValue} and {maxValue} but got value '{value}'.", - }); - } - return clamped; - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs index 8f32e50..e040aaf 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs @@ -9,6 +9,7 @@ public sealed class PetroglyphXmlRgbaColorParser : PetroglyphPrimitiveXmlParser< public static readonly PetroglyphXmlRgbaColorParser Instance = new(); private protected override Vector4Int DefaultValue => default; + internal override int EngineDataTypeId => 0x16; private PetroglyphXmlRgbaColorParser() { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlSByteParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlSByteParser.cs new file mode 100644 index 0000000..234b6ae --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlSByteParser.cs @@ -0,0 +1,41 @@ +using System; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public sealed class PetroglyphXmlSByteParser : PetroglyphNumberParser +{ + public static readonly PetroglyphXmlSByteParser Instance = new(); + + private protected override sbyte DefaultValue => 0; + + internal override int EngineDataTypeId => 0x3; + +#if !NET7_0_OR_GREATER + protected override sbyte MaxValue => sbyte.MaxValue; + + protected override sbyte MinValue => sbyte.MinValue; +#endif + + private PetroglyphXmlSByteParser() + { + } + + protected internal override sbyte ParseCore(ReadOnlySpan trimmedValue, XElement element) + { + var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); + + var asSByte = (sbyte)intValue; + if (intValue != asSByte) + { + ErrorReporter?.Report(new XmlError(this, element) + { + ErrorKind = XmlParseErrorKind.InvalidValue, + Message = $"Expected a byte value (0 - 255) but got value '{intValue}'.", + }); + } + + return asSByte; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs index a0dde7f..13cce7e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs @@ -7,12 +7,14 @@ public sealed class PetroglyphXmlStringParser : PetroglyphPrimitiveXmlParser string.Empty; + + internal override int EngineDataTypeId => 0x17; + private PetroglyphXmlStringParser() { } - private protected override string DefaultValue => string.Empty; - protected internal override string ParseCore(ReadOnlySpan trimmedValue, XElement element) { return trimmedValue.ToString(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs index 6c63f7e..420f21d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs @@ -4,12 +4,20 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public sealed class PetroglyphXmlUnsignedIntegerParser : PetroglyphPrimitiveXmlParser +public sealed class PetroglyphXmlUnsignedIntegerParser : PetroglyphNumberParser { public static readonly PetroglyphXmlUnsignedIntegerParser Instance = new(); private protected override uint DefaultValue => 0; + internal override int EngineDataTypeId => 0x5; + +#if !NET7_0_OR_GREATER + protected override uint MaxValue => uint.MaxValue; + + protected override uint MinValue => uint.MinValue; +#endif + private PetroglyphXmlUnsignedIntegerParser() { } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs index da22a09..c35e5b8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs @@ -12,12 +12,14 @@ public sealed class PetroglyphXmlVector2FParser : PetroglyphPrimitiveXmlParser default; + + internal override int EngineDataTypeId => throw new NotImplementedException(); + private PetroglyphXmlVector2FParser() { } - private protected override Vector2 DefaultValue => default; - protected internal override Vector2 ParseCore(ReadOnlySpan trimmedValue, XElement element) { var listOfValues = LooseStringListParser.Parse(element); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index 641f59e..0718ac4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -20,8 +20,8 @@ protected override XmlFileList ParseRoot(XElement element, string fileName) { ErrorReporter?.Report(new XmlError(this, child) { - ErrorKind = XmlParseErrorKind.UnknownNode, - Message = $"Tag '<{tagName}>' is not supported. Only '' is supported.", + ErrorKind = XmlParseErrorKind.UnexceptedElementName, + Message = $"Tag '<{tagName}>' should not be used. Use '' only.", }); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs index c716a6e..039867c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlObjectParser.cs @@ -12,8 +12,7 @@ public TObject Parse(XElement element) { var xmlObject = CreateXmlObject(XmlLocationInfo.FromElement(element)); Parse(xmlObject, element, EmptyParseState.Instance); - ValidateValues(xmlObject, element); - xmlObject.CoerceValues(); + ValidateAndFixupValues(xmlObject, element); ; return xmlObject; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs index bc40317..96e7c7e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using System.Runtime.CompilerServices; namespace PG.StarWarsGame.Files.XML.Utilities; @@ -8,8 +9,11 @@ internal static class PGMath [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Clamp(int value, int min, int max) { +#if NETSTANDARD2_1_OR_GREATER || NET + return Math.Clamp(value, min, max); +#endif if (min > max) - throw new ArgumentException("min cannot be larger than max."); + throw new ArgumentException($"'{max}' cannot be greater than {min}."); if (value < min) return min; return value > max ? max : value; @@ -18,10 +22,60 @@ public static int Clamp(int value, int min, int max) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Clamp(byte value, byte min, byte max) { +#if NETSTANDARD2_1_OR_GREATER || NET + return Math.Clamp(value, min, max); +#endif if (min > max) - throw new ArgumentException("min cannot be larger than max."); + throw new ArgumentException($"'{max}' cannot be greater than {min}."); if (value < min) return min; return value > max ? max : value; } + +#if NET7_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Clamp(T value, T min, T max) where T : INumber + { + if (min > max) + throw new ArgumentException($"'{max}' cannot be greater than {min}."); + if (value < min) + return min; + return value > max ? max : value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Max(T val1, T val2) where T : INumber + { + return (val1 >= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Min(T val1, T val2) where T : INumber + { + return (val1 <= val2) ? val1 : val2; + } +# else + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Clamp(T value, T min, T max) where T : struct, IEquatable, IComparable + { + if (min.CompareTo(max) > 0) + throw new ArgumentException($"'{max}' cannot be greater than {min}."); + if (value.CompareTo(min) < 0) + return min; + return value.CompareTo(max) > 0 ? max : value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Max(T val1, T val2) where T : struct, IEquatable, IComparable + { + return val1.CompareTo(val2) >= 0 ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Min(T val1, T val2) where T : struct, IEquatable, IComparable + { + return val1.CompareTo(val2) <= 0 ? val1 : val2; + } +#endif } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XElementExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XElementExtensions.cs new file mode 100644 index 0000000..eb7a6c0 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XElementExtensions.cs @@ -0,0 +1,15 @@ +using System.Xml.Linq; + +namespace PG.StarWarsGame.Files.XML; + +public static class XElementExtensions +{ + extension(XElement element) + { + /// + /// Gets the value of the element as the Petroglyph engine would parse it. + /// That is, if the element has child elements, it returns an empty string, otherwise it returns the value of the element. + /// + public string PGValue => element.HasElements ? string.Empty : element.Value; + } +} \ No newline at end of file From bd68f7738183cc9e2db765a68a563918419a336a Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 21 Feb 2026 13:13:31 +0100 Subject: [PATCH 13/15] simplified animation type code --- .../PG.StarWarsGame.Engine/GameManagerBase.cs | 1 - .../GuiDialog/Xml/GuiDialogsXml.cs | 3 +- .../PetroglyphEngineServiceContribution.cs | 1 - .../Animations/EawModelAnimationTypes.cs | 109 ------ .../Animations/FocModelAnimationTypes.cs | 124 ------ .../Animations/ModelAnimationType.cs | 170 +++++--- .../SupportedModelAnimationTypes.cs | 365 +++++++----------- .../Rendering/Font/FontManager.cs | 9 +- .../Rendering/Font/WindowsFontManager.cs | 3 +- 9 files changed, 270 insertions(+), 515 deletions(-) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/FocModelAnimationTypes.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs index c49438b..607121d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; using System.Threading; using System.Threading.Tasks; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs index 7621c15..dbaaed6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs @@ -1,5 +1,4 @@ -using PG.StarWarsGame.Engine.Xml; -using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.GuiDialog.Xml; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs index 69ce035..c8b8380 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs deleted file mode 100644 index f1a89c3..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace PG.StarWarsGame.Engine.Rendering.Animations; - -public static class EawModelAnimationTypes -{ - public static readonly ModelAnimationType Idle = new(GameEngineType.Eaw, 0x0); - public static readonly ModelAnimationType SpaceIdle = new(GameEngineType.Eaw, 0x1); - public static readonly ModelAnimationType Move = new(GameEngineType.Eaw, 0x2); - public static readonly ModelAnimationType TurnLeft = new(GameEngineType.Eaw, 0x3); - public static readonly ModelAnimationType TurnRight = new(GameEngineType.Eaw, 0x4); - public static readonly ModelAnimationType Attack = new(GameEngineType.Eaw, 0x5); - public static readonly ModelAnimationType AttackIdle = new(GameEngineType.Eaw, 0x6); - public static readonly ModelAnimationType Die = new(GameEngineType.Eaw, 0x7); - public static readonly ModelAnimationType Rotate = new(GameEngineType.Eaw, 0x8); - public static readonly ModelAnimationType SpecialA = new(GameEngineType.Eaw, 0x9); - public static readonly ModelAnimationType SpecialB = new(GameEngineType.Eaw, 0xa); - public static readonly ModelAnimationType SpecialC = new(GameEngineType.Eaw, 0xb); - public static readonly ModelAnimationType TransitionToLeftTurn = new(GameEngineType.Eaw, 0xc); - public static readonly ModelAnimationType TransitionFromLeftTurn = new(GameEngineType.Eaw, 0xd); - public static readonly ModelAnimationType TransitionToRightTurn = new(GameEngineType.Eaw, 0xe); - public static readonly ModelAnimationType TransitionFromRightTurn = new(GameEngineType.Eaw, 0xf); - public static readonly ModelAnimationType TransitionToMove = new(GameEngineType.Eaw, 0x10); - public static readonly ModelAnimationType TransitionFromMoveFrame0 = new(GameEngineType.Eaw, 0x11); - public static readonly ModelAnimationType TransitionFromMoveFrame40 = new(GameEngineType.Eaw, 0x12); - public static readonly ModelAnimationType TransitionFromMoveFrame80 = new(GameEngineType.Eaw, 0x13); - public static readonly ModelAnimationType TransitionFromMoveFrame120 = new(GameEngineType.Eaw, 0x14); - public static readonly ModelAnimationType TurnLeftHalf = new(GameEngineType.Eaw, 0x15); - public static readonly ModelAnimationType TurnLeftQuarter = new(GameEngineType.Eaw, 0x16); - public static readonly ModelAnimationType TurnRightHalf = new(GameEngineType.Eaw, 0x17); - public static readonly ModelAnimationType TurnRightQuarter = new(GameEngineType.Eaw, 0x18); - public static readonly ModelAnimationType Deploy = new(GameEngineType.Eaw, 0x19); - public static readonly ModelAnimationType Undeploy = new(GameEngineType.Eaw, 0x1a); - public static readonly ModelAnimationType Cinematic = new(GameEngineType.Eaw, 0x1b); - public static readonly ModelAnimationType BlockBlaster = new(GameEngineType.Eaw, 0x1c); - public static readonly ModelAnimationType RedirectBlaster = new(GameEngineType.Eaw, 0x1d); - public static readonly ModelAnimationType IdleBlockBlaster = new(GameEngineType.Eaw, 0x1e); - public static readonly ModelAnimationType ForceWhirlwindAttack = new(GameEngineType.Eaw, 0x1f); - public static readonly ModelAnimationType ForceWhirlwindDie = new(GameEngineType.Eaw, 0x20); - public static readonly ModelAnimationType ForceTelekinesisAttack = new(GameEngineType.Eaw, 0x21); - public static readonly ModelAnimationType ForceTelekinesisHold = new(GameEngineType.Eaw, 0x22); - public static readonly ModelAnimationType ForceTelekinesisRelease = new(GameEngineType.Eaw, 0x23); - public static readonly ModelAnimationType ForceTelekinesisDie = new(GameEngineType.Eaw, 0x24); - public static readonly ModelAnimationType EarthquakeAttack = new(GameEngineType.Eaw, 0x25); - public static readonly ModelAnimationType EarthquakeHold = new(GameEngineType.Eaw, 0x26); - public static readonly ModelAnimationType EarthquakeRelease = new(GameEngineType.Eaw, 0x27); - public static readonly ModelAnimationType ForceLightningAttack = new(GameEngineType.Eaw, 0x28); - public static readonly ModelAnimationType ForceLightningDie = new(GameEngineType.Eaw, 0x29); - public static readonly ModelAnimationType ForceRun = new(GameEngineType.Eaw, 0x2a); - public static readonly ModelAnimationType TransportLanding = new(GameEngineType.Eaw, 0x2b); - public static readonly ModelAnimationType TransportLeaving = new(GameEngineType.Eaw, 0x2c); - public static readonly ModelAnimationType FlameAttack = new(GameEngineType.Eaw, 0x2d); - public static readonly ModelAnimationType Demolition = new(GameEngineType.Eaw, 0x2e); - public static readonly ModelAnimationType BombToss = new(GameEngineType.Eaw, 0x2f); - public static readonly ModelAnimationType Jump = new(GameEngineType.Eaw, 0x30); - public static readonly ModelAnimationType FlyIdle = new(GameEngineType.Eaw, 0x31); - public static readonly ModelAnimationType FlyLand = new(GameEngineType.Eaw, 0x32); - public static readonly ModelAnimationType LandIdle = new(GameEngineType.Eaw, 0x33); - public static readonly ModelAnimationType Land = new(GameEngineType.Eaw, 0x34); - public static readonly ModelAnimationType HcWin = new(GameEngineType.Eaw, 0x35); - public static readonly ModelAnimationType HcLose = new(GameEngineType.Eaw, 0x36); - public static readonly ModelAnimationType HcDraw = new(GameEngineType.Eaw, 0x37); - public static readonly ModelAnimationType ShieldOn = new(GameEngineType.Eaw, 0x38); - public static readonly ModelAnimationType ShieldOff = new(GameEngineType.Eaw, 0x39); - public static readonly ModelAnimationType CableAttackDie = new(GameEngineType.Eaw, 0x3a); - public static readonly ModelAnimationType DeployedCableAttackDie = new(GameEngineType.Eaw, 0x3b); - public static readonly ModelAnimationType DeployedDie = new(GameEngineType.Eaw, 0x3c); - public static readonly ModelAnimationType RunAroundOnFire = new(GameEngineType.Eaw, 0x3d); - public static readonly ModelAnimationType FireDie = new(GameEngineType.Eaw, 0x3e); - public static readonly ModelAnimationType PoundAttack = new(GameEngineType.Eaw, 0x3f); - public static readonly ModelAnimationType EatAttack = new(GameEngineType.Eaw, 0x40); - public static readonly ModelAnimationType EatDie = new(GameEngineType.Eaw, 0x41); - public static readonly ModelAnimationType MoveWalk = new(GameEngineType.Eaw, 0x42); - public static readonly ModelAnimationType MoveCrouch = new(GameEngineType.Eaw, 0x43); - public static readonly ModelAnimationType StructureOpen = new(GameEngineType.Eaw, 0x44); - public static readonly ModelAnimationType StructureHold = new(GameEngineType.Eaw, 0x45); - public static readonly ModelAnimationType StructureClose = new(GameEngineType.Eaw, 0x46); - public static readonly ModelAnimationType IdleCrouch = new(GameEngineType.Eaw, 0x47); - public static readonly ModelAnimationType TurnLeftCrouch = new(GameEngineType.Eaw, 0x48); - public static readonly ModelAnimationType TurnRightCrouch = new(GameEngineType.Eaw, 0x49); - public static readonly ModelAnimationType Build = new(GameEngineType.Eaw, 0x4a); - public static readonly ModelAnimationType TransitionOwnership = new(GameEngineType.Eaw, 0x4b); - public static readonly ModelAnimationType SelfDestruct = new(GameEngineType.Eaw, 0x4c); - public static readonly ModelAnimationType Attention = new(GameEngineType.Eaw, 0x4d); - public static readonly ModelAnimationType Celebrate = new(GameEngineType.Eaw, 0x4e); - public static readonly ModelAnimationType FlinchLeft = new(GameEngineType.Eaw, 0x4f); - public static readonly ModelAnimationType FlinchRight = new(GameEngineType.Eaw, 0x50); - public static readonly ModelAnimationType FlinchFront = new(GameEngineType.Eaw, 0x51); - public static readonly ModelAnimationType FlinchBack = new(GameEngineType.Eaw, 0x52); - public static readonly ModelAnimationType AttackFlinchLeft = new(GameEngineType.Eaw, 0x53); - public static readonly ModelAnimationType AttackFlinchRight = new(GameEngineType.Eaw, 0x54); - public static readonly ModelAnimationType AttackFlinchFront = new(GameEngineType.Eaw, 0x55); - public static readonly ModelAnimationType AttackFlinchBack = new(GameEngineType.Eaw, 0x56); - public static readonly ModelAnimationType Talk = new(GameEngineType.Eaw, 0x57); - public static readonly ModelAnimationType TalkGesture = new(GameEngineType.Eaw, 0x58); - public static readonly ModelAnimationType TalkQuestion = new(GameEngineType.Eaw, 0x59); - public static readonly ModelAnimationType Hacking = new(GameEngineType.Eaw, 0x5a); - public static readonly ModelAnimationType Repairing = new(GameEngineType.Eaw, 0x5b); - public static readonly ModelAnimationType Choke = new(GameEngineType.Eaw, 0x5c); - public static readonly ModelAnimationType ChokeDie = new(GameEngineType.Eaw, 0x5d); - public static readonly ModelAnimationType DropTroopers = new(GameEngineType.Eaw, 0x5e); - public static readonly ModelAnimationType RopeSlide = new(GameEngineType.Eaw, 0x5f); - public static readonly ModelAnimationType RopeLand = new(GameEngineType.Eaw, 0x60); - public static readonly ModelAnimationType RopeDrop = new(GameEngineType.Eaw, 0x61); - public static readonly ModelAnimationType RopeLift = new(GameEngineType.Eaw, 0x62); - public static readonly ModelAnimationType Alarm = new(GameEngineType.Eaw, 0x63); - public static readonly ModelAnimationType Warning = new(GameEngineType.Eaw, 0x64); - public static readonly ModelAnimationType Crushed = new(GameEngineType.Eaw, 0x65); - public static readonly ModelAnimationType PowerDown = new(GameEngineType.Eaw, 0x66); - public static readonly ModelAnimationType PowerUp = new(GameEngineType.Eaw, 0x67); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/FocModelAnimationTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/FocModelAnimationTypes.cs deleted file mode 100644 index 743491a..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/FocModelAnimationTypes.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace PG.StarWarsGame.Engine.Rendering.Animations; - -public static class FocModelAnimationTypes -{ - public static readonly ModelAnimationType Idle = new(GameEngineType.Foc, 0x0); - public static readonly ModelAnimationType SpaceIdle = new(GameEngineType.Foc, 0x1); - public static readonly ModelAnimationType Move = new(GameEngineType.Foc, 0x2); - public static readonly ModelAnimationType TurnLeft = new(GameEngineType.Foc, 0x3); - public static readonly ModelAnimationType TurnRight = new(GameEngineType.Foc, 0x4); - public static readonly ModelAnimationType Attack = new(GameEngineType.Foc, 0x5); - public static readonly ModelAnimationType AttackIdle = new(GameEngineType.Foc, 0x6); - public static readonly ModelAnimationType Die = new(GameEngineType.Foc, 0x7); - public static readonly ModelAnimationType Rotate = new(GameEngineType.Foc, 0x8); - public static readonly ModelAnimationType SpecialA = new(GameEngineType.Foc, 0x9); - public static readonly ModelAnimationType SpecialB = new(GameEngineType.Foc, 0xa); - public static readonly ModelAnimationType SpecialC = new(GameEngineType.Foc, 0xb); - public static readonly ModelAnimationType TransitionToLeftTurn = new(GameEngineType.Foc, 0xc); - public static readonly ModelAnimationType TransitionFromLeftTurn = new(GameEngineType.Foc, 0xd); - public static readonly ModelAnimationType TransitionToRightTurn = new(GameEngineType.Foc, 0xe); - public static readonly ModelAnimationType TransitionFromRightTurn = new(GameEngineType.Foc, 0xf); - public static readonly ModelAnimationType TransitionToMove = new(GameEngineType.Foc, 0x10); - public static readonly ModelAnimationType TransitionFromMoveFrame0 = new(GameEngineType.Foc, 0x11); - public static readonly ModelAnimationType TransitionFromMoveFrame40 = new(GameEngineType.Foc, 0x12); - public static readonly ModelAnimationType TransitionFromMoveFrame80 = new(GameEngineType.Foc, 0x13); - public static readonly ModelAnimationType TransitionFromMoveFrame120 = new(GameEngineType.Foc, 0x14); - public static readonly ModelAnimationType TurnLeftHalf = new(GameEngineType.Foc, 0x15); - public static readonly ModelAnimationType TurnLeftQuarter = new(GameEngineType.Foc, 0x16); - public static readonly ModelAnimationType TurnRightHalf = new(GameEngineType.Foc, 0x17); - public static readonly ModelAnimationType TurnRightQuarter = new(GameEngineType.Foc, 0x18); - public static readonly ModelAnimationType Deploy = new(GameEngineType.Foc, 0x19); - public static readonly ModelAnimationType Undeploy = new(GameEngineType.Foc, 0x1a); - public static readonly ModelAnimationType Cinematic = new(GameEngineType.Foc, 0x1b); - public static readonly ModelAnimationType BlockBlaster = new(GameEngineType.Foc, 0x1c); - public static readonly ModelAnimationType RedirectBlaster = new(GameEngineType.Foc, 0x1d); - public static readonly ModelAnimationType IdleBlockBlaster = new(GameEngineType.Foc, 0x1e); - public static readonly ModelAnimationType ForceWhirlwindAttack = new(GameEngineType.Foc, 0x1f); - public static readonly ModelAnimationType ForceWhirlwindDie = new(GameEngineType.Foc, 0x20); - public static readonly ModelAnimationType ForceTelekinesisAttack = new(GameEngineType.Foc, 0x21); - public static readonly ModelAnimationType ForceTelekinesisHold = new(GameEngineType.Foc, 0x22); - public static readonly ModelAnimationType ForceTelekinesisRelease = new(GameEngineType.Foc, 0x23); - public static readonly ModelAnimationType ForceTelekinesisDie = new(GameEngineType.Foc, 0x24); - public static readonly ModelAnimationType EarthquakeAttack = new(GameEngineType.Foc, 0x25); - public static readonly ModelAnimationType EarthquakeHold = new(GameEngineType.Foc, 0x26); - public static readonly ModelAnimationType EarthquakeRelease = new(GameEngineType.Foc, 0x27); - public static readonly ModelAnimationType ForceLightningAttack = new(GameEngineType.Foc, 0x28); - public static readonly ModelAnimationType ForceLightningDie = new(GameEngineType.Foc, 0x29); - public static readonly ModelAnimationType ForceRun = new(GameEngineType.Foc, 0x2a); - public static readonly ModelAnimationType TransportLanding = new(GameEngineType.Foc, 0x2b); - public static readonly ModelAnimationType TransportLeaving = new(GameEngineType.Foc, 0x2c); - public static readonly ModelAnimationType FlameAttack = new(GameEngineType.Foc, 0x2d); - public static readonly ModelAnimationType Demolition = new(GameEngineType.Foc, 0x2e); - public static readonly ModelAnimationType BombToss = new(GameEngineType.Foc, 0x2f); - public static readonly ModelAnimationType Jump = new(GameEngineType.Foc, 0x30); - public static readonly ModelAnimationType FlyIdle = new(GameEngineType.Foc, 0x31); - public static readonly ModelAnimationType FlyLand = new(GameEngineType.Foc, 0x32); - public static readonly ModelAnimationType LandIdle = new(GameEngineType.Foc, 0x33); - public static readonly ModelAnimationType Land = new(GameEngineType.Foc, 0x34); - public static readonly ModelAnimationType HcWin = new(GameEngineType.Foc, 0x35); - public static readonly ModelAnimationType HcLose = new(GameEngineType.Foc, 0x36); - public static readonly ModelAnimationType HcDraw = new(GameEngineType.Foc, 0x37); - public static readonly ModelAnimationType ShieldOn = new(GameEngineType.Foc, 0x38); - public static readonly ModelAnimationType ShieldOff = new(GameEngineType.Foc, 0x39); - public static readonly ModelAnimationType CableAttackDie = new(GameEngineType.Foc, 0x3a); - public static readonly ModelAnimationType DeployedCableAttackDie = new(GameEngineType.Foc, 0x3b); - public static readonly ModelAnimationType DeployedDie = new(GameEngineType.Foc, 0x3c); - public static readonly ModelAnimationType RunAroundOnFire = new(GameEngineType.Foc, 0x3d); - public static readonly ModelAnimationType FireDie = new(GameEngineType.Foc, 0x3e); - public static readonly ModelAnimationType PoundAttack = new(GameEngineType.Foc, 0x3f); - public static readonly ModelAnimationType EatAttack = new(GameEngineType.Foc, 0x40); - public static readonly ModelAnimationType EatDie = new(GameEngineType.Foc, 0x41); - public static readonly ModelAnimationType MoveWalk = new(GameEngineType.Foc, 0x42); - public static readonly ModelAnimationType MoveCrouch = new(GameEngineType.Foc, 0x43); - public static readonly ModelAnimationType StructureOpen = new(GameEngineType.Foc, 0x44); - public static readonly ModelAnimationType StructureHold = new(GameEngineType.Foc, 0x45); - public static readonly ModelAnimationType StructureClose = new(GameEngineType.Foc, 0x46); - public static readonly ModelAnimationType IdleCrouch = new(GameEngineType.Foc, 0x47); - public static readonly ModelAnimationType TurnLeftCrouch = new(GameEngineType.Foc, 0x48); - public static readonly ModelAnimationType TurnRightCrouch = new(GameEngineType.Foc, 0x49); - public static readonly ModelAnimationType Build = new(GameEngineType.Foc, 0x4a); - public static readonly ModelAnimationType TransitionOwnership = new(GameEngineType.Foc, 0x4b); - public static readonly ModelAnimationType SelfDestruct = new(GameEngineType.Foc, 0x4c); - public static readonly ModelAnimationType Attention = new(GameEngineType.Foc, 0x4d); - public static readonly ModelAnimationType Celebrate = new(GameEngineType.Foc, 0x4e); - public static readonly ModelAnimationType FlinchLeft = new(GameEngineType.Foc, 0x4f); - public static readonly ModelAnimationType FlinchRight = new(GameEngineType.Foc, 0x50); - public static readonly ModelAnimationType FlinchFront = new(GameEngineType.Foc, 0x51); - public static readonly ModelAnimationType FlinchBack = new(GameEngineType.Foc, 0x52); - public static readonly ModelAnimationType AttackFlinchLeft = new(GameEngineType.Foc, 0x53); - public static readonly ModelAnimationType AttackFlinchRight = new(GameEngineType.Foc, 0x54); - public static readonly ModelAnimationType AttackFlinchFront = new(GameEngineType.Foc, 0x55); - public static readonly ModelAnimationType AttackFlinchBack = new(GameEngineType.Foc, 0x56); - public static readonly ModelAnimationType Talk = new(GameEngineType.Foc, 0x57); - public static readonly ModelAnimationType TalkGesture = new(GameEngineType.Foc, 0x58); - public static readonly ModelAnimationType TalkQuestion = new(GameEngineType.Foc, 0x59); - public static readonly ModelAnimationType Hacking = new(GameEngineType.Foc, 0x5a); - public static readonly ModelAnimationType Repairing = new(GameEngineType.Foc, 0x5b); - public static readonly ModelAnimationType Choke = new(GameEngineType.Foc, 0x5c); - public static readonly ModelAnimationType ChokeDie = new(GameEngineType.Foc, 0x5d); - public static readonly ModelAnimationType DropTroopers = new(GameEngineType.Foc, 0x5e); - public static readonly ModelAnimationType RopeSlide = new(GameEngineType.Foc, 0x5f); - public static readonly ModelAnimationType RopeLand = new(GameEngineType.Foc, 0x60); - public static readonly ModelAnimationType RopeDrop = new(GameEngineType.Foc, 0x61); - public static readonly ModelAnimationType RopeLift = new(GameEngineType.Foc, 0x62); - public static readonly ModelAnimationType Alarm = new(GameEngineType.Foc, 0x63); - public static readonly ModelAnimationType Warning = new(GameEngineType.Foc, 0x64); - public static readonly ModelAnimationType Crushed = new(GameEngineType.Foc, 0x65); - public static readonly ModelAnimationType PowerDown = new(GameEngineType.Foc, 0x66); - public static readonly ModelAnimationType PowerUp = new(GameEngineType.Foc, 0x67); - public static readonly ModelAnimationType SpinMove = new(GameEngineType.Foc, 0x68); - public static readonly ModelAnimationType ForceRevealBegin = new(GameEngineType.Foc, 0x69); - public static readonly ModelAnimationType ForceRevealLoop = new(GameEngineType.Foc, 0x6a); - public static readonly ModelAnimationType ForceRevealEnd = new(GameEngineType.Foc, 0x6b); - public static readonly ModelAnimationType SaberThrow = new(GameEngineType.Foc, 0x6c); - public static readonly ModelAnimationType SaberControl = new(GameEngineType.Foc, 0x6d); - public static readonly ModelAnimationType SaberCatch = new(GameEngineType.Foc, 0x6e); - public static readonly ModelAnimationType SaberSpin = new(GameEngineType.Foc, 0x6f); - public static readonly ModelAnimationType ContaminateAttack = new(GameEngineType.Foc, 0x70); - public static readonly ModelAnimationType ContaminateLoop = new(GameEngineType.Foc, 0x71); - public static readonly ModelAnimationType ContaminateRelease = new(GameEngineType.Foc, 0x72); - public static readonly ModelAnimationType DeployedWalk = new(GameEngineType.Foc, 0x73); - public static readonly ModelAnimationType PadBuild = new(GameEngineType.Foc, 0x74); - public static readonly ModelAnimationType PadSell = new(GameEngineType.Foc, 0x75); - public static readonly ModelAnimationType Heal = new(GameEngineType.Foc, 0x76); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/ModelAnimationType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/ModelAnimationType.cs index 867ba08..31ecb22 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/ModelAnimationType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/ModelAnimationType.cs @@ -1,47 +1,127 @@ -using System; +namespace PG.StarWarsGame.Engine.Rendering.Animations; -namespace PG.StarWarsGame.Engine.Rendering.Animations; - -public readonly struct ModelAnimationType : IEquatable +public enum ModelAnimationType { - public GameEngineType TargetEngine { get; } - - public int Value { get; } - - internal ModelAnimationType(GameEngineType engine, int value) - { - TargetEngine = engine; - Value = value; - } - - public override string ToString() - { - var nameLookup = SupportedModelAnimationTypes.GetAnimationTypesForEngine(TargetEngine); - return $"'{nameLookup[this]}' ({Value})"; - } - - public bool Equals(ModelAnimationType other) - { - return TargetEngine == other.TargetEngine && Value == other.Value; - } - - public override bool Equals(object? obj) - { - return obj is ModelAnimationType other && Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine((int)TargetEngine, Value); - } - - public static bool operator ==(ModelAnimationType left, ModelAnimationType right) - { - return left.Equals(right); - } - - public static bool operator !=(ModelAnimationType left, ModelAnimationType right) - { - return !(left == right); - } -} \ No newline at end of file + Invalid = -1, + None = -1, + Idle = 0x0, + SpaceIdle = 0x1, + Move = 0x2, + TurnLeft = 0x3, + TurnRight = 0x4, + Attack = 0x5, + AttackIdle = 0x6, + Die = 0x7, + Rotate = 0x8, + SpecialA = 0x9, + SpecialB = 0xa, + SpecialC = 0xb, + TransitionToLeftTurn = 0xc, + TransitionFromLeftTurn = 0xd, + TransitionToRightTurn = 0xe, + TransitionFromRightTurn = 0xf, + TransitionToMove = 0x10, + TransitionFromMoveFrame0 = 0x11, + TransitionFromMoveFrame40 = 0x12, + TransitionFromMoveFrame80 = 0x13, + TransitionFromMoveFrame120 = 0x14, + TurnLeftHalf = 0x15, + TurnLeftQuarter = 0x16, + TurnRightHalf = 0x17, + TurnRightQuarter = 0x18, + Deploy = 0x19, + Undeploy = 0x1a, + Cinematic = 0x1b, + BlockBlaster = 0x1c, + RedirectBlaster = 0x1d, + IdleBlockBlaster = 0x1e, + ForceWhirlwindAttack = 0x1f, + ForceWhirlwindDie = 0x20, + ForceTelekinesisAttack = 0x21, + ForceTelekinesisHold = 0x22, + ForceTelekinesisRelease = 0x23, + ForceTelekinesisDie = 0x24, + EarthquakeAttack = 0x25, + EarthquakeHold = 0x26, + EarthquakeRelease = 0x27, + ForceLightningAttack = 0x28, + ForceLightningDie = 0x29, + ForceRun = 0x2a, + TransportLanding = 0x2b, + TransportLeaving = 0x2c, + FlameAttack = 0x2d, + Demolition = 0x2e, + BombToss = 0x2f, + Jump = 0x30, + FlyIdle = 0x31, + FlyLand = 0x32, + LandIdle = 0x33, + Land = 0x34, + HcWin = 0x35, + HcLose = 0x36, + HcDraw = 0x37, + ShieldOn = 0x38, + ShieldOff = 0x39, + CableAttackDie = 0x3a, + DeployedCableAttackDie = 0x3b, + DeployedDie = 0x3c, + RunAroundOnFire = 0x3d, + FireDie = 0x3e, + PoundAttack = 0x3f, + EatAttack = 0x40, + EatDie = 0x41, + MoveWalk = 0x42, + MoveCrouch = 0x43, + StructureOpen = 0x44, + StructureHold = 0x45, + StructureClose = 0x46, + IdleCrouch = 0x47, + TurnLeftCrouch = 0x48, + TurnRightCrouch = 0x49, + Build = 0x4a, + TransitionOwnership = 0x4b, + SelfDestruct = 0x4c, + Attention = 0x4d, + Celebrate = 0x4e, + FlinchLeft = 0x4f, + FlinchRight = 0x50, + FlinchFront = 0x51, + FlinchBack = 0x52, + AttackFlinchLeft = 0x53, + AttackFlinchRight = 0x54, + AttackFlinchFront = 0x55, + AttackFlinchBack = 0x56, + Talk = 0x57, + TalkGesture = 0x58, + TalkQuestion = 0x59, + Hacking = 0x5a, + Repairing = 0x5b, + Choke = 0x5c, + ChokeDie = 0x5d, + DropTroopers = 0x5e, + RopeSlide = 0x5f, + RopeLand = 0x60, + RopeDrop = 0x61, + RopeLift = 0x62, + Alarm = 0x63, + Warning = 0x64, + Crushed = 0x65, + PowerDown = 0x66, + PowerUp = 0x67, + // These are exclusive to FoC + SpinMove = 0x68, + ForceRevealBegin = 0x69, + ForceRevealLoop = 0x6a, + ForceRevealEnd = 0x6b, + SaberThrow = 0x6c, + SaberControl = 0x6d, + SaberCatch = 0x6e, + SaberSpin = 0x6f, + ContaminateAttack = 0x70, + ContaminateLoop = 0x71, + ContaminateRelease = 0x72, + DeployedWalk = 0x73, + PadBuild = 0x74, + PadSell = 0x75, + Heal = 0x76, +}; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs index 2a7aa3f..302c2fb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs @@ -1,11 +1,16 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace PG.StarWarsGame.Engine.Rendering.Animations; public static class SupportedModelAnimationTypes { + // NB: The games uses two different string mappings for animations. + // The strings here are used to identify the animation file name. + // These strings have an underscore '_' as delimiter, + // while the animation names for the LUA functions (Play_Animation("ANIM NAME")) use a space ' ' as delimiter. + public static IReadOnlyDictionary GetAnimationTypesForEngine(GameEngineType engineType) { return engineType switch @@ -15,237 +20,135 @@ public static IReadOnlyDictionary GetAnimationTypesF _ => throw new NotSupportedException() }; } - - [SuppressMessage("ReSharper", "StringLiteralTypo")] - private static readonly Dictionary FocSupportedAnimations = new() - { - { FocModelAnimationTypes.Idle, "IDLE"}, - { FocModelAnimationTypes.SpaceIdle, "SPACE_IDLE"}, - { FocModelAnimationTypes.Move, "MOVE"}, - { FocModelAnimationTypes.TurnLeft, "TURNL"}, - { FocModelAnimationTypes.TurnRight, "TURNR"}, - { FocModelAnimationTypes.Attack, "ATTACK"}, - { FocModelAnimationTypes.AttackIdle, "ATTACKIDLE"}, - { FocModelAnimationTypes.Die, "DIE"}, - { FocModelAnimationTypes.Rotate, "ROTATE"}, - { FocModelAnimationTypes.SpecialA, "SPECIAL_A"}, - { FocModelAnimationTypes.SpecialB, "SPECIAL_B"}, - { FocModelAnimationTypes.SpecialC, "SPECIAL_C"}, - { FocModelAnimationTypes.TransitionToLeftTurn, "TURNL_BEGIN"}, - { FocModelAnimationTypes.TransitionFromLeftTurn, "TURNL_END"}, - { FocModelAnimationTypes.TransitionToRightTurn, "TURNR_BEGIN"}, - { FocModelAnimationTypes.TransitionFromRightTurn, "TURNR_END"}, - { FocModelAnimationTypes.TransitionToMove, "MOVESTART"}, - { FocModelAnimationTypes.TransitionFromMoveFrame0, "MOVE_ENDONE"}, - { FocModelAnimationTypes.TransitionFromMoveFrame40, "MOVE_ENDTWO"}, - { FocModelAnimationTypes.TransitionFromMoveFrame80, "MOVE_ENDTHREE"}, - { FocModelAnimationTypes.TransitionFromMoveFrame120, "MOVE_ENDFOUR"}, - { FocModelAnimationTypes.TurnLeftHalf, "TURNL_HALF"}, - { FocModelAnimationTypes.TurnLeftQuarter, "TURNL_QUARTER"}, - { FocModelAnimationTypes.TurnRightHalf, "TURNR_HALF"}, - { FocModelAnimationTypes.TurnRightQuarter, "TURNR_QUARTER"}, - { FocModelAnimationTypes.Deploy, "DEPLOY"}, - { FocModelAnimationTypes.Undeploy, "UNDEPLOY"}, - { FocModelAnimationTypes.Cinematic, "CINEMATIC"}, - { FocModelAnimationTypes.BlockBlaster, "BLOCK_BLASTER"}, - { FocModelAnimationTypes.RedirectBlaster, "REDIRECT_BLASTER"}, - { FocModelAnimationTypes.IdleBlockBlaster, "IDLE_BLOCKBLASTER"}, - { FocModelAnimationTypes.ForceWhirlwindAttack, "FW_ATTACK"}, - { FocModelAnimationTypes.ForceWhirlwindDie, "FW_DIE"}, - { FocModelAnimationTypes.ForceTelekinesisAttack, "FTK_ATTACK"}, - { FocModelAnimationTypes.ForceTelekinesisHold, "FTK_HOLD"}, - { FocModelAnimationTypes.ForceTelekinesisRelease, "FTK_RELEASE"}, - { FocModelAnimationTypes.ForceTelekinesisDie, "FTK_DIE"}, - { FocModelAnimationTypes.EarthquakeAttack, "FB_ATTACK"}, - { FocModelAnimationTypes.EarthquakeHold, "FB_HOLD"}, - { FocModelAnimationTypes.EarthquakeRelease, "FB_RELEASE"}, - { FocModelAnimationTypes.ForceLightningAttack, "FL_ATTACK"}, - { FocModelAnimationTypes.ForceLightningDie, "FL_DIE"}, - { FocModelAnimationTypes.ForceRun, "FORCE_RUN"}, - { FocModelAnimationTypes.TransportLanding, "LAND"}, - { FocModelAnimationTypes.TransportLeaving, "TAKEOFF"}, - { FocModelAnimationTypes.FlameAttack, "FLAME_ATTACK"}, - { FocModelAnimationTypes.Demolition, "DEMOLITION"}, - { FocModelAnimationTypes.BombToss, "BOMBTOSS"}, - { FocModelAnimationTypes.Jump, "JUMP"}, - { FocModelAnimationTypes.FlyIdle, "FLYIDLE"}, - { FocModelAnimationTypes.FlyLand, "FLYLAND"}, - { FocModelAnimationTypes.LandIdle, "FLYLANDIDLE"}, - { FocModelAnimationTypes.Land, "FLYLANDDROP"}, - { FocModelAnimationTypes.HcWin, "HC_WIN"}, - { FocModelAnimationTypes.HcLose, "HC_LOSE"}, - { FocModelAnimationTypes.HcDraw, "HC_DRAW"}, - { FocModelAnimationTypes.ShieldOn, "SHIELD_ON"}, - { FocModelAnimationTypes.ShieldOff, "SHIELD_OFF"}, - { FocModelAnimationTypes.CableAttackDie, "CA_DIE"}, - { FocModelAnimationTypes.DeployedCableAttackDie, "DEPLOYED_CA_DIE"}, - { FocModelAnimationTypes.DeployedDie, "DEPLOYED_DIE"}, - { FocModelAnimationTypes.RunAroundOnFire, "FIRE_MOVE"}, - { FocModelAnimationTypes.FireDie, "FIRE_DIE"}, - { FocModelAnimationTypes.PoundAttack, "POUND_ATTACK"}, - { FocModelAnimationTypes.EatAttack, "EAT_ATTACK"}, - { FocModelAnimationTypes.EatDie, "EATEN_DIE"}, - { FocModelAnimationTypes.MoveWalk, "WALKMOVE"}, - { FocModelAnimationTypes.MoveCrouch, "CROUCHMOVE"}, - { FocModelAnimationTypes.StructureOpen, "OPEN"}, - { FocModelAnimationTypes.StructureHold, "HOLD"}, - { FocModelAnimationTypes.StructureClose, "CLOSE"}, - { FocModelAnimationTypes.IdleCrouch, "CROUCHIDLE"}, - { FocModelAnimationTypes.TurnLeftCrouch, "CROUCHTURNL"}, - { FocModelAnimationTypes.TurnRightCrouch, "CROUCHTURNR"}, - { FocModelAnimationTypes.Build, "BUILD"}, - { FocModelAnimationTypes.TransitionOwnership, "TRANS"}, - { FocModelAnimationTypes.SelfDestruct, "SELF_DESTRUCT"}, - { FocModelAnimationTypes.Attention, "ATTENTION"}, - { FocModelAnimationTypes.Celebrate, "CELEBRATE"}, - { FocModelAnimationTypes.FlinchLeft, "FLINCHL"}, - { FocModelAnimationTypes.FlinchRight, "FLINCHR"}, - { FocModelAnimationTypes.FlinchFront, "FLINCHF"}, - { FocModelAnimationTypes.FlinchBack, "FLINCHB"}, - { FocModelAnimationTypes.AttackFlinchLeft, "ATTACKFLINCHL"}, - { FocModelAnimationTypes.AttackFlinchRight, "ATTACKFLINCHR"}, - { FocModelAnimationTypes.AttackFlinchFront, "ATTACKFLINCHF"}, - { FocModelAnimationTypes.AttackFlinchBack, "ATTACKFLINCHB"}, - { FocModelAnimationTypes.Talk, "TALK"}, - { FocModelAnimationTypes.TalkGesture, "TALKGESTURE"}, - { FocModelAnimationTypes.TalkQuestion, "TALKQUESTION"}, - { FocModelAnimationTypes.Hacking, "HACKING"}, - { FocModelAnimationTypes.Repairing, "REPAIRING"}, - { FocModelAnimationTypes.Choke, "CHOKE"}, - { FocModelAnimationTypes.ChokeDie, "CHOKEDEATH"}, - { FocModelAnimationTypes.DropTroopers, "TROOPDROP"}, - { FocModelAnimationTypes.RopeSlide, "ROPESLIDE"}, - { FocModelAnimationTypes.RopeLand, "ROPELAND"}, - { FocModelAnimationTypes.RopeDrop, "ROPE_DROP"}, - { FocModelAnimationTypes.RopeLift, "ROPE_LIFT"}, - { FocModelAnimationTypes.Alarm, "ALARM"}, - { FocModelAnimationTypes.Warning, "WARNING"}, - { FocModelAnimationTypes.Crushed, "CRUSHED"}, - { FocModelAnimationTypes.PowerDown, "POWERDOWN"}, - { FocModelAnimationTypes.PowerUp, "POWERUP"}, - { FocModelAnimationTypes.SpinMove, "SPINMOVE"}, - { FocModelAnimationTypes.ForceRevealBegin, "FORCE_REVEAL_BEGIN"}, - { FocModelAnimationTypes.ForceRevealLoop, "FORCE_REVEAL_LOOP"}, - { FocModelAnimationTypes.ForceRevealEnd, "FORCE_REVEAL_END"}, - { FocModelAnimationTypes.SaberThrow, "SWORD_THROW"}, - { FocModelAnimationTypes.SaberControl, "SWORD_CONTROL"}, - { FocModelAnimationTypes.SaberCatch, "SWORD_CATCH"}, - { FocModelAnimationTypes.SaberSpin, "SWORDSPIN"}, - { FocModelAnimationTypes.ContaminateAttack, "CONTAMINATE_ATTACK"}, - { FocModelAnimationTypes.ContaminateLoop, "CONTAMINATE_LOOP"}, - { FocModelAnimationTypes.ContaminateRelease, "CONTAMINATE_RELEASE"}, - { FocModelAnimationTypes.DeployedWalk, "WALK"}, - { FocModelAnimationTypes.PadBuild, "PAD_BUILD"}, - { FocModelAnimationTypes.PadSell, "PAD_SELL"}, - { FocModelAnimationTypes.Heal, "HEAL"}, - }; - - [SuppressMessage("ReSharper", "StringLiteralTypo")] + private static readonly Dictionary EawSupportedAnimations = new() { - { EawModelAnimationTypes.Idle, "IDLE"}, - { EawModelAnimationTypes.SpaceIdle, "SPACE_IDLE"}, - { EawModelAnimationTypes.Move, "MOVE"}, - { EawModelAnimationTypes.TurnLeft, "TURNL"}, - { EawModelAnimationTypes.TurnRight, "TURNR"}, - { EawModelAnimationTypes.Attack, "ATTACK"}, - { EawModelAnimationTypes.AttackIdle, "ATTACKIDLE"}, - { EawModelAnimationTypes.Die, "DIE"}, - { EawModelAnimationTypes.Rotate, "ROTATE"}, - { EawModelAnimationTypes.SpecialA, "SPECIAL_A"}, - { EawModelAnimationTypes.SpecialB, "SPECIAL_B"}, - { EawModelAnimationTypes.SpecialC, "SPECIAL_C"}, - { EawModelAnimationTypes.TransitionToLeftTurn, "TURNL_BEGIN"}, - { EawModelAnimationTypes.TransitionFromLeftTurn, "TURNL_END"}, - { EawModelAnimationTypes.TransitionToRightTurn, "TURNR_BEGIN"}, - { EawModelAnimationTypes.TransitionFromRightTurn, "TURNR_END"}, - { EawModelAnimationTypes.TransitionToMove, "MOVESTART"}, - { EawModelAnimationTypes.TransitionFromMoveFrame0, "MOVE_ENDONE"}, - { EawModelAnimationTypes.TransitionFromMoveFrame40, "MOVE_ENDTWO"}, - { EawModelAnimationTypes.TransitionFromMoveFrame80, "MOVE_ENDTHREE"}, - { EawModelAnimationTypes.TransitionFromMoveFrame120, "MOVE_ENDFOUR"}, - { EawModelAnimationTypes.TurnLeftHalf, "TURNL_HALF"}, - { EawModelAnimationTypes.TurnLeftQuarter, "TURNL_QUARTER"}, - { EawModelAnimationTypes.TurnRightHalf, "TURNR_HALF"}, - { EawModelAnimationTypes.TurnRightQuarter, "TURNR_QUARTER"}, - { EawModelAnimationTypes.Deploy, "DEPLOY"}, - { EawModelAnimationTypes.Undeploy, "UNDEPLOY"}, - { EawModelAnimationTypes.Cinematic, "CINEMATIC"}, - { EawModelAnimationTypes.BlockBlaster, "BLOCK_BLASTER"}, - { EawModelAnimationTypes.RedirectBlaster, "REDIRECT_BLASTER"}, - { EawModelAnimationTypes.IdleBlockBlaster, "IDLE_BLOCKBLASTER"}, - { EawModelAnimationTypes.ForceWhirlwindAttack, "FW_ATTACK"}, - { EawModelAnimationTypes.ForceWhirlwindDie, "FW_DIE"}, - { EawModelAnimationTypes.ForceTelekinesisAttack, "FTK_ATTACK"}, - { EawModelAnimationTypes.ForceTelekinesisHold, "FTK_HOLD"}, - { EawModelAnimationTypes.ForceTelekinesisRelease, "FTK_RELEASE"}, - { EawModelAnimationTypes.ForceTelekinesisDie, "FTK_DIE"}, - { EawModelAnimationTypes.EarthquakeAttack, "FB_ATTACK"}, - { EawModelAnimationTypes.EarthquakeHold, "FB_HOLD"}, - { EawModelAnimationTypes.EarthquakeRelease, "FB_RELEASE"}, - { EawModelAnimationTypes.ForceLightningAttack, "FL_ATTACK"}, - { EawModelAnimationTypes.ForceLightningDie, "FL_DIE"}, - { EawModelAnimationTypes.ForceRun, "FORCE_RUN"}, - { EawModelAnimationTypes.TransportLanding, "LAND"}, - { EawModelAnimationTypes.TransportLeaving, "TAKEOFF"}, - { EawModelAnimationTypes.FlameAttack, "FLAME_ATTACK"}, - { EawModelAnimationTypes.Demolition, "DEMOLITION"}, - { EawModelAnimationTypes.BombToss, "BOMBTOSS"}, - { EawModelAnimationTypes.Jump, "JUMP"}, - { EawModelAnimationTypes.FlyIdle, "FLYIDLE"}, - { EawModelAnimationTypes.FlyLand, "FLYLAND"}, - { EawModelAnimationTypes.LandIdle, "FLYLANDIDLE"}, - { EawModelAnimationTypes.Land, "FLYLANDDROP"}, - { EawModelAnimationTypes.HcWin, "HC_WIN"}, - { EawModelAnimationTypes.HcLose, "HC_LOSE"}, - { EawModelAnimationTypes.HcDraw, "HC_DRAW"}, - { EawModelAnimationTypes.ShieldOn, "SHIELD_ON"}, - { EawModelAnimationTypes.ShieldOff, "SHIELD_OFF"}, - { EawModelAnimationTypes.CableAttackDie, "CA_DIE"}, - { EawModelAnimationTypes.DeployedCableAttackDie, "DEPLOYED_CA_DIE"}, - { EawModelAnimationTypes.DeployedDie, "DEPLOYED_DIE"}, - { EawModelAnimationTypes.RunAroundOnFire, "FIRE_MOVE"}, - { EawModelAnimationTypes.FireDie, "FIRE_DIE"}, - { EawModelAnimationTypes.PoundAttack, "POUND_ATTACK"}, - { EawModelAnimationTypes.EatAttack, "EAT_ATTACK"}, - { EawModelAnimationTypes.EatDie, "EATEN_DIE"}, - { EawModelAnimationTypes.MoveWalk, "WALKMOVE"}, - { EawModelAnimationTypes.MoveCrouch, "CROUCHMOVE"}, - { EawModelAnimationTypes.StructureOpen, "OPEN"}, - { EawModelAnimationTypes.StructureHold, "HOLD"}, - { EawModelAnimationTypes.StructureClose, "CLOSE"}, - { EawModelAnimationTypes.IdleCrouch, "CROUCHIDLE"}, - { EawModelAnimationTypes.TurnLeftCrouch, "CROUCHTURNL"}, - { EawModelAnimationTypes.TurnRightCrouch, "CROUCHTURNR"}, - { EawModelAnimationTypes.Build, "BUILD"}, - { EawModelAnimationTypes.TransitionOwnership, "TRANS"}, - { EawModelAnimationTypes.SelfDestruct, "SELF_DESTRUCT"}, - { EawModelAnimationTypes.Attention, "ATTENTION"}, - { EawModelAnimationTypes.Celebrate, "CELEBRATE"}, - { EawModelAnimationTypes.FlinchLeft, "FLINCHL"}, - { EawModelAnimationTypes.FlinchRight, "FLINCHR"}, - { EawModelAnimationTypes.FlinchFront, "FLINCHF"}, - { EawModelAnimationTypes.FlinchBack, "FLINCHB"}, - { EawModelAnimationTypes.AttackFlinchLeft, "ATTACKFLINCHL"}, - { EawModelAnimationTypes.AttackFlinchRight, "ATTACKFLINCHR"}, - { EawModelAnimationTypes.AttackFlinchFront, "ATTACKFLINCHF"}, - { EawModelAnimationTypes.AttackFlinchBack, "ATTACKFLINCHB"}, - { EawModelAnimationTypes.Talk, "TALK"}, - { EawModelAnimationTypes.TalkGesture, "TALKGESTURE"}, - { EawModelAnimationTypes.TalkQuestion, "TALKQUESTION"}, - { EawModelAnimationTypes.Hacking, "HACKING"}, - { EawModelAnimationTypes.Repairing, "REPAIRING"}, - { EawModelAnimationTypes.Choke, "CHOKE"}, - { EawModelAnimationTypes.ChokeDie, "CHOKEDEATH"}, - { EawModelAnimationTypes.DropTroopers, "TROOPDROP"}, - { EawModelAnimationTypes.RopeSlide, "ROPESLIDE"}, - { EawModelAnimationTypes.RopeLand, "ROPELAND"}, - { EawModelAnimationTypes.RopeDrop, "ROPE_DROP"}, - { EawModelAnimationTypes.RopeLift, "ROPE_LIFT"}, - { EawModelAnimationTypes.Alarm, "ALARM"}, - { EawModelAnimationTypes.Warning, "WARNING"}, - { EawModelAnimationTypes.Crushed, "CRUSHED"}, - { EawModelAnimationTypes.PowerDown, "POWERDOWN"}, - { EawModelAnimationTypes.PowerUp, "POWERUP"} + { ModelAnimationType.Idle, "IDLE"}, + { ModelAnimationType.SpaceIdle, "SPACE_IDLE"}, + { ModelAnimationType.Move, "MOVE"}, + { ModelAnimationType.TurnLeft, "TURNL"}, + { ModelAnimationType.TurnRight, "TURNR"}, + { ModelAnimationType.Attack, "ATTACK"}, + { ModelAnimationType.AttackIdle, "ATTACKIDLE"}, + { ModelAnimationType.Die, "DIE"}, + { ModelAnimationType.Rotate, "ROTATE"}, + { ModelAnimationType.SpecialA, "SPECIAL_A"}, + { ModelAnimationType.SpecialB, "SPECIAL_B"}, + { ModelAnimationType.SpecialC, "SPECIAL_C"}, + { ModelAnimationType.TransitionToLeftTurn, "TURNL_BEGIN"}, + { ModelAnimationType.TransitionFromLeftTurn, "TURNL_END"}, + { ModelAnimationType.TransitionToRightTurn, "TURNR_BEGIN"}, + { ModelAnimationType.TransitionFromRightTurn, "TURNR_END"}, + { ModelAnimationType.TransitionToMove, "MOVESTART"}, + { ModelAnimationType.TransitionFromMoveFrame0, "MOVE_ENDONE"}, + { ModelAnimationType.TransitionFromMoveFrame40, "MOVE_ENDTWO"}, + { ModelAnimationType.TransitionFromMoveFrame80, "MOVE_ENDTHREE"}, + { ModelAnimationType.TransitionFromMoveFrame120, "MOVE_ENDFOUR"}, + { ModelAnimationType.TurnLeftHalf, "TURNL_HALF"}, + { ModelAnimationType.TurnLeftQuarter, "TURNL_QUARTER"}, + { ModelAnimationType.TurnRightHalf, "TURNR_HALF"}, + { ModelAnimationType.TurnRightQuarter, "TURNR_QUARTER"}, + { ModelAnimationType.Deploy, "DEPLOY"}, + { ModelAnimationType.Undeploy, "UNDEPLOY"}, + { ModelAnimationType.Cinematic, "CINEMATIC"}, + { ModelAnimationType.BlockBlaster, "BLOCK_BLASTER"}, + { ModelAnimationType.RedirectBlaster, "REDIRECT_BLASTER"}, + { ModelAnimationType.IdleBlockBlaster, "IDLE_BLOCKBLASTER"}, + { ModelAnimationType.ForceWhirlwindAttack, "FW_ATTACK"}, + { ModelAnimationType.ForceWhirlwindDie, "FW_DIE"}, + { ModelAnimationType.ForceTelekinesisAttack, "FTK_ATTACK"}, + { ModelAnimationType.ForceTelekinesisHold, "FTK_HOLD"}, + { ModelAnimationType.ForceTelekinesisRelease, "FTK_RELEASE"}, + { ModelAnimationType.ForceTelekinesisDie, "FTK_DIE"}, + { ModelAnimationType.EarthquakeAttack, "FB_ATTACK"}, + { ModelAnimationType.EarthquakeHold, "FB_HOLD"}, + { ModelAnimationType.EarthquakeRelease, "FB_RELEASE"}, + { ModelAnimationType.ForceLightningAttack, "FL_ATTACK"}, + { ModelAnimationType.ForceLightningDie, "FL_DIE"}, + { ModelAnimationType.ForceRun, "FORCE_RUN"}, + { ModelAnimationType.TransportLanding, "LAND"}, + { ModelAnimationType.TransportLeaving, "TAKEOFF"}, + { ModelAnimationType.FlameAttack, "FLAME_ATTACK"}, + { ModelAnimationType.Demolition, "DEMOLITION"}, + { ModelAnimationType.BombToss, "BOMBTOSS"}, + { ModelAnimationType.Jump, "JUMP"}, + { ModelAnimationType.FlyIdle, "FLYIDLE"}, + { ModelAnimationType.FlyLand, "FLYLAND"}, + { ModelAnimationType.LandIdle, "FLYLANDIDLE"}, + { ModelAnimationType.Land, "FLYLANDDROP"}, + { ModelAnimationType.HcWin, "HC_WIN"}, + { ModelAnimationType.HcLose, "HC_LOSE"}, + { ModelAnimationType.HcDraw, "HC_DRAW"}, + { ModelAnimationType.ShieldOn, "SHIELD_ON"}, + { ModelAnimationType.ShieldOff, "SHIELD_OFF"}, + { ModelAnimationType.CableAttackDie, "CA_DIE"}, + { ModelAnimationType.DeployedCableAttackDie, "DEPLOYED_CA_DIE"}, + { ModelAnimationType.DeployedDie, "DEPLOYED_DIE"}, + { ModelAnimationType.RunAroundOnFire, "FIRE_MOVE"}, + { ModelAnimationType.FireDie, "FIRE_DIE"}, + { ModelAnimationType.PoundAttack, "POUND_ATTACK"}, + { ModelAnimationType.EatAttack, "EAT_ATTACK"}, + { ModelAnimationType.EatDie, "EATEN_DIE"}, + { ModelAnimationType.MoveWalk, "WALKMOVE"}, + { ModelAnimationType.MoveCrouch, "CROUCHMOVE"}, + { ModelAnimationType.StructureOpen, "OPEN"}, + { ModelAnimationType.StructureHold, "HOLD"}, + { ModelAnimationType.StructureClose, "CLOSE"}, + { ModelAnimationType.IdleCrouch, "CROUCHIDLE"}, + { ModelAnimationType.TurnLeftCrouch, "CROUCHTURNL"}, + { ModelAnimationType.TurnRightCrouch, "CROUCHTURNR"}, + { ModelAnimationType.Build, "BUILD"}, + { ModelAnimationType.TransitionOwnership, "TRANS"}, + { ModelAnimationType.SelfDestruct, "SELF_DESTRUCT"}, + { ModelAnimationType.Attention, "ATTENTION"}, + { ModelAnimationType.Celebrate, "CELEBRATE"}, + { ModelAnimationType.FlinchLeft, "FLINCHL"}, + { ModelAnimationType.FlinchRight, "FLINCHR"}, + { ModelAnimationType.FlinchFront, "FLINCHF"}, + { ModelAnimationType.FlinchBack, "FLINCHB"}, + { ModelAnimationType.AttackFlinchLeft, "ATTACKFLINCHL"}, + { ModelAnimationType.AttackFlinchRight, "ATTACKFLINCHR"}, + { ModelAnimationType.AttackFlinchFront, "ATTACKFLINCHF"}, + { ModelAnimationType.AttackFlinchBack, "ATTACKFLINCHB"}, + { ModelAnimationType.Talk, "TALK"}, + { ModelAnimationType.TalkGesture, "TALKGESTURE"}, + { ModelAnimationType.TalkQuestion, "TALKQUESTION"}, + { ModelAnimationType.Hacking, "HACKING"}, + { ModelAnimationType.Repairing, "REPAIRING"}, + { ModelAnimationType.Choke, "CHOKE"}, + { ModelAnimationType.ChokeDie, "CHOKEDEATH"}, + { ModelAnimationType.DropTroopers, "TROOPDROP"}, + { ModelAnimationType.RopeSlide, "ROPESLIDE"}, + { ModelAnimationType.RopeLand, "ROPELAND"}, + { ModelAnimationType.RopeDrop, "ROPE_DROP"}, + { ModelAnimationType.RopeLift, "ROPE_LIFT"}, + { ModelAnimationType.Alarm, "ALARM"}, + { ModelAnimationType.Warning, "WARNING"}, + { ModelAnimationType.Crushed, "CRUSHED"}, + { ModelAnimationType.PowerDown, "POWERDOWN"}, + { ModelAnimationType.PowerUp, "POWERUP"} }; + + // ReSharper disable StringLiteralTypo + private static readonly Dictionary FocSupportedAnimations = + + EawSupportedAnimations.Concat(new Dictionary + { + { ModelAnimationType.SpinMove, "SPINMOVE" }, + { ModelAnimationType.ForceRevealBegin, "FORCE_REVEAL_BEGIN" }, + { ModelAnimationType.ForceRevealLoop, "FORCE_REVEAL_LOOP" }, + { ModelAnimationType.ForceRevealEnd, "FORCE_REVEAL_END" }, + { ModelAnimationType.SaberThrow, "SWORD_THROW" }, + { ModelAnimationType.SaberControl, "SWORD_CONTROL" }, + { ModelAnimationType.SaberCatch, "SWORD_CATCH" }, + { ModelAnimationType.SaberSpin, "SWORDSPIN" }, + { ModelAnimationType.ContaminateAttack, "CONTAMINATE_ATTACK" }, + { ModelAnimationType.ContaminateLoop, "CONTAMINATE_LOOP" }, + { ModelAnimationType.ContaminateRelease, "CONTAMINATE_RELEASE" }, + { ModelAnimationType.DeployedWalk, "WALK" }, + { ModelAnimationType.PadBuild, "PAD_BUILD" }, + { ModelAnimationType.PadSell, "PAD_SELL" }, + { ModelAnimationType.Heal, "HEAL" }, + }) + .ToDictionary(x => x.Key, x => x.Value); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs index 00ca4f2..b42eec6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs @@ -24,7 +24,14 @@ public FontManager(GameRepository repository, GameEngineErrorReporterWrapper err _fontManager = new NetFontManager(); } - public IReadOnlyCollection FontNames => [.._fontNames]; + public IReadOnlyCollection FontNames + { + get + { + ThrowIfNotInitialized(); + return [.._fontNames]; + } + } public FontData? CreateFont(string fontName, int size, bool bold, bool italic, bool staticSize, float stretchFactor) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs index fe8f546..ef2344f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs @@ -28,7 +28,8 @@ public IEnumerable GetFontFamilies() return fonts; } - static IEnumerable<(Gdi32.ENUMLOGFONTEXDV lpelfe, Gdi32.ENUMTEXTMETRIC _, Gdi32.FontType __)> GetFonts(Gdi32.SafeHDC hdc) + private static IEnumerable<(Gdi32.ENUMLOGFONTEXDV lpelfe, Gdi32.ENUMTEXTMETRIC _, Gdi32.FontType __)> GetFonts( + Gdi32.SafeHDC hdc) { return Gdi32.EnumFontFamiliesEx(hdc, lfCharSet: CharacterSet.DEFAULT_CHARSET); } From d9eecf49f5aca686920625bf0a717e41f98d68ad Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 21 Feb 2026 14:35:49 +0100 Subject: [PATCH 14/15] refactor commandbar parser --- .../CommandBar/CommandBarGameManager.cs | 4 +- .../NamedObjects/CommandBarComponentParser.cs | 739 ++++++++++-------- .../Primitives/PetroglyphXmlBooleanParser.cs | 2 +- .../PetroglyphXmlLooseStringListParser.cs | 2 +- .../Primitives/PetroglyphXmlVector2FParser.cs | 2 +- 5 files changed, 418 insertions(+), 331 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 8b5d175..b19112a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -84,8 +84,8 @@ protected override async Task InitializeCoreAsync(CancellationToken token) var parsedCommandBarComponents = new FrugalValueListDictionary(); await Task.Run(() => contentParser.ParseEntriesFromFileListXml( - "DATA\\XML\\CommandBarComponentFiles.XML", - ".\\DATA\\XML", + ".\\Data\\XML\\CommandBarComponentFiles.xml", + ".\\DATA\\XML\\", parsedCommandBarComponents, VerifyFilePathLength), token); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs index be3dcc0..69a3120 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs @@ -1,7 +1,6 @@ using System; using System.Collections.ObjectModel; using System.Xml.Linq; -using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; @@ -18,330 +17,6 @@ protected override CommandBarComponentData CreateXmlObject(string name, Crc32 na return new CommandBarComponentData(name, nameCrc, location); } - protected override bool ParseTag(XElement tag, CommandBarComponentData componentData, in IReadOnlyFrugalValueListDictionary parseState) - { - switch (tag.Name.LocalName) - { - case CommandBarComponentTags.SelectedTextureName: - componentData.SelectedTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.BlankTextureName: - componentData.BlankTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.IconAlternateTextureName: - componentData.IconAlternateTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.MouseOverTextureName: - componentData.MouseOverTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.BarTextureName: - componentData.BarTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.BarOverlayName: - componentData.BarOverlayNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.AlternateFontName: - componentData.AlternateFontNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.TooltipText: - componentData.TooltipTexts = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.LowerEffectTextureName: - componentData.LowerEffectTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.UpperEffectTextureName: - componentData.UpperEffectTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.OverlayTextureName: - componentData.OverlayTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - case CommandBarComponentTags.Overlay2TextureName: - componentData.Overlay2TextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); - return true; - - case CommandBarComponentTags.IconTextureName: - componentData.IconTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DisabledTextureName: - componentData.DisabledTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.FlashTextureName: - componentData.FlashTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BuildTextureName: - componentData.BuildTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ModelName: - componentData.ModelName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BoneName: - componentData.BoneName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.CursorTextureName: - componentData.CursorTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.FontName: - componentData.FontName = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ClickSfx: - componentData.ClickSfx = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.MouseOverSfx: - componentData.MouseOverSfx = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.RightClickSfx: - componentData.RightClickSfx = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Type: - componentData.Type = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Group: - componentData.Group = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.AssociatedText: - componentData.AssociatedText = PetroglyphXmlStringParser.Instance.Parse(tag); - return true; - - case CommandBarComponentTags.DragAndDrop: - componentData.DragAndDrop = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DragSelect: - componentData.DragSelect = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Receptor: - componentData.Receptor = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Toggle: - componentData.Toggle = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Tab: - componentData.Tab = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Hidden: - componentData.Hidden = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ClearColor: - componentData.ClearColor = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Disabled: - componentData.Disabled = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.SwapTexture: - componentData.SwapTexture = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DrawAdditive: - componentData.DrawAdditive = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Editable: - componentData.Editable = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TextOutline: - componentData.TextOutline = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Stackable: - componentData.Stackable = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ModelOffsetX: - componentData.ModelOffsetX = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ModelOffsetY: - componentData.ModelOffsetY = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ScaleModelX: - componentData.ScaleModelX = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ScaleModelY: - componentData.ScaleModelY = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Collideable: - componentData.Collideable = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TextEmboss: - componentData.TextEmboss = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ShouldGhost: - componentData.ShouldGhost = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.GhostBaseOnly: - componentData.GhostBaseOnly = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.CrossFade: - componentData.CrossFade = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.LeftJustified: - componentData.LeftJustified = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.RightJustified: - componentData.RightJustified = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.NoShell: - componentData.NoShell = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.SnapDrag: - componentData.SnapDrag = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.SnapLocation: - componentData.SnapLocation = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.OffsetRender: - componentData.OffsetRender = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BlinkFade: - componentData.BlinkFade = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.NoHiddenCollision: - componentData.NoHiddenCollision = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ManualOffset: - componentData.ManualOffset = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.SelectedAlpha: - componentData.SelectedAlpha = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.PixelAlign: - componentData.PixelAlign = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.CanDragStack: - componentData.CanDragStack = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.CanAnimate: - componentData.CanAnimate = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.LoopAnim: - componentData.LoopAnim = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.SmoothBar: - componentData.SmoothBar = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.OutlinedBar: - componentData.OutlinedBar = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DragBack: - componentData.DragBack = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.LowerEffectAdditive: - componentData.LowerEffectAdditive = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.UpperEffectAdditive: - componentData.UpperEffectAdditive = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ClickShift: - componentData.ClickShift = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TutorialScene: - componentData.TutorialScene = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DialogScene: - componentData.DialogScene = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ShouldRenderAtDragPos: - componentData.ShouldRenderAtDragPos = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DisableDarken: - componentData.DisableDarken = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.AnimateBack: - componentData.AnimateBack = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.AnimateUpperEffect: - componentData.AnimateUpperEffect = PetroglyphXmlBooleanParser.Instance.Parse(tag); - return true; - - case CommandBarComponentTags.Size: - componentData.Size = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TextOffset: - componentData.TextOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TextOffset2: - componentData.TextOffset2 = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Offset: - componentData.Offset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DefaultOffset: - componentData.DefaultOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DefaultOffsetWidescreen: - componentData.DefaultOffsetWidescreen = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.IconOffset: - componentData.IconOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.MouseOverOffset: - componentData.MouseOverOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.DisabledOffset: - componentData.DisabledOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BuildDialOffset: - componentData.BuildDialOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BuildDial2Offset: - componentData.BuildDial2Offset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.LowerEffectOffset: - componentData.LowerEffectOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.UpperEffectOffset: - componentData.UpperEffectOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.OverlayOffset: - componentData.OverlayOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.Overlay2Offset: - componentData.Overlay2Offset = PetroglyphXmlVector2FParser.Instance.Parse(tag); - return true; - - case CommandBarComponentTags.MaxTextLength: - componentData.MaxTextLength = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.FontPointSize: - componentData.FontPointSize = (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - - case CommandBarComponentTags.Scale: - componentData.Scale = PetroglyphXmlFloatParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BlinkRate: - componentData.BlinkRate = PetroglyphXmlFloatParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.MaxTextWidth: - componentData.MaxTextWidth = PetroglyphXmlFloatParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.BlinkDuration: - componentData.BlinkDuration = PetroglyphXmlFloatParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.ScaleDuration: - componentData.ScaleDuration = PetroglyphXmlFloatParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.AnimFps: - componentData.AnimFps = PetroglyphXmlFloatParser.Instance.Parse(tag); - return true; - - case CommandBarComponentTags.BaseLayer: - componentData.BaseLayer = (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.MaxBarLevel: - componentData.MaxBarLevel = (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); - return true; - - case CommandBarComponentTags.Color: - componentData.Color = PetroglyphXmlRgbaColorParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TextColor: - componentData.TextColor = PetroglyphXmlRgbaColorParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.TextColor2: - componentData.TextColor2 = PetroglyphXmlRgbaColorParser.Instance.Parse(tag); - return true; - case CommandBarComponentTags.MaxBarColor: - componentData.MaxBarColor = PetroglyphXmlRgbaColorParser.Instance.Parse(tag); - return true; - - default: return true; - } - } protected override void ValidateAndFixupValues(CommandBarComponentData xmlData, XElement element) { @@ -357,11 +32,423 @@ protected override void ValidateAndFixupValues(CommandBarComponentData xmlData, xmlData.FixupValues(); } - private sealed class CommandBarComponentDataXmlTagMapper(IServiceProvider serviceProvider) + private sealed class CommandBarComponentDataXmlTagMapper(IServiceProvider serviceProvider) : XmlTagMapper(serviceProvider) { protected override void BuildMappings() { + AddMapping( + CommandBarComponentTags.SelectedTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.SelectedTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.BlankTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.BlankTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.IconTextureName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.IconTextureName = val); + AddMapping( + CommandBarComponentTags.IconAlternateTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.IconAlternateTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.MouseOverTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.MouseOverTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.DisabledTextureName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.DisabledTextureName = val); + AddMapping( + CommandBarComponentTags.FlashTextureName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.FlashTextureName = val); + AddMapping( + CommandBarComponentTags.BarTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.BarTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.BarOverlayName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.BarOverlayNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.BuildTextureName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.BuildTextureName = val); + AddMapping( + CommandBarComponentTags.ModelName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.ModelName = val); + AddMapping( + CommandBarComponentTags.BoneName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.BoneName = val); + AddMapping( + CommandBarComponentTags.CursorTextureName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.CursorTextureName = val); + AddMapping( + CommandBarComponentTags.FontName, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.FontName = val); + AddMapping( + CommandBarComponentTags.AlternateFontName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.AlternateFontNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.TooltipText, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.TooltipTexts = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.ClickSfx, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.ClickSfx = val); + AddMapping( + CommandBarComponentTags.MouseOverSfx, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.MouseOverSfx = val); + AddMapping( + CommandBarComponentTags.LowerEffectTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.LowerEffectTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.UpperEffectTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.UpperEffectTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.OverlayTextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.OverlayTextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.Overlay2TextureName, + PetroglyphXmlLooseStringListParser.Instance.Parse, + (obj, val) => obj.Overlay2TextureNames = new ReadOnlyCollection(val)); + AddMapping( + CommandBarComponentTags.RightClickSfx, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.RightClickSfx = val); + AddMapping( + CommandBarComponentTags.Type, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.Type = val); + AddMapping( + CommandBarComponentTags.Group, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.Group = val); + AddMapping( + CommandBarComponentTags.DragAndDrop, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.DragAndDrop = val); + AddMapping( + CommandBarComponentTags.DragSelect, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.DragSelect = val); + AddMapping( + CommandBarComponentTags.Receptor, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Receptor = val); + AddMapping( + CommandBarComponentTags.Toggle, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Toggle = val); + AddMapping( + CommandBarComponentTags.Tab, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Tab = val); + AddMapping( + CommandBarComponentTags.AssociatedText, + PetroglyphXmlStringParser.Instance.Parse, + (obj, val) => obj.AssociatedText = val); + AddMapping( + CommandBarComponentTags.Hidden, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Hidden = val); + AddMapping( + CommandBarComponentTags.Scale, + PetroglyphXmlFloatParser.Instance.Parse, + (obj, val) => obj.Scale = val); + AddMapping( + CommandBarComponentTags.Color, + PetroglyphXmlRgbaColorParser.Instance.Parse, + (obj, val) => obj.Color = val); + AddMapping( + CommandBarComponentTags.TextColor, + PetroglyphXmlRgbaColorParser.Instance.Parse, + (obj, val) => obj.TextColor = val); + AddMapping( + CommandBarComponentTags.TextColor2, + PetroglyphXmlRgbaColorParser.Instance.Parse, + (obj, val) => obj.TextColor2 = val); + AddMapping( + CommandBarComponentTags.Size, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.Size = val); + AddMapping( + CommandBarComponentTags.ClearColor, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ClearColor = val); + AddMapping( + CommandBarComponentTags.Disabled, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Disabled = val); + AddMapping( + CommandBarComponentTags.SwapTexture, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.SwapTexture = val); + AddMapping( + CommandBarComponentTags.BaseLayer, + x => (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(x), + (obj, val) => obj.BaseLayer = val); + AddMapping( + CommandBarComponentTags.DrawAdditive, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.DrawAdditive = val); + AddMapping( + CommandBarComponentTags.TextOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.TextOffset = val); + AddMapping( + CommandBarComponentTags.TextOffset2, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.TextOffset2 = val); + AddMapping( + CommandBarComponentTags.Offset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.Offset = val); + AddMapping( + CommandBarComponentTags.DefaultOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.DefaultOffset = val); + AddMapping( + CommandBarComponentTags.DefaultOffsetWidescreen, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.DefaultOffsetWidescreen = val); + AddMapping( + CommandBarComponentTags.IconOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.IconOffset = val); + AddMapping( + CommandBarComponentTags.MouseOverOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.MouseOverOffset = val); + AddMapping( + CommandBarComponentTags.DisabledOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.DisabledOffset = val); + AddMapping( + CommandBarComponentTags.BuildDialOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.BuildDialOffset = val); + AddMapping( + CommandBarComponentTags.BuildDial2Offset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.BuildDial2Offset = val); + AddMapping( + CommandBarComponentTags.LowerEffectOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.LowerEffectOffset = val); + AddMapping( + CommandBarComponentTags.UpperEffectOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.UpperEffectOffset = val); + AddMapping( + CommandBarComponentTags.OverlayOffset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.OverlayOffset = val); + AddMapping( + CommandBarComponentTags.Overlay2Offset, + PetroglyphXmlVector2FParser.Instance.Parse, + (obj, val) => obj.Overlay2Offset = val); + AddMapping( + CommandBarComponentTags.Editable, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Editable = val); + AddMapping( + CommandBarComponentTags.MaxTextLength, + PetroglyphXmlUnsignedIntegerParser.Instance.Parse, + (obj, val) => obj.MaxTextLength = val); + AddMapping( + CommandBarComponentTags.BlinkRate, + PetroglyphXmlFloatParser.Instance.Parse, + (obj, val) => obj.BlinkRate = val); + AddMapping( + CommandBarComponentTags.FontPointSize, + x => (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(x), + (obj, val) => obj.FontPointSize = val); + AddMapping( + CommandBarComponentTags.TextOutline, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.TextOutline = val); + AddMapping( + CommandBarComponentTags.MaxTextWidth, + PetroglyphXmlFloatParser.Instance.Parse, + (obj, val) => obj.MaxTextWidth = val); + AddMapping( + CommandBarComponentTags.Stackable, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Stackable = val); + AddMapping( + CommandBarComponentTags.ModelOffsetX, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ModelOffsetX = val); + AddMapping( + CommandBarComponentTags.ModelOffsetY, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ModelOffsetY = val); + AddMapping( + CommandBarComponentTags.ScaleModelX, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ScaleModelX = val); + AddMapping( + CommandBarComponentTags.ScaleModelY, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ScaleModelY = val); + AddMapping( + CommandBarComponentTags.Collideable, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.Collideable = val); + AddMapping( + CommandBarComponentTags.TextEmboss, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.TextEmboss = val); + AddMapping( + CommandBarComponentTags.ShouldGhost, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ShouldGhost = val); + AddMapping( + CommandBarComponentTags.GhostBaseOnly, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.GhostBaseOnly = val); + AddMapping( + CommandBarComponentTags.MaxBarLevel, + x => PetroglyphXmlIntegerParser.Instance.Parse(x), + (obj, val) => obj.MaxBarLevel = val); + AddMapping( + CommandBarComponentTags.MaxBarColor, + PetroglyphXmlRgbaColorParser.Instance.Parse, + (obj, val) => obj.MaxBarColor = val); + AddMapping( + CommandBarComponentTags.CrossFade, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.CrossFade = val); + AddMapping( + CommandBarComponentTags.LeftJustified, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.LeftJustified = val); + AddMapping( + CommandBarComponentTags.RightJustified, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.RightJustified = val); + AddMapping( + CommandBarComponentTags.NoShell, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.NoShell = val); + AddMapping( + CommandBarComponentTags.SnapDrag, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.SnapDrag = val); + AddMapping( + CommandBarComponentTags.SnapLocation, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.SnapLocation = val); + AddMapping( + CommandBarComponentTags.BlinkDuration, + PetroglyphXmlFloatParser.Instance.Parse, + (obj, val) => obj.BlinkDuration = val); + AddMapping( + CommandBarComponentTags.ScaleDuration, + PetroglyphXmlFloatParser.Instance.Parse, + (obj, val) => obj.ScaleDuration = val); + AddMapping( + CommandBarComponentTags.OffsetRender, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.OffsetRender = val); + AddMapping( + CommandBarComponentTags.BlinkFade, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.BlinkFade = val); + AddMapping( + CommandBarComponentTags.NoHiddenCollision, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.NoHiddenCollision = val); + AddMapping( + CommandBarComponentTags.ManualOffset, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ManualOffset = val); + AddMapping( + CommandBarComponentTags.SelectedAlpha, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.SelectedAlpha = val); + AddMapping( + CommandBarComponentTags.PixelAlign, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.PixelAlign = val); + AddMapping( + CommandBarComponentTags.CanDragStack, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.CanDragStack = val); + AddMapping( + CommandBarComponentTags.CanAnimate, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.CanAnimate = val); + AddMapping( + CommandBarComponentTags.AnimFps, + PetroglyphXmlFloatParser.Instance.Parse, + (obj, val) => obj.AnimFps = val); + AddMapping( + CommandBarComponentTags.LoopAnim, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.LoopAnim = val); + AddMapping( + CommandBarComponentTags.SmoothBar, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.SmoothBar = val); + AddMapping( + CommandBarComponentTags.OutlinedBar, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.OutlinedBar = val); + AddMapping( + CommandBarComponentTags.DragBack, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.DragBack = val); + AddMapping( + CommandBarComponentTags.LowerEffectAdditive, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.LowerEffectAdditive = val); + AddMapping( + CommandBarComponentTags.UpperEffectAdditive, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.UpperEffectAdditive = val); + AddMapping( + CommandBarComponentTags.ClickShift, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ClickShift = val); + AddMapping( + CommandBarComponentTags.TutorialScene, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.TutorialScene = val); + AddMapping( + CommandBarComponentTags.DialogScene, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.DialogScene = val); + AddMapping( + CommandBarComponentTags.ShouldRenderAtDragPos, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.ShouldRenderAtDragPos = val); + AddMapping( + CommandBarComponentTags.DisableDarken, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.DisableDarken = val); + AddMapping( + CommandBarComponentTags.AnimateBack, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.AnimateBack = val); + AddMapping( + CommandBarComponentTags.AnimateUpperEffect, + PetroglyphXmlBooleanParser.Instance.Parse, + (obj, val) => obj.AnimateUpperEffect = val); } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs index b52554d..c609d0f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs @@ -9,7 +9,7 @@ public sealed class PetroglyphXmlBooleanParser : PetroglyphPrimitiveXmlParser false; - internal override int EngineDataTypeId => 0x0; + internal override int EngineDataTypeId => 0x0 & 0x50; private PetroglyphXmlBooleanParser() { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs index 47a0603..3859a3a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -20,7 +20,7 @@ public sealed class PetroglyphXmlLooseStringListParser : PetroglyphPrimitiveXmlP public static readonly PetroglyphXmlLooseStringListParser Instance = new(); private protected override IList DefaultValue => []; - internal override int EngineDataTypeId => 0x18; + internal override int EngineDataTypeId => 0x18 & 0x1B; private PetroglyphXmlLooseStringListParser() { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs index c35e5b8..488ee0a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs @@ -14,7 +14,7 @@ public sealed class PetroglyphXmlVector2FParser : PetroglyphPrimitiveXmlParser default; - internal override int EngineDataTypeId => throw new NotImplementedException(); + internal override int EngineDataTypeId => 0x0F; private PetroglyphXmlVector2FParser() { From 3f5ee17a946ed6d89e8f2ead3383d1ddbc8436a1 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 22 Feb 2026 19:07:14 +0100 Subject: [PATCH 15/15] Shell scale and offset calculation --- .../CommandBar/CommandBarGameManager.cs | 213 +--------------- .../CommandBarGameManager_Initialization.cs | 237 ++++++++++++++++++ .../Components/CommandBarShellComponent.cs | 26 ++ .../Rendering/Matrix3x4.cs | 198 +++++++++++++++ .../Utilities/PGMath.cs | 12 + .../Utilities/PGMath.cs | 4 +- 6 files changed, 482 insertions(+), 208 deletions(-) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager_Initialization.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Matrix3x4.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/PGMath.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index b19112a..4478fea 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -1,27 +1,20 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.CommandBar.Components; -using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; -using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.MTD.Files; using PG.StarWarsGame.Files.MTD.Services; using System; using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AnakinRaW.CommonUtilities.Collections; -using PG.StarWarsGame.Engine.Xml; +using System.Numerics; namespace PG.StarWarsGame.Engine.CommandBar; -internal class CommandBarGameManager( +internal partial class CommandBarGameManager( GameRepository repository, PGRender pgRender, IGameConstants gameConstants, @@ -33,7 +26,6 @@ internal class CommandBarGameManager( private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); private readonly Dictionary _groups = new(); - private readonly PGRender _pgRender = pgRender; private bool _megaTextureExists; private FontData? _defaultFont; @@ -42,7 +34,7 @@ internal class CommandBarGameManager( public IReadOnlyDictionary Groups => _groups; - public FontData? DefaultFont + public FontData? DefaultFont { get { @@ -70,200 +62,7 @@ private set } } - protected override async Task InitializeCoreAsync(CancellationToken token) - { - Logger?.LogInformation("Creating command bar components..."); - - var contentParser = new PetroglyphStarWarsGameXmlParser(GameRepository, new PetroglyphStarWarsGameXmlParseSettings - { - GameManager = ToString(), - InvalidObjectXmlFailsInitialization = true, - InvalidFilesListXmlFailsInitialization = true - }, ServiceProvider, ErrorReporter); - - var parsedCommandBarComponents = new FrugalValueListDictionary(); - - await Task.Run(() => contentParser.ParseEntriesFromFileListXml( - ".\\Data\\XML\\CommandBarComponentFiles.xml", - ".\\DATA\\XML\\", - parsedCommandBarComponents, - VerifyFilePathLength), - token); - - // Create Scene - // Create Camera - // Resize(true) - - foreach (var parsedCommandBarComponent in parsedCommandBarComponents.Values) - { - var component = CommandBarBaseComponent.Create(parsedCommandBarComponent, ErrorReporter); - if (component is not null) - { - var crc = _hashingService.GetCrc32(component.Name, PGConstants.DefaultPGEncoding); - NamedEntries.Add(crc, component); - } - } - - SetComponentGroup(Components); - SetMegaTexture(); - SetDefaultFont(); - - LinkComponentsToShell(); - LinkComponentsWithActions(); - } - - private void LinkComponentsWithActions() - { - var nameLookup = SupportedCommandBarComponentData.GetComponentIdsForEngine(GameRepository.EngineType); - - foreach (var idPair in nameLookup) - { - var crc = _hashingService.GetCrc32(idPair.Value, PGConstants.DefaultPGEncoding); - if (NamedEntries.TryGetFirstValue(crc, out var component)) - component.Id = idPair.Key; - } - } - - private void LinkComponentsToShell() - { - if (!Groups.TryGetValue(CommandBarConstants.ShellGroupName, out var shellGroup)) - return; - - var modelCache = new Dictionary(); - foreach (var component in Components) - { - if (component.Type == CommandBarComponentType.Shell) - continue; - - foreach (var shellComponent in shellGroup.Components) - { - if (LinkToShell(component, shellComponent as CommandBarShellComponent, modelCache)) - break; - } - } - - foreach (var model in modelCache.Values) - model?.Dispose(); - } - - private bool LinkToShell( - CommandBarBaseComponent component, - CommandBarShellComponent? shell, - IDictionary modelCache) - { - if (shell is null) - { - ErrorReporter.Assert( - EngineAssert.FromNullOrEmpty( - [component.Name], $"Cannot link component '{component}' because shell component is null.")); - return false; - } - - var componentName = component.Name; - if (string.IsNullOrEmpty(componentName)) - return false; - - var modelPath = shell.ModelPath; - if (string.IsNullOrEmpty(modelPath)) - return false; - - if (!modelCache.TryGetValue(shell.Name, out var model)) - { - model = _pgRender.LoadModelAndAnimations(modelPath.AsSpan(), null, true); - modelCache.Add(shell.Name, model); - } - - if (model is null) - { - ErrorReporter.Assert( - EngineAssert.FromNullOrEmpty( - [$"component='{component.Name}'", $"shell='{shell.Name}'"], - $"Cannot link component '{componentName}' to shell '{shell.Name}' because model '{modelPath}' could not be loaded.")); - return false; - } - - if (!model.IsModel) - { - ErrorReporter.Assert( - EngineAssert.FromNullOrEmpty( - [$"component='{component.Name}'", $"shell='{shell.Name}'"], - $"Cannot link component '{componentName}' to shell '{shell.Name}' because the loaded file '{modelPath}' is not a model.")); - return false; - } - - var boneIndex = model.IndexOfBone(componentName); - - if (boneIndex == -1) - return false; - component.Bone = boneIndex; - component.ParentShell = shell; - return true; - } - - private void SetDefaultFont() - { - // The code is only triggered iff at least one Text CommandbarBar component existed - if (Components.FirstOrDefault(x => x is CommandBarTextComponent or CommandBarTextButtonComponent) is null) - return; - - if (_defaultFont is null) - { - // TODO: From GameConstants - var fontName = PGConstants.DefaultUnicodeFontName; - var size = 11; - var font = fontManager.CreateFont(fontName, size, true, false, false, 1.0f); - if (font is null) - ErrorReporter.Assert(EngineAssert.FromNullOrEmpty([ToString()], $"Unable to create Default from name {fontName}")); - DefaultFont = font; - } - } - - private void SetMegaTexture() - { - // The code is only triggered iff at least one Shell CommandbarBar component existed - if (Components.FirstOrDefault(x => x is CommandBarShellComponent) is null) - return; - // Note: The tag is not used by the engine - var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{CommandBarConstants.MegaTextureBaseName}.mtd"); - using var megaTexture = GameRepository.TryOpenFile(mtdPath); - - try - { - MegaTextureFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); - } - catch (BinaryCorruptedException e) - { - var message = $"Failed to load MTD file '{mtdPath}': {e.Message}"; - Logger?.LogError(e, message); - ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, [], message)); - } - _megaTextureExists = GameRepository.TextureRepository.FileExists($"{CommandBarConstants.MegaTextureBaseName}.tga"); - } - - private void SetComponentGroup(IEnumerable components) - { - var groupData = components - .Where(x => !string.IsNullOrEmpty(x.XmlData.Group)) - .GroupBy(x => x.XmlData.Group!, StringComparer.Ordinal); + public Vector3 CommandBarScale { get; } - foreach (var grouping in groupData) - { - var group = new CommandBarComponentGroup(grouping.Key, grouping); - _groups.Add(grouping.Key, group); - foreach (var component in grouping) - component.Group = group; - } - } - - private void VerifyFilePathLength(string filePath) - { - if (filePath.Length > PGConstants.MaxCommandBarDatabaseFileName) - { - ErrorReporter.Report(new InitializationError - { - GameManager = ToString(), - Message = $"CommandBar file '{filePath}' is longer than {PGConstants.MaxCommandBarDatabaseFileName} characters." - }); - } - } -} + public Vector3 CommandBarOffset { get; internal set; } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager_Initialization.cs new file mode 100644 index 0000000..8bfa0d2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager_Initialization.cs @@ -0,0 +1,237 @@ +using AnakinRaW.CommonUtilities.Collections; +using Microsoft.Extensions.Logging; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.CommandBar.Components; +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.Rendering; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.Binary; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading; +using System.Threading.Tasks; + +namespace PG.StarWarsGame.Engine.CommandBar; + +internal partial class CommandBarGameManager +{ + protected override async Task InitializeCoreAsync(CancellationToken token) + { + Logger?.LogInformation("Creating command bar components..."); + + var contentParser = new PetroglyphStarWarsGameXmlParser(GameRepository, new PetroglyphStarWarsGameXmlParseSettings + { + GameManager = ToString(), + InvalidObjectXmlFailsInitialization = true, + InvalidFilesListXmlFailsInitialization = true + }, ServiceProvider, ErrorReporter); + + var parsedCommandBarComponents = new FrugalValueListDictionary(); + + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( + ".\\Data\\XML\\CommandBarComponentFiles.xml", + ".\\DATA\\XML\\", + parsedCommandBarComponents, + VerifyFilePathLength), + token); + + CommandBarOffset = new Vector3(-0.5f, -0.5f, 0.0f); + + // Create Scene + // Create Camera + // Resize(force: true) + + foreach (var parsedCommandBarComponent in parsedCommandBarComponents.Values) + { + var component = CommandBarBaseComponent.Create(parsedCommandBarComponent, ErrorReporter); + if (component is not null) + { + var crc = _hashingService.GetCrc32(component.Name, PGConstants.DefaultPGEncoding); + NamedEntries.Add(crc, component); + } + + if (component is CommandBarShellComponent shellComponent) + SetModelTransform(shellComponent); + } + + SetComponentGroup(Components); + SetMegaTexture(); + SetDefaultFont(); + + LinkComponentsToShell(); + LinkComponentsWithActions(); + + + // CommandBarClass::Set_Encyclopedia_Delay_Time(this); + // CommandBarClass::Find_Neighbors(this); + + // CommandBarClass::Load_Hero_Particles(this); + // CommandBarClass::Load_Corruption_Particle(this); + } + + private void SetModelTransform(CommandBarShellComponent shellComponent) + { + if (string.IsNullOrEmpty(shellComponent.ModelPath) || + !GameRepository.ModelRepository.FileExists(shellComponent.ModelPath)) + return; + shellComponent.SetOffsetAndScale(CommandBarOffset, CommandBarScale); + } + + private void LinkComponentsWithActions() + { + // NB: Currently we do not have "action" but we keep the original method name + var nameLookup = SupportedCommandBarComponentData.GetComponentIdsForEngine(GameRepository.EngineType); + foreach (var idPair in nameLookup) + { + var crc = _hashingService.GetCrc32(idPair.Value, PGConstants.DefaultPGEncoding); + if (NamedEntries.TryGetFirstValue(crc, out var component)) + component.Id = idPair.Key; + } + } + + private void LinkComponentsToShell() + { + if (!Groups.TryGetValue(CommandBarConstants.ShellGroupName, out var shellGroup)) + return; + + var modelCache = new Dictionary(); + foreach (var component in Components) + { + if (component.Type == CommandBarComponentType.Shell) + continue; + + foreach (var shellComponent in shellGroup.Components) + { + if (LinkToShell(component, shellComponent as CommandBarShellComponent, modelCache)) + break; + } + } + + foreach (var model in modelCache.Values) + model?.Dispose(); + } + + private bool LinkToShell( + CommandBarBaseComponent component, + CommandBarShellComponent? shell, + IDictionary modelCache) + { + if (shell is null) + { + ErrorReporter.Assert( + EngineAssert.FromNullOrEmpty( + [component.Name], $"Cannot link component '{component}' because shell component is null.")); + return false; + } + + var componentName = component.Name; + if (string.IsNullOrEmpty(componentName)) + return false; + + var modelPath = shell.ModelPath; + if (string.IsNullOrEmpty(modelPath)) + return false; + + if (!modelCache.TryGetValue(shell.Name, out var model)) + { + model = pgRender.LoadModelAndAnimations(modelPath.AsSpan(), null, true); + modelCache.Add(shell.Name, model); + } + + if (model is null) + { + ErrorReporter.Assert( + EngineAssert.FromNullOrEmpty( + [$"component='{component.Name}'", $"shell='{shell.Name}'"], + $"Cannot link component '{componentName}' to shell '{shell.Name}' because model '{modelPath}' could not be loaded.")); + return false; + } + + if (!model.IsModel) + { + ErrorReporter.Assert( + EngineAssert.FromNullOrEmpty( + [$"component='{component.Name}'", $"shell='{shell.Name}'"], + $"Cannot link component '{componentName}' to shell '{shell.Name}' because the loaded file '{modelPath}' is not a model.")); + return false; + } + + var boneIndex = model.IndexOfBone(componentName); + + if (boneIndex == -1) + return false; + component.Bone = boneIndex; + component.ParentShell = shell; + return true; + } + + private void SetDefaultFont() + { + // The code is only triggered iff at least one Text CommandbarBar component existed + if (Components.FirstOrDefault(x => x is CommandBarTextComponent or CommandBarTextButtonComponent) is null) + return; + + if (_defaultFont is null) + { + // TODO: From GameConstants + var fontName = PGConstants.DefaultUnicodeFontName; + var size = 11; + var font = fontManager.CreateFont(fontName, size, true, false, false, 1.0f); + if (font is null) + ErrorReporter.Assert(EngineAssert.FromNullOrEmpty([ToString()], $"Unable to create Default from name {fontName}")); + DefaultFont = font; + } + } + + private void SetMegaTexture() + { + // The code is only triggered iff at least one Shell CommandbarBar component existed + if (Components.FirstOrDefault(x => x is CommandBarShellComponent) is null) + return; + // Note: The tag is not used by the engine + var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{CommandBarConstants.MegaTextureBaseName}.mtd"); + using var megaTexture = GameRepository.TryOpenFile(mtdPath); + + try + { + MegaTextureFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); + } + catch (BinaryCorruptedException e) + { + var message = $"Failed to load MTD file '{mtdPath}': {e.Message}"; + Logger?.LogError(e, message); + ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, [], message)); + } + _megaTextureExists = GameRepository.TextureRepository.FileExists($"{CommandBarConstants.MegaTextureBaseName}.tga"); + } + + private void SetComponentGroup(IEnumerable components) + { + var groupData = components + .Where(x => !string.IsNullOrEmpty(x.XmlData.Group)) + .GroupBy(x => x.XmlData.Group!, StringComparer.Ordinal); + + foreach (var grouping in groupData) + { + var group = new CommandBarComponentGroup(grouping.Key, grouping); + _groups.Add(grouping.Key, group); + foreach (var component in grouping) + component.Group = group; + } + } + + private void VerifyFilePathLength(string filePath) + { + if (filePath.Length > PGConstants.MaxCommandBarDatabaseFileName) + { + ErrorReporter.Report(new InitializationError + { + GameManager = ToString(), + Message = $"CommandBar file '{filePath}' is longer than {PGConstants.MaxCommandBarDatabaseFileName} characters." + }); + } + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs index 799040e..f649d80 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs @@ -1,4 +1,7 @@ using PG.StarWarsGame.Engine.CommandBar.Xml; +using System.Numerics; +using PG.StarWarsGame.Engine.Rendering; +using PG.StarWarsGame.Engine.Utilities; namespace PG.StarWarsGame.Engine.CommandBar.Components; @@ -10,10 +13,33 @@ public class CommandBarShellComponent : CommandBarBaseComponent public string? ModelPath { get; } + public Matrix3x4 ModelTransform { get; internal set; } = Matrix3x4.Identity; + public CommandBarShellComponent(CommandBarComponentData xmlData) : base(xmlData) { ModelName = xmlData.ModelName; if (!string.IsNullOrEmpty(ModelName)) ModelPath = $"DATA\\ART\\MODELS\\{ModelName}"; } + + internal void SetOffsetAndScale(Vector3 offset, Vector3 scale) + { + var newOffset = new Vector3(0.0f, 0.0f, 0.0f); + var newScale = new Vector3(1.0f, 1.0f, 1.0f); + if (XmlData.ModelOffsetX) + newOffset.X = offset.X; + if (XmlData.ModelOffsetY) + newOffset.Y = offset.Y; + if (XmlData.ScaleModelX) + newScale.X = scale.X; + if (XmlData.ScaleModelY) + newScale.Y = scale.Y; + + newOffset.X = PGMath.Floor(newOffset.X) + 0.5f; + newOffset.Y = PGMath.Floor(newOffset.Y) + 0.5f; + + ModelTransform = Matrix3x4.Identity + * Matrix3x4.Scale(newScale) + * Matrix3x4.CreateTranslation(newOffset); + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Matrix3x4.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Matrix3x4.cs new file mode 100644 index 0000000..136f647 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Matrix3x4.cs @@ -0,0 +1,198 @@ +using System; +using System.Globalization; +using System.Numerics; + +namespace PG.StarWarsGame.Engine.Rendering; + +public struct Matrix3x4 : IEquatable +{ + public float M11; + public float M12; + public float M13; + public float M14; + public float M21; + public float M22; + public float M23; + public float M24; + public float M31; + public float M32; + public float M33; + public float M34; + + public static Matrix3x4 Identity { get; } = new( + 1f, 0.0f, 0.0f, 0.0f, + 0.0f, 1f, 0.0f, 0.0f, + 0.0f, 0.0f, 1f, 0.0f); + + /// Constructs a Matrix3x4 from the given components. + public Matrix3x4( + float m11, + float m12, + float m13, + float m14, + float m21, + float m22, + float m23, + float m24, + float m31, + float m32, + float m33, + float m34) + { + M11 = m11; + M12 = m12; + M13 = m13; + M14 = m14; + M21 = m21; + M22 = m22; + M23 = m23; + M24 = m24; + M31 = m31; + M32 = m32; + M33 = m33; + M34 = m34; + } + + public static Matrix3x4 Scale(Vector3 scale) + { + return Scale(scale.X, scale.Y, scale.Z); + } + + public static Matrix3x4 Scale(float xScale, float yScale, float zScale) + { + return new Matrix3x4 + { + M11 = xScale, M12 = 0, M13 = 0, M14 = 0, + M21 = 0, M22 = yScale, M23 = 0, M24 = 0, + M31 = 0, M32 = 0, M33 = zScale, M34 = 0 + }; + } + + public static Matrix3x4 CreateTranslation(Vector3 position) + { + return CreateTranslation(position.X, position.Y, position.Z); + } + + public static Matrix3x4 CreateTranslation(float xPosition, float yPosition, float zPosition) + { + return new Matrix3x4 + { + M11 = 1, M12 = 0, M13 = 0, M14 = xPosition, + M21 = 0, M22 = 1, M23 = 0, M24 = yPosition, + M31 = 0, M32 = 0, M33 = 1, M34 = zPosition + }; + } + + public static Matrix3x4 operator *(Matrix3x4 value1, Matrix3x4 value2) + { + Matrix3x4 matrix3x4; + matrix3x4.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21 + value1.M13 * value2.M31; + matrix3x4.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22 + value1.M13 * value2.M32; + matrix3x4.M13 = value1.M11 * value2.M13 + value1.M12 * value2.M23 + value1.M13 * value2.M33; + matrix3x4.M14 = value1.M11 * value2.M14 + value1.M12 * value2.M24 + value1.M13 * value2.M34 + value1.M14; + matrix3x4.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21 + value1.M23 * value2.M31; + matrix3x4.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22 + value1.M23 * value2.M32; + matrix3x4.M23 = value1.M21 * value2.M13 + value1.M22 * value2.M23 + value1.M23 * value2.M33; + matrix3x4.M24 = value1.M21 * value2.M14 + value1.M22 * value2.M24 + value1.M23 * value2.M34 + value1.M24; + matrix3x4.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value1.M33 * value2.M31; + matrix3x4.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value1.M33 * value2.M32; + matrix3x4.M33 = value1.M31 * value2.M13 + value1.M32 * value2.M23 + value1.M33 * value2.M33; + matrix3x4.M34 = value1.M31 * value2.M14 + value1.M32 * value2.M24 + value1.M33 * value2.M34 + value1.M34; + return matrix3x4; + } + + public static Matrix3x4 operator *(Matrix3x4 value1, float value2) + { + Matrix3x4 matrix3x4; + matrix3x4.M11 = value1.M11 * value2; + matrix3x4.M12 = value1.M12 * value2; + matrix3x4.M13 = value1.M13 * value2; + matrix3x4.M14 = value1.M14 * value2; + matrix3x4.M21 = value1.M21 * value2; + matrix3x4.M22 = value1.M22 * value2; + matrix3x4.M23 = value1.M23 * value2; + matrix3x4.M24 = value1.M24 * value2; + matrix3x4.M31 = value1.M31 * value2; + matrix3x4.M32 = value1.M32 * value2; + matrix3x4.M33 = value1.M33 * value2; + matrix3x4.M34 = value1.M34 * value2; + return matrix3x4; + } + + /// + /// Returns a boolean indicating whether the given two matrices are equal. + /// + /// The first matrix to compare. + /// The second matrix to compare. + /// True if the given matrices are equal; False otherwise. + public static bool operator ==(Matrix3x4 value1, Matrix3x4 value2) + { + return value1.M11 == (double)value2.M11 && value1.M22 == (double)value2.M22 && value1.M33 == (double)value2.M33 && + value1.M12 == (double)value2.M12 && value1.M13 == (double)value2.M13 && value1.M14 == (double)value2.M14 && + value1.M21 == (double)value2.M21 && value1.M23 == (double)value2.M23 && value1.M24 == (double)value2.M24 && + value1.M31 == (double)value2.M31 && value1.M32 == (double)value2.M32 && value1.M34 == (double)value2.M34; + } + + /// + /// Returns a boolean indicating whether the given two matrices are not equal. + /// + /// The first matrix to compare. + /// The second matrix to compare. + /// True if the given matrices are not equal; False if they are equal. + public static bool operator !=(Matrix3x4 value1, Matrix3x4 value2) + { + return value1.M11 != (double)value2.M11 || value1.M12 != (double)value2.M12 || value1.M13 != (double)value2.M13 || value1.M14 != (double)value2.M14 || + value1.M21 != (double)value2.M21 || value1.M22 != (double)value2.M22 || value1.M23 != (double)value2.M23 || value1.M24 != (double)value2.M24 || + value1.M31 != (double)value2.M31 || value1.M32 != (double)value2.M32 || value1.M33 != (double)value2.M33 || value1.M34 != (double)value2.M34; + } + + /// + /// Returns a boolean indicating whether this matrix instance is equal to the other given matrix. + /// + /// The matrix to compare this instance to. + /// True if the matrices are equal; False otherwise. + public bool Equals(Matrix3x4 other) + { + return M11 == (double)other.M11 && M22 == (double)other.M22 && M33 == (double)other.M33 && + M12 == (double)other.M12 && M13 == (double)other.M13 && M14 == (double)other.M14 && + M21 == (double)other.M21 && M23 == (double)other.M23 && M24 == (double)other.M24 && + M31 == (double)other.M31 && M32 == (double)other.M32 && M34 == (double)other.M34; + } + + /// + /// Returns a boolean indicating whether the given Object is equal to this matrix instance. + /// + /// The Object to compare against. + /// True if the Object is equal to this matrix; False otherwise. + public override bool Equals(object? obj) => obj is Matrix3x4 other && Equals(other); + + /// Returns a String representing this matrix instance. + /// The string representation. + public override string ToString() + { + var currentCulture = CultureInfo.CurrentCulture; + return string.Format(currentCulture, + "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}}", + M11.ToString(currentCulture), + M12.ToString(currentCulture), + M13.ToString(currentCulture), + M14.ToString(currentCulture), + M21.ToString(currentCulture), + M22.ToString(currentCulture), + M23.ToString(currentCulture), + M24.ToString(currentCulture), + M31.ToString(currentCulture), + M32.ToString(currentCulture), + M33.ToString(currentCulture), + M34.ToString(currentCulture)); + } + + /// Returns the hash code for this instance. + /// The hash code. + public override int GetHashCode() + { + return M11.GetHashCode() + M12.GetHashCode() + M13.GetHashCode() + M14.GetHashCode() + + M21.GetHashCode() + M22.GetHashCode() + M23.GetHashCode() + M24.GetHashCode() + + M31.GetHashCode() + M32.GetHashCode() + M33.GetHashCode() + M34.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/PGMath.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/PGMath.cs new file mode 100644 index 0000000..bacabc3 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/PGMath.cs @@ -0,0 +1,12 @@ +using System; + +namespace PG.StarWarsGame.Engine.Utilities; + +internal static class PGMath +{ +#if NETSTANDARD2_1_OR_GREATER || NET + public static float Floor(float value) => MathF.Floor(value); +#else + public static float Floor(float value) => (float)Math.Floor(value); +#endif +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs index 96e7c7e..4290b15 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs @@ -1,6 +1,8 @@ using System; -using System.Numerics; using System.Runtime.CompilerServices; +#if NET7_0_OR_GREATER +using System.Numerics; +#endif namespace PG.StarWarsGame.Files.XML.Utilities;