From 6aa6fca09942267dabec3b635afe7ebd7c084233 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Feb 2025 13:38:52 +0100 Subject: [PATCH 01/34] Check icons (#13) * update branch in sub * add mdt to solution * update branch in sub * add mdt to solution * prototype texture verifier * improve error reporting * finish new game init reporting * start GameManagers * update subs * update sub * move to gamemanager arch * first support for gui dialogs * remove valuelistdict to xml objects to avoid boxing * fix gameobject land terrain model mapping uses only the first ocurrance * update deps and module * minor changes * fix initialization * correct file not found handling * use spans for repositories * support custom xml parser action * reduce allocations in repository * update valuestringbuilder usages * fix some files are not found. * extract constants * implement parsing commandbar components (except colors) * move code * move code * move code * remove submodule * remove module * update all deps and make run again * read corrupt string throws BinaryCorruptedExcpetion * update to .net9 --- .github/workflows/release.yml | 8 +- .github/workflows/test.yml | 2 +- .gitmodules | 3 - Directory.Build.props | 2 +- ModVerify.sln | 21 -- PetroglyphTools | 1 - .../GameFinder/GameFinderService.cs | 28 +- .../ModSelectors/AutomaticModSelector.cs | 26 +- .../ModSelectors/ConsoleModSelector.cs | 4 +- .../ModSelectors/ManualModSelector.cs | 6 +- .../ModSelectors/ModSelectorBase.cs | 5 +- .../ModSelectors/SettingsBasedModSelector.cs | 30 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 26 +- src/ModVerify.CliApp/Program.cs | 30 +- .../Properties/launchSettings.json | 2 +- src/ModVerify.CliApp/SettingsBuilder.cs | 1 + src/ModVerify/ModVerify.csproj | 9 +- src/ModVerify/ModVerify.csproj.DotSettings | 2 + .../ConcurrentGameDatabaseErrorListener.cs | 26 ++ .../Reporting/IDatabaseErrorCollection.cs | 10 + .../Reporting/Reporters/ConsoleReporter.cs | 5 +- .../Reporting/Reporters/FileBasedReporter.cs | 1 + .../Reporters/JSON/JsonReporterSettings.cs | 4 +- .../Reporting/Reporters/ReporterBase.cs | 1 + .../Text/TextFileReporterSettings.cs | 4 +- .../VerificationReportersExtensions.cs | 1 + .../FileBasedReporterSettings.cs | 2 +- .../GlobalVerificationReportSettings.cs | 4 +- .../VerificationReportSettings.cs | 2 +- .../Reporting/VerificationReportBroker.cs | 1 + src/ModVerify/Settings/GameVerifySettings.cs | 2 +- src/ModVerify/Utilities/PathExtensions.cs | 22 ++ src/ModVerify/VerificationProvider.cs | 1 + src/ModVerify/Verifiers/AudioFilesVerifier.cs | 132 ++++--- ...ameDatabaseInitializationErrorCollector.cs | 29 ++ .../InitializationErrorReporter.cs | 16 + .../InitializationErrorReporterBase.cs | 48 +++ .../XmlParseErrorReporter.cs} | 55 +-- .../Verifiers/DuplicateNameFinder.cs | 14 +- src/ModVerify/Verifiers/GameVerifierBase.cs | 18 +- .../Verifiers/ReferencedModelsVerifier.cs | 40 +- .../ReferencedTexturesVerifier.GUI.cs | 143 ++++++++ .../Verifiers/ReferencedTexturesVerifier.cs | 32 ++ src/ModVerify/Verifiers/VerifierErrorCodes.cs | 2 + src/ModVerify/VerifyGamePipeline.cs | 40 +- .../Audio/Sfx/ISfxEventGameManager.cs | 5 + .../Audio/Sfx/SfxEvent.cs | 239 ++++++++++++ .../Audio/Sfx/SfxEventGameManager.cs | 79 ++++ .../CommandBar/CommandBarComponentType.cs | 15 + .../CommandBar/CommandBarGameManager.cs | 84 +++++ .../CommandBar/Xml/CommandBarComponentData.cs | 142 +++++++ .../DataTypes/GameConstants.cs | 6 - .../DataTypes/GameObject.cs | 65 ---- .../DataTypes/SfxEvent.cs | 260 ------------- .../DataTypes/XmlObject.cs | 47 --- .../ErrorReporting/DatabaseErrorListener.cs | 12 + .../DatabaseErrorListenerWrapper.cs | 61 +++ .../ErrorReporting/IDatabaseErrorListener.cs | 7 + .../ErrorReporting/InitializationError.cs | 8 + .../Database/ErrorReporting/XmlError.cs | 18 + .../Database/GameDatabase.cs | 22 +- .../Database/GameDatabaseService.cs | 47 +-- .../Database/GameInitializationOptions.cs | 14 + .../Database/GameInitializer.cs | 109 ++++++ .../Database/GameManagerBase.cs | 94 +++++ .../Database/IGameDatabase.cs | 17 +- .../Database/IGameDatabaseService.cs | 11 +- .../{IXmlDatabase.cs => IGameManager.cs} | 3 +- .../Initialization/CreateDatabaseStep.cs | 25 -- .../GameDatabaseCreationPipeline.cs | 147 -------- .../ParseXmlDatabaseFromContainerStep.cs | 69 ---- .../Initialization/ParseXmlDatabaseStep.cs | 42 --- .../Database/XmlDatabase.cs | 27 -- .../FocHardcodedConstants.cs | 2 +- .../GameConstants/GameConstants.cs | 17 + .../GameConstants/GameConstantsXml.cs | 3 + .../GameConstants/IGameConstants.cs | 3 + .../GameObjects/GameObject.cs | 84 +++++ .../GameObjectType.cs | 2 +- .../GameObjectTypeTypeGameManager.cs | 42 +++ .../GameObjects/IGameObjectTypeGameManager.cs | 5 + .../GuiDialog/ComponentTextureEntry.cs | 10 + .../GuiDialog/GuiComponentType.cs | 93 +++++ .../GuiDialog/GuiDialogGameManager.cs | 169 +++++++++ .../GuiDialogGameManager_Initialization.cs | 190 ++++++++++ .../GuiDialog/GuiTextureOrigin.cs | 7 + .../GuiDialog/IGuiDialogManager.cs | 26 ++ .../TypeBasedComponentTextureEntryComparer.cs | 22 ++ .../GuiDialog/Xml/GuiDialogsXml.cs | 9 + .../GuiDialog/Xml/GuiDialogsXmlTextureData.cs | 15 + .../GuiDialog/Xml/XmlComponentTextureData.cs | 14 + .../IO/Repositories/EffectsRepository.cs | 103 ++++++ .../IO/Repositories/FileFoundInfo.cs | 27 ++ .../Repositories/FocGameRepository.cs | 55 +-- .../IO/Repositories/GameRepository.Files.cs | 216 +++++++++++ .../{ => IO}/Repositories/GameRepository.cs | 170 +-------- .../Repositories/GameRepositoryFactory.cs | 9 +- .../{ => IO}/Repositories/IGameRepository.cs | 7 +- .../IO/Repositories/IGameRepositoryFactory.cs | 8 + .../IO/Repositories/IRepository.cs | 17 + .../IO/Repositories/MultiPassRepository.cs | 64 ++++ .../IO/Repositories/TextureRepository.cs | 69 ++++ .../Utilities/DirectoryInfoGlobbingWrapper.cs | 4 +- .../Utilities/FileInfoGlobbingWrapper.cs | 2 +- .../{ => IO}/Utilities/MatcherExtensions.cs | 4 +- .../IO/Utilities/PathExtensions.cs | 30 ++ .../EawGameLanguageManager.cs | 2 +- .../FocGameLanguageManager.cs | 2 +- .../GameLanguageManager.cs | 46 +-- .../GameLanguageManagerProvider.cs | 2 +- .../IGameLanguageManager.cs | 2 +- .../IGameLanguageManagerProvider.cs | 2 +- .../LanguageType.cs} | 2 +- .../PG.StarWarsGame.Engine.csproj | 16 +- .../PG.StarWarsGame.Engine.csproj.DotSettings | 7 + .../PG.StarWarsGame.Engine/PGConstants.cs | 13 +- .../PetroglyphEngineServiceContribution.cs | 11 +- .../Rendering/RgbaColor.cs | 63 ++++ .../Repositories/EffectsRepository.cs | 42 --- .../Repositories/IGameRepositoryFactory.cs | 9 - .../Repositories/IRepository.cs | 12 - .../Repositories/MultiPassRepository.cs | 46 --- .../Repositories/TextureRepository.cs | 64 ---- .../Utilities/ValueStringBuilder.cs | 347 +++++++++++++++++- .../Xml/IPetroglyphXmlFileParserFactory.cs | 2 +- .../Xml/NamedXmlObject.cs | 13 + .../Parsers/Data/CommandBarComponentParser.cs | 342 +++++++++++++++++ .../Xml/Parsers/Data/GameConstantsParser.cs | 12 +- .../Xml/Parsers/Data/GameObjectParser.cs | 105 ++++-- .../Xml/Parsers/Data/SfxEventParser.cs | 218 +++++++---- .../File/CommandBarComponentFileParser.cs | 38 ++ .../Parsers/File/GameObjectFileFileParser.cs | 8 +- .../Xml/Parsers/File/GuiDialogParser.cs | 65 ++++ .../Xml/Parsers/File/SfxEventFileParser.cs | 19 +- .../Xml/Parsers/IXmlContainerContentParser.cs | 20 + .../Xml/Parsers/XmlContainerContentParser.cs | 109 ++++++ .../XmlContainerParserErrorEventArgs.cs | 40 ++ .../Xml/Parsers/XmlObjectParser.cs | 82 +++-- .../Xml/PetroglyphXmlParserFactory.cs | 16 +- .../Xml/Tags/CommandBarComponentTags.cs | 108 ++++++ .../Xml/Tags/ComponentTextureKeyExtensions.cs | 114 ++++++ .../PG.StarWarsGame.Engine/Xml/XmlObject.cs | 12 + .../Identifier/AloContentInfoIdentifier.cs | 2 +- .../Binary/Reader/ModelFileReader.cs | 2 +- .../Binary/Reader/ParticleReaderV1.cs | 2 +- .../Files/AloFileInformation.cs | 1 - .../Files/IAloFile.cs | 3 +- .../Files/Models/AloModelFile.cs | 1 - .../Files/Particles/AloParticleFile.cs | 1 - .../PG.StarWarsGame.Files.ALO.csproj | 3 + .../Services/AloFileService.cs | 22 +- .../Services/IAloFileService.cs | 1 - .../Binary/Reader/ChunkFileReaderBase.cs | 4 +- .../Binary/Reader/ChunkReader.cs | 51 +-- .../Files/IChunkFile.cs | 3 +- .../PG.StarWarsGame.Files.ChunkFiles.csproj | 5 +- .../ErrorHandling/XmlParseErrorEventArgs.cs | 23 +- .../ErrorHandling/XmlParseErrorKind.cs | 8 + .../PG.StarWarsGame.Files.XML.csproj | 7 +- .../Parsers/IPetroglyphXmlFileParser.cs | 3 +- .../Parsers/PetroglyphXmlFileParser.cs | 37 +- .../Parsers/PetroglyphXmlParser.cs | 29 +- .../Primitives/IPrimitiveParserProvider.cs | 2 + .../Primitives/PetroglyphXmlByteParser.cs | 5 +- .../Primitives/PetroglyphXmlFloatParser.cs | 28 +- .../Primitives/PetroglyphXmlIntegerParser.cs | 44 ++- .../PetroglyphXmlLooseStringListParser.cs | 4 +- .../PetroglyphXmlMax100ByteParser.cs | 28 +- .../PetroglyphXmlUnsignedIntegerParser.cs | 6 +- .../Primitives/PetroglyphXmlVector2FParser.cs | 34 ++ .../Primitives/PrimitiveParserProvider.cs | 5 + .../Primitives/XmlFileContainerParser.cs | 5 +- .../ValueListDictionary.cs | 312 ---------------- 173 files changed, 4697 insertions(+), 2029 deletions(-) delete mode 100644 .gitmodules delete mode 160000 PetroglyphTools create mode 100644 src/ModVerify/ModVerify.csproj.DotSettings create mode 100644 src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs create mode 100644 src/ModVerify/Reporting/IDatabaseErrorCollection.cs rename src/ModVerify/Reporting/{ => Settings}/FileBasedReporterSettings.cs (88%) rename src/ModVerify/Reporting/{ => Settings}/GlobalVerificationReportSettings.cs (82%) rename src/ModVerify/Reporting/{ => Settings}/VerificationReportSettings.cs (76%) create mode 100644 src/ModVerify/Utilities/PathExtensions.cs create mode 100644 src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs create mode 100644 src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs create mode 100644 src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs rename src/ModVerify/Verifiers/{XmlParseErrorCollector.cs => DatabaseError/XmlParseErrorReporter.cs} (54%) create mode 100644 src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs create mode 100644 src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameConstants.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObject.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/SfxEvent.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/XmlObject.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Database/{IXmlDatabase.cs => IGameManager.cs} (75%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/CreateDatabaseStep.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/GameDatabaseCreationPipeline.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseFromContainerStep.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseStep.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/XmlDatabase.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/IGameConstants.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{DataTypes => GameObjects}/GameObjectType.cs (95%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/ComponentTextureEntry.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiComponentType.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiTextureOrigin.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/IGuiDialogManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/TypeBasedComponentTextureEntryComparer.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Repositories/FocGameRepository.cs (56%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Repositories/GameRepository.cs (62%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Repositories/GameRepositoryFactory.cs (52%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Repositories/IGameRepository.cs (70%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Utilities/DirectoryInfoGlobbingWrapper.cs (95%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Utilities/FileInfoGlobbingWrapper.cs (95%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{ => IO}/Utilities/MatcherExtensions.cs (97%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language => Localization}/EawGameLanguageManager.cs (92%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language => Localization}/FocGameLanguageManager.cs (94%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language => Localization}/GameLanguageManager.cs (85%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language => Localization}/GameLanguageManagerProvider.cs (93%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language => Localization}/IGameLanguageManager.cs (92%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language => Localization}/IGameLanguageManagerProvider.cs (70%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Language/SupportedLanguage.cs => Localization/LanguageType.cs} (80%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/EffectsRepository.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepositoryFactory.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IRepository.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/MultiPassRepository.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/TextureRepository.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GuiDialogParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IXmlContainerContentParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/CommandBarComponentTags.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/ComponentTextureKeyExtensions.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObject.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ValueListDictionary.cs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 31c58a8..a0cd9f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: - name: Create NetFramework Release run: dotnet publish .\src\ModVerify.CliApp\ModVerify.CliApp.csproj --configuration Release -f net48 --output ./releases/net48 - name: Create Net Core Release - run: dotnet publish .\src\ModVerify.CliApp\ModVerify.CliApp.csproj --configuration Release -f net8.0 --output ./releases/net8.0 + run: dotnet publish .\src\ModVerify.CliApp\ModVerify.CliApp.csproj --configuration Release -f net9.0 --output ./releases/net9.0 - name: Upload a Build Artifact uses: actions/upload-artifact@v4 with: @@ -53,8 +53,8 @@ jobs: path: ./releases - name: Create NET Core .zip # Change into the artifacts directory to avoid including the directory itself in the zip archive - working-directory: ./releases/net8.0 - run: zip -r ../ModVerify-Net8.zip . + working-directory: ./releases/net9.0 + run: zip -r ../ModVerify-Net9.zip . - uses: dotnet/nbgv@v0.4.2 id: nbgv - name: Create GitHub release @@ -66,4 +66,4 @@ jobs: generate_release_notes: true files: | ./releases/net48/ModVerify.exe - ./releases/ModVerify-Net8.zip \ No newline at end of file + ./releases/ModVerify-Net9.zip \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 20a36cc..f6c4d43 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,6 +25,6 @@ jobs: submodules: recursive - uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Build & Test in Release Mode run: dotnet test --configuration Release \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 25d6b29..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "PetroglyphTools"] - path = PetroglyphTools - url = https://github.com/AlamoEngine-Tools/PetroglyphTools diff --git a/Directory.Build.props b/Directory.Build.props index 1d41c28..26f6094 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ all - 3.6.139 + 3.6.143 \ No newline at end of file diff --git a/ModVerify.sln b/ModVerify.sln index baee725..d09a64e 100644 --- a/ModVerify.sln +++ b/ModVerify.sln @@ -5,12 +5,6 @@ VisualStudioVersion = 17.11.34909.67 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PetroglyphTools", "PetroglyphTools", "{15F8B753-814A-406E-9147-EB048DADAC96}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PG.Commons", "PetroglyphTools\PG.Commons\PG.Commons\PG.Commons.csproj", "{1A9E1B15-DD77-47E3-893E-AFADF982CEC6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PG.StarWarsGame.Files.DAT", "PetroglyphTools\PG.StarWarsGame.Files.DAT\PG.StarWarsGame.Files.DAT\PG.StarWarsGame.Files.DAT.csproj", "{4630F85C-D1C4-4454-9126-BE13F6901E0B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PG.StarWarsGame.Files.MEG", "PetroglyphTools\PG.StarWarsGame.Files.MEG\PG.StarWarsGame.Files.MEG\PG.StarWarsGame.Files.MEG.csproj", "{885291F4-E5E8-45B2-B0B4-40B2910228A3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModVerify", "src\ModVerify\ModVerify.csproj", "{22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModVerify.CliApp", "src\ModVerify.CliApp\ModVerify.CliApp.csproj", "{84479931-A329-4113-9BE5-90B71E5486E6}" @@ -29,18 +23,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1A9E1B15-DD77-47E3-893E-AFADF982CEC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A9E1B15-DD77-47E3-893E-AFADF982CEC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A9E1B15-DD77-47E3-893E-AFADF982CEC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A9E1B15-DD77-47E3-893E-AFADF982CEC6}.Release|Any CPU.Build.0 = Release|Any CPU - {4630F85C-D1C4-4454-9126-BE13F6901E0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4630F85C-D1C4-4454-9126-BE13F6901E0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4630F85C-D1C4-4454-9126-BE13F6901E0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4630F85C-D1C4-4454-9126-BE13F6901E0B}.Release|Any CPU.Build.0 = Release|Any CPU - {885291F4-E5E8-45B2-B0B4-40B2910228A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {885291F4-E5E8-45B2-B0B4-40B2910228A3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {885291F4-E5E8-45B2-B0B4-40B2910228A3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {885291F4-E5E8-45B2-B0B4-40B2910228A3}.Release|Any CPU.Build.0 = Release|Any CPU {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Debug|Any CPU.Build.0 = Debug|Any CPU {22ED0E2C-FF3B-40EB-9CE2-DCDE65CDF31B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -70,9 +52,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {1A9E1B15-DD77-47E3-893E-AFADF982CEC6} = {15F8B753-814A-406E-9147-EB048DADAC96} - {4630F85C-D1C4-4454-9126-BE13F6901E0B} = {15F8B753-814A-406E-9147-EB048DADAC96} - {885291F4-E5E8-45B2-B0B4-40B2910228A3} = {15F8B753-814A-406E-9147-EB048DADAC96} {92F2A0C8-61B6-424B-99D5-7898CDBA7CA6} = {15F8B753-814A-406E-9147-EB048DADAC96} {DF76A383-C94E-4D03-A07C-22D61ED37059} = {15F8B753-814A-406E-9147-EB048DADAC96} {418C68FA-531B-432E-8459-6433181C8AD3} = {15F8B753-814A-406E-9147-EB048DADAC96} diff --git a/PetroglyphTools b/PetroglyphTools deleted file mode 160000 index 0347671..0000000 --- a/PetroglyphTools +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0347671ec7c89d2a79b167e2110df3cd495f71aa diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index fa21e3b..2e5f5f4 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -8,7 +8,6 @@ using PG.StarWarsGame.Infrastructure.Games; using PG.StarWarsGame.Infrastructure.Mods; using PG.StarWarsGame.Infrastructure.Services; -using PG.StarWarsGame.Infrastructure.Services.Dependencies; using PG.StarWarsGame.Infrastructure.Services.Detection; namespace AET.ModVerifyTool.GameFinder; @@ -69,17 +68,20 @@ public GameFinderResult FindGamesFromPathOrGlobal(string path) private bool TryDetectGame(GameType gameType, IList detectors, out GameDetectionResult result) { var gd = new CompositeGameDetector(detectors, _serviceProvider); - result = gd.Detect(new GameDetectorOptions(gameType)); - if (result.Error is not null) + try { - _logger?.LogTrace($"Unable to find game installation: {result.Error.Message}", result.Error); - return false; + result = gd.Detect(gameType); + if (result.GameLocation is null) + return false; + return true; } - if (result.GameLocation is null) + catch (Exception e) + { + result = GameDetectionResult.NotInstalled(gameType); + _logger?.LogTrace($"Unable to find game installation: {e.Message}"); return false; - - return true; + } } private GameFinderResult FindGames(IList detectors) @@ -128,15 +130,15 @@ private GameFinderResult FindGames(IList detectors) private void SetupMods(IGame game) { - var modFinder = _serviceProvider.GetRequiredService(); + var modFinder = _serviceProvider.GetRequiredService(); var modRefs = modFinder.FindMods(game); var mods = new List(); foreach (var modReference in modRefs) { - var mod = _modFactory.FromReference(game, modReference, CultureInfo.InvariantCulture); - mods.AddRange(mod); + var mod = _modFactory.CreatePhysicalMod(game, modReference, CultureInfo.InvariantCulture); + mods.Add(mod); } foreach (var mod in mods) @@ -145,9 +147,7 @@ private void SetupMods(IGame game) // Mods need to be added to the game first, before resolving their dependencies. foreach (var mod in mods) { - var resolver = _serviceProvider.GetRequiredService(); - mod.ResolveDependencies(resolver, - new DependencyResolverOptions { CheckForCycle = true, ResolveCompleteChain = true }); + mod.ResolveDependencies(); } } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs index 1d2a85f..00fc4ae 100644 --- a/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/AutomaticModSelector.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Globalization; using System.IO.Abstractions; +using System.Linq; using AET.ModVerifyTool.GameFinder; using AET.ModVerifyTool.Options; -using EawModinfo.Model; -using EawModinfo.Spec; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; @@ -13,7 +11,7 @@ using PG.StarWarsGame.Infrastructure.Games; using PG.StarWarsGame.Infrastructure.Mods; using PG.StarWarsGame.Infrastructure.Services; -using PG.StarWarsGame.Infrastructure.Services.Dependencies; +using PG.StarWarsGame.Infrastructure.Services.Detection; namespace AET.ModVerifyTool.ModSelectors; @@ -21,10 +19,14 @@ internal class AutomaticModSelector(IServiceProvider serviceProvider) : ModSelec { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - public override GameLocations? Select(GameInstallationsSettings settings, out IPhysicalPlayableObject? targetObject, out GameEngineType? actualEngineType) + public override GameLocations? Select( + GameInstallationsSettings settings, + out IPhysicalPlayableObject? targetObject, + out GameEngineType? actualEngineType) { var pathToVerify = settings.AutoPath; - Debug.Assert(pathToVerify is not null); + if (pathToVerify is null) + throw new InvalidOperationException("path to verify cannot be null."); actualEngineType = settings.EngineType; @@ -100,14 +102,18 @@ private GameLocations GetDetachedModLocations(string modPath, GameFinderResult g if (game is null) throw new GameNotFoundException($"Unable to find game of type '{settings.EngineType}'"); + var modFinder = ServiceProvider.GetRequiredService(); + var modRef = modFinder.FindMods(game, _fileSystem.DirectoryInfo.New(modPath)).FirstOrDefault(); + + if (modRef is null) + throw new NotSupportedException($"The mod at '{modPath}' is not compatible to the found game '{game}'."); + var modFactory = ServiceProvider.GetRequiredService(); - mod = modFactory.FromReference(game, new ModReference(modPath, ModType.Default), true, CultureInfo.InvariantCulture); + mod = modFactory.CreatePhysicalMod(game, modRef, CultureInfo.InvariantCulture); game.AddMod(mod); - var resolver = ServiceProvider.GetRequiredService(); - mod.ResolveDependencies(resolver, - new DependencyResolverOptions { CheckForCycle = true, ResolveCompleteChain = true }); + mod.ResolveDependencies(); return GetLocations(mod, gameResult, settings.AdditionalFallbackPaths); } diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index c218305..953b0fe 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; +using AET.Modinfo.Spec; using AET.ModVerifyTool.GameFinder; using AET.ModVerifyTool.Options; -using EawModinfo.Spec; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; using PG.StarWarsGame.Infrastructure.Mods; @@ -11,7 +11,7 @@ namespace AET.ModVerifyTool.ModSelectors; internal class ConsoleModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) { - public override GameLocations? Select(GameInstallationsSettings settings, out IPhysicalPlayableObject? targetObject, + public override GameLocations Select(GameInstallationsSettings settings, out IPhysicalPlayableObject targetObject, out GameEngineType? actualEngineType) { var gameResult = GameFinderService.FindGames(); diff --git a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs index 08f6537..d5913c6 100644 --- a/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ManualModSelector.cs @@ -7,7 +7,9 @@ namespace AET.ModVerifyTool.ModSelectors; internal class ManualModSelector(IServiceProvider serviceProvider) : ModSelectorBase(serviceProvider) { - public override GameLocations Select(GameInstallationsSettings settings, out IPhysicalPlayableObject? targetObject, + public override GameLocations Select( + GameInstallationsSettings settings, + out IPhysicalPlayableObject? targetObject, out GameEngineType? actualEngineType) { actualEngineType = settings.EngineType; @@ -21,7 +23,7 @@ public override GameLocations Select(GameInstallationsSettings settings, out IPh return new GameLocations( settings.ModPaths, - settings.GamePath, + settings.GamePath!, GetFallbackPaths(settings.FallbackGamePath, settings.AdditionalFallbackPaths)); } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs index 861451a..0eec285 100644 --- a/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs +++ b/src/ModVerify.CliApp/ModSelectors/ModSelectorBase.cs @@ -25,7 +25,9 @@ protected ModSelectorBase(IServiceProvider serviceProvider) GameFinderService = new GameFinderService(serviceProvider); } - public abstract GameLocations? Select(GameInstallationsSettings settings, out IPhysicalPlayableObject? targetObject, + public abstract GameLocations? Select( + GameInstallationsSettings settings, + out IPhysicalPlayableObject? targetObject, out GameEngineType? actualEngineType); protected GameLocations GetLocations(IPhysicalPlayableObject playableObject, GameFinderResult finderResult, IList additionalFallbackPaths) @@ -66,7 +68,6 @@ private IList GetModPaths(IPhysicalPlayableObject modOrGame) var traverser = ServiceProvider.GetRequiredService(); return traverser.Traverse(mod) - .Select(x => x.Mod) .OfType().Select(x => x.Directory.FullName) .ToList(); } diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index 356e6c2..c620aae 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -1,21 +1,13 @@ using System; -using System.Globalization; -using System.IO.Abstractions; using System.Linq; using AET.ModVerifyTool.Options; -using EawModinfo.Model; -using EawModinfo.Spec; -using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; -using PG.StarWarsGame.Infrastructure.Games; -using PG.StarWarsGame.Infrastructure.Services.Name; namespace AET.ModVerifyTool.ModSelectors; internal class SettingsBasedModSelector(IServiceProvider serviceProvider) { - private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); public VerifyGameInstallationData CreateInstallationDataFromSettings(GameInstallationsSettings settings) { var gameLocations = new ModSelectorFactory(serviceProvider).CreateSelector(settings) @@ -35,30 +27,12 @@ public VerifyGameInstallationData CreateInstallationDataFromSettings(GameInstall }; } - private string GetNameFromGameLocations(IPlayableObject? targetObject, GameLocations gameLocations, GameEngineType engineType) + private static string GetNameFromGameLocations(IPlayableObject? targetObject, GameLocations gameLocations, GameEngineType engineType) { if (targetObject is not null) return targetObject.Name; var mod = gameLocations.ModPaths.FirstOrDefault(); - - var name = mod is not null ? GetNameFromMod(mod) : GetNameFromGame(engineType); - - if (string.IsNullOrEmpty(name)) - throw new InvalidOperationException("Mod or game name cannot be null or empty."); - - return name; - } - - private string? GetNameFromGame(GameEngineType type) - { - var nameResolver = serviceProvider.GetRequiredService(); - return nameResolver.ResolveName(new GameIdentity(type.FromEngineType(), GamePlatform.Undefined), CultureInfo.InvariantCulture); - } - - private string? GetNameFromMod(string mod) - { - var nameResolver = serviceProvider.GetRequiredService(); - return nameResolver.ResolveName(new ModReference(_fileSystem.Path.GetFullPath(mod), ModType.Default), CultureInfo.InvariantCulture); + return mod ?? gameLocations.GamePath; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 1fbe04f..82ddda9 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -2,7 +2,7 @@ false - net8.0;net48 + net9.0;net48 Exe AET.ModVerifyTool ModVerify @@ -20,23 +20,24 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -50,7 +51,6 @@ - @@ -58,6 +58,10 @@ + + + + diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index ea5fff9..a8da9eb 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.IO.Abstractions; -using System.Runtime.CompilerServices; using System.Threading.Tasks; using AET.ModVerify; using AET.ModVerify.Reporting.Reporters; @@ -16,19 +14,19 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; -using PG.Commons.Extensibility; +using PG.Commons; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Files.ALO; -using PG.StarWarsGame.Files.DAT.Services.Builder; -using PG.StarWarsGame.Files.MEG.Data.Archives; +using PG.StarWarsGame.Files.MEG; +using PG.StarWarsGame.Files.MTD; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Infrastructure; -using PG.StarWarsGame.Infrastructure.Clients; using PG.StarWarsGame.Infrastructure.Services.Detection; using PG.StarWarsGame.Infrastructure.Services.Name; using Serilog; using Serilog.Events; using Serilog.Filters; +using Testably.Abstractions; namespace AET.ModVerifyTool; @@ -90,7 +88,7 @@ await parseResult.WithNotParsedAsync(e => private static IServiceCollection CreateCoreServices(bool verboseLogging) { - var fileSystem = new FileSystem(); + var fileSystem = new RealFileSystem(); var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton(new WindowsRegistry()); @@ -107,13 +105,12 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl serviceCollection.AddSingleton(sp => new HashingService(sp)); SteamAbstractionLayer.InitializeServices(serviceCollection); - PetroglyphGameClients.InitializeServices(serviceCollection); PetroglyphGameInfrastructure.InitializeServices(serviceCollection); - RuntimeHelpers.RunClassConstructor(typeof(IDatBuilder).TypeHandle); - RuntimeHelpers.RunClassConstructor(typeof(IMegArchive).TypeHandle); + serviceCollection.SupportMTD(); + serviceCollection.SupportMEG(); AloServiceContribution.ContributeServices(serviceCollection); - serviceCollection.CollectPgServiceContributions(); + PetroglyphCommons.ContributeServices(serviceCollection); XmlServiceContribution.ContributeServices(serviceCollection); PetroglyphEngineServiceContribution.ContributeServices(serviceCollection); @@ -122,15 +119,8 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl SetupReporting(serviceCollection, settings); - serviceCollection.AddSingleton(sp => new CompositeModNameResolver(sp, s => - new List - { - new OfflineWorkshopNameResolver(s), - new OnlineWorkshopNameResolver(s), - new DirectoryModNameResolver(s) - })); - - serviceCollection.AddSingleton(sp => new OfflineModGameTypeResolver(sp)); + serviceCollection.AddSingleton(sp => new OnlineModNameResolver(sp)); + serviceCollection.AddSingleton(sp => new OnlineModGameTypeResolver(sp)); return serviceCollection.BuildServiceProvider(); } diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index 51b4cdc..d84156a 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive": { "commandName": "Project", - "commandLineArgs": "-o verifyResults --minFailSeverity Information --baseline focBaseline.json" + "commandLineArgs": "-o verifyResults --minFailSeverity Information --baseline c:/test/focBaseline.json" }, "FromModPath": { diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index eeba013..27c7857 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -3,6 +3,7 @@ using System.IO; using System.IO.Abstractions; using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; using AET.ModVerifyTool.Options; using Microsoft.Extensions.DependencyInjection; diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 4a77b42..0649885 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -22,7 +22,8 @@ - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -31,7 +32,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -39,4 +40,8 @@ + + + + diff --git a/src/ModVerify/ModVerify.csproj.DotSettings b/src/ModVerify/ModVerify.csproj.DotSettings new file mode 100644 index 0000000..8af804c --- /dev/null +++ b/src/ModVerify/ModVerify.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs b/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs new file mode 100644 index 0000000..95461ad --- /dev/null +++ b/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs @@ -0,0 +1,26 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using PG.StarWarsGame.Engine.Database.ErrorReporting; + +namespace AET.ModVerify.Reporting; + +internal class ConcurrentGameDatabaseErrorListener : DatabaseErrorListener, IDatabaseErrorCollection +{ + private readonly ConcurrentBag _xmlErrors = new(); + + private readonly ConcurrentBag _initializationErrors = new(); + + public IEnumerable XmlErrors => _xmlErrors.ToList(); + public IEnumerable InitializationErrors => _initializationErrors.ToList(); + + public override void OnXmlError(XmlError error) + { + _xmlErrors.Add(error); + } + + public override void OnInitializationError(InitializationError error) + { + _initializationErrors.Add(error); + } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs new file mode 100644 index 0000000..90c8203 --- /dev/null +++ b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using PG.StarWarsGame.Engine.Database.ErrorReporting; + +namespace AET.ModVerify.Reporting; + +internal interface IDatabaseErrorCollection +{ + IEnumerable XmlErrors { get; } + IEnumerable InitializationErrors { get; } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs index bda277f..2e1cb6a 100644 --- a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs +++ b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AET.ModVerify.Reporting.Settings; namespace AET.ModVerify.Reporting.Reporters; @@ -9,7 +10,7 @@ internal class ConsoleReporter(VerificationReportSettings settings, IServiceProv { public override Task ReportAsync(IReadOnlyCollection errors) { - var filteredErrors = FilteredErrors(errors).ToList(); + var filteredErrors = FilteredErrors(errors).OrderByDescending(x => x.Severity).ToList(); Console.WriteLine(); Console.WriteLine("GAME VERIFICATION RESULT"); @@ -20,7 +21,7 @@ public override Task ReportAsync(IReadOnlyCollection errors) Console.WriteLine("No errors!"); foreach (var error in filteredErrors) - Console.WriteLine(error); + Console.WriteLine($"[{error.Severity}] [{error.Id}] Message={error.Message}"); Console.WriteLine(); diff --git a/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs b/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs index d0213d0..b66f29a 100644 --- a/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs +++ b/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.IO.Abstractions; +using AET.ModVerify.Reporting.Settings; using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Reporting.Reporters; diff --git a/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs b/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs index fd5962d..4207b36 100644 --- a/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs @@ -1,3 +1,5 @@ -namespace AET.ModVerify.Reporting.Reporters.JSON; +using AET.ModVerify.Reporting.Settings; + +namespace AET.ModVerify.Reporting.Reporters.JSON; public record JsonReporterSettings : FileBasedReporterSettings; \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ReporterBase.cs b/src/ModVerify/Reporting/Reporters/ReporterBase.cs index 9360c0e..ff71507 100644 --- a/src/ModVerify/Reporting/Reporters/ReporterBase.cs +++ b/src/ModVerify/Reporting/Reporters/ReporterBase.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AET.ModVerify.Reporting.Settings; namespace AET.ModVerify.Reporting.Reporters; diff --git a/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs b/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs index e6847a3..8fb833b 100644 --- a/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs @@ -1,4 +1,6 @@ -namespace AET.ModVerify.Reporting.Reporters.Text; +using AET.ModVerify.Reporting.Settings; + +namespace AET.ModVerify.Reporting.Reporters.Text; public record TextFileReporterSettings : FileBasedReporterSettings { diff --git a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs index 04f7c7d..41c2938 100644 --- a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs +++ b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs @@ -1,5 +1,6 @@ using AET.ModVerify.Reporting.Reporters.JSON; using AET.ModVerify.Reporting.Reporters.Text; +using AET.ModVerify.Reporting.Settings; using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Reporting.Reporters; diff --git a/src/ModVerify/Reporting/FileBasedReporterSettings.cs b/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs similarity index 88% rename from src/ModVerify/Reporting/FileBasedReporterSettings.cs rename to src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs index 6616258..fef047c 100644 --- a/src/ModVerify/Reporting/FileBasedReporterSettings.cs +++ b/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs @@ -1,6 +1,6 @@ using System; -namespace AET.ModVerify.Reporting; +namespace AET.ModVerify.Reporting.Settings; public record FileBasedReporterSettings : VerificationReportSettings { diff --git a/src/ModVerify/Reporting/GlobalVerificationReportSettings.cs b/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs similarity index 82% rename from src/ModVerify/Reporting/GlobalVerificationReportSettings.cs rename to src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs index 3982d4d..fe19d68 100644 --- a/src/ModVerify/Reporting/GlobalVerificationReportSettings.cs +++ b/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs @@ -1,6 +1,4 @@ -using System; - -namespace AET.ModVerify.Reporting; +namespace AET.ModVerify.Reporting.Settings; public record GlobalVerificationReportSettings : VerificationReportSettings { diff --git a/src/ModVerify/Reporting/VerificationReportSettings.cs b/src/ModVerify/Reporting/Settings/VerificationReportSettings.cs similarity index 76% rename from src/ModVerify/Reporting/VerificationReportSettings.cs rename to src/ModVerify/Reporting/Settings/VerificationReportSettings.cs index fc02024..6be2905 100644 --- a/src/ModVerify/Reporting/VerificationReportSettings.cs +++ b/src/ModVerify/Reporting/Settings/VerificationReportSettings.cs @@ -1,4 +1,4 @@ -namespace AET.ModVerify.Reporting; +namespace AET.ModVerify.Reporting.Settings; public record VerificationReportSettings { diff --git a/src/ModVerify/Reporting/VerificationReportBroker.cs b/src/ModVerify/Reporting/VerificationReportBroker.cs index f9c3c68..ea4d127 100644 --- a/src/ModVerify/Reporting/VerificationReportBroker.cs +++ b/src/ModVerify/Reporting/VerificationReportBroker.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Verifiers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/src/ModVerify/Settings/GameVerifySettings.cs b/src/ModVerify/Settings/GameVerifySettings.cs index 488bfe4..5e67f46 100644 --- a/src/ModVerify/Settings/GameVerifySettings.cs +++ b/src/ModVerify/Settings/GameVerifySettings.cs @@ -1,4 +1,4 @@ -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; namespace AET.ModVerify.Settings; diff --git a/src/ModVerify/Utilities/PathExtensions.cs b/src/ModVerify/Utilities/PathExtensions.cs new file mode 100644 index 0000000..6ddc47a --- /dev/null +++ b/src/ModVerify/Utilities/PathExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.IO.Abstractions; +using AnakinRaW.CommonUtilities.FileSystem; + +namespace AET.ModVerify.Utilities; + +public static class PathExtensions +{ + public static ReadOnlySpan GetGameStrippedPath(this IPath path, ReadOnlySpan gamePath, ReadOnlySpan modPath) + { + if (!path.IsPathFullyQualified(modPath)) + return modPath; + + if (modPath.Length <= gamePath.Length) + return modPath; + + if (path.IsChildOf(gamePath, modPath)) + return modPath.Slice(gamePath.Length); + + return modPath; + } +} \ No newline at end of file diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify/VerificationProvider.cs index a1800dc..ca0f42d 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify/VerificationProvider.cs @@ -13,5 +13,6 @@ public IEnumerable GetAllDefaultVerifiers(IGameDatabase databa yield return new ReferencedModelsVerifier(database, settings, serviceProvider); yield return new DuplicateNameFinder(database, settings, serviceProvider); yield return new AudioFilesVerifier(database, settings, serviceProvider); + yield return new ReferencedTexturesVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/AudioFilesVerifier.cs b/src/ModVerify/Verifiers/AudioFilesVerifier.cs index 1f4a3e9..e5ba70b 100644 --- a/src/ModVerify/Verifiers/AudioFilesVerifier.cs +++ b/src/ModVerify/Verifiers/AudioFilesVerifier.cs @@ -1,18 +1,22 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.IO.Abstractions; using System.Linq; +using System.Text; using System.Threading; using AET.ModVerify.Reporting; using AET.ModVerify.Settings; +using AnakinRaW.CommonUtilities.FileSystem.Normalization; using Microsoft.Extensions.DependencyInjection; using PG.Commons.Hashing; using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Engine.Language; +using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Files.MEG.Services.Builder.Normalization; + #if NETSTANDARD2_0 using AnakinRaW.CommonUtilities.FileSystem; #endif @@ -21,14 +25,21 @@ namespace AET.ModVerify.Verifiers; public class AudioFilesVerifier : GameVerifierBase { - private readonly PetroglyphDataEntryPathNormalizer _pathNormalizer; + private static readonly PathNormalizeOptions SampleNormalizerOptions = new() + { + UnifyCase = UnifyCasingKind.UpperCaseForce, + UnifySeparatorKind = DirectorySeparatorKind.Windows, + UnifyDirectorySeparators = true + }; + + private readonly EmpireAtWarMegDataEntryPathNormalizer _pathNormalizer = EmpireAtWarMegDataEntryPathNormalizer.Instance; private readonly ICrc32HashingService _hashingService; private readonly IFileSystem _fileSystem; private readonly IGameLanguageManager _languageManager; - public AudioFilesVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) : base(gameDatabase, settings, serviceProvider) + public AudioFilesVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) + : base(gameDatabase, settings, serviceProvider) { - _pathNormalizer = new(serviceProvider); _hashingService = serviceProvider.GetRequiredService(); _fileSystem = serviceProvider.GetRequiredService(); _languageManager = serviceProvider.GetRequiredService() @@ -41,67 +52,86 @@ protected override void RunVerification(CancellationToken token) { var visitedSamples = new HashSet(); var languagesToVerify = GetLanguagesToVerify().ToList(); - foreach (var sfxEvent in Database.SfxEvents.Entries) + foreach (var sfxEvent in Database.SfxGameManager.Entries) { foreach (var codedSample in sfxEvent.AllSamples) { - VerifySample(codedSample, sfxEvent, languagesToVerify, visitedSamples); + VerifySample(codedSample.AsSpan(), sfxEvent, languagesToVerify, visitedSamples); } } } - private void VerifySample(string sample, SfxEvent sfxEvent, IEnumerable languagesToVerify, HashSet visitedSamples) + private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent, IEnumerable languagesToVerify, HashSet visitedSamples) { - Span sampleNameBuffer = stackalloc char[PGConstants.MaxPathLength]; + char[]? pooledBuffer = null; - var i = _pathNormalizer.Normalize(sample.AsSpan(), sampleNameBuffer); - var normalizedSampleName = sampleNameBuffer.Slice(0, i); - var crc = _hashingService.GetCrc32(normalizedSampleName, PGConstants.PGCrc32Encoding); - if (!visitedSamples.Add(crc)) - return; + var buffer = sample.Length < PGConstants.MaxMegEntryPathLength + ? stackalloc char[PGConstants.MaxMegEntryPathLength] + : pooledBuffer = ArrayPool.Shared.Rent(sample.Length); - - if (normalizedSampleName.Length > PGConstants.MaxPathLength) + try { - AddError(VerificationError.Create( - this, - VerifierErrorCodes.FilePathTooLong, - $"Sample name '{sample}' is too long.", - VerificationSeverity.Error, - sample)); - return; - } + var length = PathNormalizer.Normalize(sample, buffer, SampleNormalizerOptions); + var sampleNameBuffer = buffer.Slice(0, length); - var normalizedSampleNameString = normalizedSampleName.ToString(); + var crc = _hashingService.GetCrc32(sampleNameBuffer, Encoding.ASCII); + if (!visitedSamples.Add(crc)) + return; - if (sfxEvent.IsLocalized) - { - foreach (var language in languagesToVerify) + if (sfxEvent.IsLocalized) + { + foreach (var language in languagesToVerify) + { + VerifySampleLocalized(sfxEvent, sampleNameBuffer, language, out var localized); + if (!localized) + return; + } + } + else { - var localizedSampleName = _languageManager.LocalizeFileName(normalizedSampleNameString, language, out var localized); - VerifySample(localizedSampleName, sfxEvent); - - if (!localized) - return; + VerifySample(sampleNameBuffer, sfxEvent); } } - else + finally { - VerifySample(normalizedSampleNameString, sfxEvent); + if (pooledBuffer is not null) + ArrayPool.Shared.Return(pooledBuffer); } + } - private void VerifySample(string sample, SfxEvent sfxEvent) + private void VerifySampleLocalized(SfxEvent sfxEvent, ReadOnlySpan sample, LanguageType language, out bool localized) + { + char[]? pooledBuffer = null; + + var buffer = sample.Length < PGConstants.MaxMegEntryPathLength + ? stackalloc char[PGConstants.MaxMegEntryPathLength] + : pooledBuffer = ArrayPool.Shared.Rent(sample.Length); + try + { + var l = _languageManager.LocalizeFileName(sample, language, buffer, out localized); + var localizedName = buffer.Slice(0, l); + VerifySample(localizedName, sfxEvent); + } + finally + { + if (pooledBuffer is not null) + ArrayPool.Shared.Return(pooledBuffer); + } + } + + private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent) { using var sampleStream = Repository.TryOpenFile(sample); if (sampleStream is null) { + var sampleString = sample.ToString(); AddError(VerificationError.Create( this, - VerifierErrorCodes.SampleNotFound, - $"Audio file '{sample}' could not be found.", + VerifierErrorCodes.SampleNotFound, + $"Audio file '{sampleString}' could not be found.", VerificationSeverity.Error, - sample)); + sampleString)); return; } using var binaryReader = new BinaryReader(sampleStream); @@ -121,42 +151,46 @@ private void VerifySample(string sample, SfxEvent sfxEvent) if (format != WaveFormats.PCM) { + var sampleString = sample.ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.SampleNotPCM, - $"Audio file '{sample}' has an invalid format '{format}'. Supported is {WaveFormats.PCM}", + $"Audio file '{sampleString}' has an invalid format '{format}'. Supported is {WaveFormats.PCM}", VerificationSeverity.Error, - sample)); + sampleString)); } if (channels > 1 && !IsAmbient2D(sfxEvent)) { + var sampleString = sample.ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.SampleNotMono, - $"Audio file '{sample}' is not mono audio.", - VerificationSeverity.Information, - sample)); + $"Audio file '{sampleString}' is not mono audio.", + VerificationSeverity.Information, + sampleString)); } if (sampleRate > 48_000) { + var sampleString = sample.ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes. InvalidSampleRate, - $"Audio file '{sample}' has a too high sample rate of {sampleRate}. Maximum is 48.000Hz.", + $"Audio file '{sampleString}' has a too high sample rate of {sampleRate}. Maximum is 48.000Hz.", VerificationSeverity.Error, - sample)); + sampleString)); } if (bitPerSecondPerChannel > 16) { + var sampleString = sample.ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.InvalidBitsPerSeconds, - $"Audio file '{sample}' has an invalid bit size of {bitPerSecondPerChannel}. Supported are 16bit.", - VerificationSeverity.Error, - sample)); + $"Audio file '{sampleString}' has an invalid bit size of {bitPerSecondPerChannel}. Supported are 16bit.", + VerificationSeverity.Error, + sampleString)); } } diff --git a/src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs b/src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs new file mode 100644 index 0000000..e8543d6 --- /dev/null +++ b/src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using AET.ModVerify.Reporting; +using AET.ModVerify.Settings; +using PG.StarWarsGame.Engine.Database; + +namespace AET.ModVerify.Verifiers; + +internal sealed class GameDatabaseInitializationErrorCollector( + IDatabaseErrorCollection errorCollection, + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) : GameVerifierBase(gameDatabase, settings, serviceProvider) +{ + public override string FriendlyName => "Reporting Game Initialization Errors"; + + protected override void RunVerification(CancellationToken token) + { + AddErrors(new InitializationErrorReporter(Repository, Services).GetErrors(errorCollection.InitializationErrors)); + AddErrors(new XmlParseErrorReporter(Repository, Services).GetErrors(errorCollection.XmlErrors)); + } + + private void AddErrors(IEnumerable errors) + { + foreach (var error in errors) + AddError(error); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs new file mode 100644 index 0000000..e2e2e10 --- /dev/null +++ b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs @@ -0,0 +1,16 @@ +using System; +using AET.ModVerify.Reporting; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; + +namespace AET.ModVerify.Verifiers; + +internal sealed class InitializationErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) : InitializationErrorReporterBase(gameRepository, serviceProvider) +{ + public override string Name => "InitializationErrors"; + + protected override void CreateError(InitializationError error, out ErrorData errorData) + { + errorData = new ErrorData("INIT00", error.Message, [error.GameManager], VerificationSeverity.Critical); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs new file mode 100644 index 0000000..df3e22e --- /dev/null +++ b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using AET.ModVerify.Reporting; +using AnakinRaW.CommonUtilities; +using PG.StarWarsGame.Engine.IO.Repositories; + +namespace AET.ModVerify.Verifiers; + +internal abstract class InitializationErrorReporterBase(IGameRepository gameRepository, IServiceProvider serviceProvider) +{ + protected readonly IGameRepository GameRepository = gameRepository ?? throw new ArgumentNullException(nameof(gameRepository)); + protected readonly IServiceProvider ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + + public abstract string Name { get; } + + public IEnumerable GetErrors(IEnumerable errors) + { + foreach (var error in errors) + { + CreateError(error, out var data); + yield return new VerificationError(data.Identifier, data.Message, Name, data.Assets, data.Severity); + } + } + + protected abstract void CreateError(T error, out ErrorData errorData); + + protected readonly ref struct ErrorData + { + public string Identifier { get; } + public string Message { get; } + public IEnumerable Assets { get; } + public VerificationSeverity Severity { get; } + + public ErrorData(string identifier, string message, IEnumerable assets, VerificationSeverity severity) + { + ThrowHelper.ThrowIfNullOrEmpty(identifier); + ThrowHelper.ThrowIfNullOrEmpty(message); + Identifier = identifier; + Message = message; + Assets = assets; + Severity = severity; + } + + public ErrorData(string identifier, string message, VerificationSeverity severity) : this(identifier, message, [], severity) + { + } + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/XmlParseErrorCollector.cs b/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs similarity index 54% rename from src/ModVerify/Verifiers/XmlParseErrorCollector.cs rename to src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs index 0819eb8..9c0bdc9 100644 --- a/src/ModVerify/Verifiers/XmlParseErrorCollector.cs +++ b/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs @@ -1,39 +1,36 @@ using System; using System.Collections.Generic; -using System.Threading; +using System.IO.Abstractions; using AET.ModVerify.Reporting; -using AET.ModVerify.Settings; -using PG.StarWarsGame.Engine.Database; +using AET.ModVerify.Utilities; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace AET.ModVerify.Verifiers; -public sealed class XmlParseErrorCollector( - IEnumerable xmlErrors, - IGameDatabase gameDatabase, - GameVerifySettings settings, - IServiceProvider serviceProvider) : - GameVerifierBase(gameDatabase, settings, serviceProvider) +internal sealed class XmlParseErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) : + InitializationErrorReporterBase(gameRepository, serviceProvider) { - public override string FriendlyName => "XML Parsing Errors"; + private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - protected override void RunVerification(CancellationToken token) + public override string Name => "XMLError"; + + protected override void CreateError(XmlError error, out ErrorData errorData) { - foreach (var xmlError in xmlErrors) - AddError(ConvertXmlToVerificationError(xmlError)); - } + var id = GetIdFromError(error.ErrorKind); + var severity = GetSeverityFromError(error.ErrorKind); - private VerificationError ConvertXmlToVerificationError(XmlParseErrorEventArgs xmlError) - { - var id = GetIdFromError(xmlError.ErrorKind); - var severity = GetSeverityFromError(xmlError.ErrorKind); + var strippedFileName = _fileSystem.Path + .GetGameStrippedPath(GameRepository.Path.AsSpan(), error.FileLocation.XmlFile.ToUpperInvariant().AsSpan()).ToString(); var assets = new List { - GetGameStrippedPath(xmlError.File.ToUpperInvariant()) + strippedFileName }; - var xmlElement = xmlError.Element; + var xmlElement = error.Element; if (xmlElement is not null) { @@ -49,11 +46,19 @@ private VerificationError ConvertXmlToVerificationError(XmlParseErrorEventArgs x } - return VerificationError.Create(this, id, xmlError.Message, severity, assets); + var errorMessage = CreateErrorMessage(error, strippedFileName); + errorData = new ErrorData(id, errorMessage, assets, severity); + } + + private static string CreateErrorMessage(XmlError error, string strippedFileName) + { + if (error.FileLocation.Line.HasValue) + return $"{error.Message} File='{strippedFileName} #{error.FileLocation.Line.Value}'"; + return $"{error.Message} File='{strippedFileName}'"; } - private VerificationSeverity GetSeverityFromError(XmlParseErrorKind xmlErrorErrorKind) + private static VerificationSeverity GetSeverityFromError(XmlParseErrorKind xmlErrorErrorKind) { return xmlErrorErrorKind switch { @@ -65,11 +70,13 @@ private VerificationSeverity GetSeverityFromError(XmlParseErrorKind xmlErrorErro XmlParseErrorKind.MissingReference => VerificationSeverity.Error, XmlParseErrorKind.TooLongData => VerificationSeverity.Warning, XmlParseErrorKind.DataBeforeHeader => VerificationSeverity.Information, + XmlParseErrorKind.MissingNode => VerificationSeverity.Critical, + XmlParseErrorKind.UnknownNode => VerificationSeverity.Information, _ => VerificationSeverity.Warning }; } - private string GetIdFromError(XmlParseErrorKind xmlErrorErrorKind) + private static string GetIdFromError(XmlParseErrorKind xmlErrorErrorKind) { return xmlErrorErrorKind switch { @@ -82,6 +89,8 @@ private string GetIdFromError(XmlParseErrorKind xmlErrorErrorKind) XmlParseErrorKind.TooLongData => VerifierErrorCodes.XmlValueTooLong, XmlParseErrorKind.Unknown => VerifierErrorCodes.GenericXmlError, XmlParseErrorKind.DataBeforeHeader => VerifierErrorCodes.XmlDataBeforeHeader, + XmlParseErrorKind.MissingNode => VerifierErrorCodes.XmlMissingNode, + XmlParseErrorKind.UnknownNode => VerifierErrorCodes.XmlUnsupportedTag, _ => throw new ArgumentOutOfRangeException(nameof(xmlErrorErrorKind), xmlErrorErrorKind, null) }; } diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 14d2e67..0a60b50 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -5,7 +5,7 @@ using AET.ModVerify.Settings; using AnakinRaW.CommonUtilities.Collections; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.DataTypes; +using PG.StarWarsGame.Engine.Xml; namespace AET.ModVerify.Verifiers; @@ -19,15 +19,15 @@ public sealed class DuplicateNameFinder( protected override void RunVerification(CancellationToken token) { - CheckDatabaseForDuplicates(Database.GameObjects, "GameObject"); - CheckDatabaseForDuplicates(Database.SfxEvents, "SFXEvent"); + CheckDatabaseForDuplicates(Database.GameObjectTypeManager, "GameObject"); + CheckDatabaseForDuplicates(Database.SfxGameManager, "SFXEvent"); } - private void CheckDatabaseForDuplicates(IXmlDatabase database, string databaseName) where T : XmlObject + private void CheckDatabaseForDuplicates(IGameManager gameManager, string databaseName) where T : NamedXmlObject { - foreach (var key in database.EntryKeys) + foreach (var key in gameManager.EntryKeys) { - var entries = database.GetEntries(key); + var entries = gameManager.GetEntries(key); if (entries.Count > 1) { var entryNames = entries.Select(x => x.Name); @@ -41,7 +41,7 @@ private void CheckDatabaseForDuplicates(IXmlDatabase database, string data } } - private string CreateDuplicateErrorMessage(string databaseName, ReadOnlyFrugalList entries) where T : XmlObject + private string CreateDuplicateErrorMessage(string databaseName, ReadOnlyFrugalList entries) where T : NamedXmlObject { var firstEntry = entries.First(); diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index a1f10cd..768236c 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -1,16 +1,14 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO.Abstractions; using System.Threading; using AET.ModVerify.Reporting; using AET.ModVerify.Settings; -using AnakinRaW.CommonUtilities.FileSystem; using AnakinRaW.CommonUtilities.SimplePipeline.Steps; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Repositories; +using PG.StarWarsGame.Engine.IO.Repositories; namespace AET.ModVerify.Verifiers; @@ -70,18 +68,4 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter exceptionHandler(e); } } - - protected string GetGameStrippedPath(string path) - { - if (!FileSystem.Path.IsPathFullyQualified(path)) - return path; - - if (path.Length <= Repository.Path.Length) - return path; - - if (path.StartsWith(Repository.Path, StringComparison.OrdinalIgnoreCase)) - return path.Substring(Repository.Path.Length); - - return path; - } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs index 8a6e7ba..bfb9f7c 100644 --- a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs +++ b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs @@ -5,9 +5,8 @@ using System.Threading; using AET.ModVerify.Reporting; using AET.ModVerify.Settings; +using AET.ModVerify.Utilities; using Microsoft.Extensions.DependencyInjection; -using PG.Commons.Binary; -using PG.Commons.Files; using PG.Commons.Utilities; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; @@ -16,7 +15,8 @@ using PG.StarWarsGame.Files.ALO.Services; using PG.StarWarsGame.Files.ChunkFiles.Data; using AnakinRaW.CommonUtilities.FileSystem; -using System.Reflection; +using PG.StarWarsGame.Files; +using PG.StarWarsGame.Files.Binary; namespace AET.ModVerify.Verifiers; @@ -34,7 +34,7 @@ public sealed class ReferencedModelsVerifier( protected override void RunVerification(CancellationToken token) { - var aloQueue = new Queue(Database.GameObjects.Entries + var aloQueue = new Queue(Database.GameObjectTypeManager.Entries .SelectMany(x => x.Models) .Concat(FocHardcodedConstants.HardcodedModels)); @@ -85,7 +85,7 @@ private void VerifyModelOrParticle(Stream modelStream, Queue workingQueu } catch (BinaryCorruptedException e) { - var aloFile = GetGameStrippedPath(modelStream.GetFilePath()); + var aloFile = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), modelStream.GetFilePath().AsSpan()).ToString(); var message = $"{aloFile} is corrupted: {e.Message}"; AddError(VerificationError.Create(this, VerifierErrorCodes.ModelBroken, message, VerificationSeverity.Critical, aloFile)); } @@ -99,7 +99,7 @@ private void VerifyParticle(IAloParticleFile file) e => e is ArgumentException, _ => { - var modelFilePath = GetGameStrippedPath(file.FilePath); + var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.InvalidTexture, @@ -116,7 +116,7 @@ private void VerifyParticle(IAloParticleFile file) if (!fileName.Equals(name, StringComparison.OrdinalIgnoreCase)) { - var modelFilePath = GetGameStrippedPath(file.FilePath); + var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.InvalidParticleName, @@ -135,7 +135,8 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) e => e is ArgumentException, _ => { - var modelFilePath = GetGameStrippedPath(file.FilePath); + var modelFilePath = + FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.InvalidTexture, @@ -151,13 +152,14 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) e => e is ArgumentException, _ => { - var modelFilePath = GetGameStrippedPath(file.FilePath); + var shaderPath = + FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.InvalidShader, - $"Invalid texture file name '{shader}' in model '{modelFilePath}'", + $"Invalid texture file name '{shader}' in model '{shaderPath}'", VerificationSeverity.Error, - shader, modelFilePath)); + shader, shaderPath)); }); } @@ -168,13 +170,14 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) e => e is ArgumentException, _ => { - var modelFilePath = GetGameStrippedPath(file.FilePath); + var proxyPath = FileSystem.Path + .GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( this, VerifierErrorCodes.InvalidProxy, - $"Invalid proxy file name '{proxy}' in model '{modelFilePath}'", + $"Invalid proxy file name '{proxy}' in model '{proxyPath}'", VerificationSeverity.Error, - proxy, modelFilePath)); + proxy, proxyPath)); }); } } @@ -186,7 +189,8 @@ private void VerifyTextureExists(IPetroglyphFileHolder().ToArray(); + + private void VerifyGuiTextures(ISet visitedTextures) + { + VerifyMegaTexturesExist(); + + var components = new List + { + DefaultComponentIdentifier + }; + components.AddRange(Database.GuiDialogManager.Components); + + foreach (var component in components) + VerifyGuiComponentTexturesExist(component, visitedTextures); + + } + + private void VerifyMegaTexturesExist() + { + var megaTextureName = Database.GuiDialogManager.GuiDialogsXml?.TextureData.MegaTexture; + if (Database.GuiDialogManager.MtdFile is null) + { + var mtdFileName = megaTextureName ?? "<>"; + VerificationError.Create(this, MtdNotFound, $"MtdFile '{mtdFileName}.mtd' could not be found", + VerificationSeverity.Critical, mtdFileName); + } + + + if (megaTextureName is not null) + { + var megaTextureFileName = $"{megaTextureName}.tga"; + + if (!Repository.TextureRepository.FileExists(megaTextureFileName)) + { + VerificationError.Create(this, TexutreNotFound, $"Could not find texture '{megaTextureFileName}' could not be found", + VerificationSeverity.Error, megaTextureFileName); + } + } + + + var compressedMegaTextureName = Database.GuiDialogManager.GuiDialogsXml?.TextureData.CompressedMegaTexture; + if (compressedMegaTextureName is not null) + { + var compressedMegaTextureFieName = $"{compressedMegaTextureName}.dds"; + + if (!Repository.TextureRepository.FileExists(compressedMegaTextureFieName)) + { + VerificationError.Create(this, TexutreNotFound, $"Could not find texture '{compressedMegaTextureFieName}' could not be found", + VerificationSeverity.Error, compressedMegaTextureFieName); + } + } + } + + private void VerifyGuiComponentTexturesExist(string component, ISet visitedTextures) + { + var middleButtonInRepoMode = false; + + + var entriesForComponent = GetTextureEntriesForComponents(component, out var defined); + if (!defined) + return; + + foreach (var componentType in GuiComponentTypes) + { + try + { + if (!entriesForComponent.TryGetValue(componentType, out var texture)) + continue; + + if (!visitedTextures.Add(texture.Texture)) + { + // If we are in a special case we don't want to skip + if (!middleButtonInRepoMode && + componentType is not GuiComponentType.ButtonMiddle && + componentType is not GuiComponentType.Scanlines && + componentType is not GuiComponentType.FrameBackground) + continue; + } + + if (!Database.GuiDialogManager.TextureExists( + texture, + out var origin, + out var isNone, + middleButtonInRepoMode) + && !isNone) + { + + if (origin == GuiTextureOrigin.MegaTexture && texture.Texture.Length > MtdFileConstants.MaxFileNameSize) + { + AddError(VerificationError.Create(this, FileNameTooLong, + $"The filename is too long. Max length is {MtdFileConstants.MaxFileNameSize} characters.", + VerificationSeverity.Error, texture.Texture)); + } + else + { + var message = $"Could not find GUI texture '{texture.Texture}' at location '{origin}'."; + + if (texture.Texture.Length > PGConstants.MaxMegEntryPathLength) + message += " The file name is too long."; + + AddError(VerificationError.Create(this, TexutreNotFound, + message, VerificationSeverity.Error, + texture.Texture, component, origin.ToString())); + } + } + + if (componentType is GuiComponentType.ButtonMiddle && origin is GuiTextureOrigin.Repository) + middleButtonInRepoMode = true; + } + finally + { + + if (componentType >= GuiComponentType.ButtonRightDisabled) + middleButtonInRepoMode = false; + } + } + } + + private IReadOnlyDictionary GetTextureEntriesForComponents(string component, out bool defined) + { + if (component == DefaultComponentIdentifier) + { + defined = true; + return Database.GuiDialogManager.DefaultTextureEntries; + } + return Database.GuiDialogManager.GetTextureEntries(component, out defined); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs b/src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs new file mode 100644 index 0000000..58e600c --- /dev/null +++ b/src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using AET.ModVerify.Settings; +using PG.StarWarsGame.Engine.Database; + +namespace AET.ModVerify.Verifiers; +public sealed partial class ReferencedTexturesVerifier( + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) + : + GameVerifierBase(gameDatabase, settings, serviceProvider) +{ + public const string MtdNotFound = "TEX00"; + public const string TexutreNotFound = "TEX01"; + public const string FileNameTooLong = "PAT00"; + + protected override void RunVerification(CancellationToken token) + { + var textures = new HashSet(StringComparer.OrdinalIgnoreCase); + try + { + VerifyGuiTextures(textures); + } + finally + { + textures.Clear(); + } + + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/VerifierErrorCodes.cs b/src/ModVerify/Verifiers/VerifierErrorCodes.cs index d4b3fa0..6a68e91 100644 --- a/src/ModVerify/Verifiers/VerifierErrorCodes.cs +++ b/src/ModVerify/Verifiers/VerifierErrorCodes.cs @@ -32,4 +32,6 @@ public static class VerifierErrorCodes public const string MissingXmlReference = "XML06"; public const string XmlValueTooLong = "XML07"; public const string XmlDataBeforeHeader = "XML08"; + public const string XmlMissingNode = "XML09"; + public const string XmlUnsupportedTag = "XML10"; } \ No newline at end of file diff --git a/src/ModVerify/VerifyGamePipeline.cs b/src/ModVerify/VerifyGamePipeline.cs index e546447..6eda635 100644 --- a/src/ModVerify/VerifyGamePipeline.cs +++ b/src/ModVerify/VerifyGamePipeline.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -13,8 +12,6 @@ using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; namespace AET.ModVerify; @@ -23,14 +20,12 @@ public abstract class VerifyGamePipeline : Pipeline private readonly List _verificationSteps = new(); private readonly GameEngineType _targetType; private readonly GameLocations _gameLocations; - private readonly ParallelRunner _verifyRunner; + private readonly ParallelStepRunner _verifyRunner; protected GameVerifySettings Settings { get; } public IReadOnlyCollection Errors { get; private set; } = Array.Empty(); - - private readonly ConcurrentBag _xmlParseErrors = new(); - + protected VerifyGamePipeline(GameEngineType targetType, GameLocations gameLocations, GameVerifySettings settings, IServiceProvider serviceProvider) : base(serviceProvider) { @@ -41,10 +36,9 @@ protected VerifyGamePipeline(GameEngineType targetType, GameLocations gameLocati if (settings.ParallelVerifiers is < 0 or > 64) throw new ArgumentException("Settings has invalid parallel worker number.", nameof(settings)); - _verifyRunner = new ParallelRunner(settings.ParallelVerifiers, serviceProvider); + _verifyRunner = new ParallelStepRunner(settings.ParallelVerifiers, serviceProvider); } - protected sealed override Task PrepareCoreAsync() { _verificationSteps.Clear(); @@ -58,21 +52,18 @@ protected sealed override async Task RunCoreAsync(CancellationToken token) { var databaseService = ServiceProvider.GetRequiredService(); - IGameDatabase database; - try - { - databaseService.XmlParseError += OnXmlParseError; - database = await databaseService.CreateDatabaseAsync(_targetType, _gameLocations, token); - } - finally + var initializationErrorListener = new ConcurrentGameDatabaseErrorListener(); + var initOptions = new GameInitializationOptions { - databaseService.XmlParseError -= OnXmlParseError; - databaseService.Dispose(); - } + Locations = _gameLocations, + TargetEngineType = _targetType, + ErrorListener = initializationErrorListener + + }; + var database = await databaseService.InitializeGameAsync(initOptions, token); + + AddStep(new GameDatabaseInitializationErrorCollector(initializationErrorListener, database, Settings, ServiceProvider)); - - AddStep(new XmlParseErrorCollector(_xmlParseErrors, database, Settings, ServiceProvider)); - foreach (var gameVerificationStep in CreateVerificationSteps(database)) AddStep(gameVerificationStep); @@ -113,9 +104,4 @@ private void AddStep(GameVerifierBase verifier) _verifyRunner.AddStep(verifier); _verificationSteps.Add(verifier); } - - private void OnXmlParseError(IPetroglyphXmlParser sender, XmlParseErrorEventArgs e) - { - _xmlParseErrors.Add(e); - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs new file mode 100644 index 0000000..f054f1c --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs @@ -0,0 +1,5 @@ +using PG.StarWarsGame.Engine.Database; + +namespace PG.StarWarsGame.Engine.Audio.Sfx; + +public interface ISfxEventGameManager : IGameManager; \ 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 new file mode 100644 index 0000000..aed53f2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEvent.cs @@ -0,0 +1,239 @@ +using System.Collections.Generic; +using System.Linq; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.Audio.Sfx; + +public sealed class SfxEvent : NamedXmlObject +{ + private byte _minVolume = DefaultMinVolume; + private byte _maxVolume = DefaultMaxVolume; + private byte _minPitch = DefaultMinPitch; + private byte _maxPitch = DefaultMaxPitch; + private byte _minPan2D = DefaultMinPan2d; + private byte _maxPan2D = DefaultMaxPan2d; + private uint _minPredelay; + private uint _maxPredelay; + private uint _minPostdelay; + private uint _maxPostdelay; + + public const byte MaxVolumeValue = 100; + public const byte MaxPitchValue = 200; + public const byte MinPitchValue = 50; + 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; + + // Default values which are not the default value of the type + public const byte DefaultPriority = 3; + public const bool DefaultIs3d = true; + public const byte DefaultProbability = 100; + public const sbyte DefaultPlayCount = 1; + public const sbyte DefaultMaxInstances = 1; + public const byte DefaultMinVolume = 100; + public const byte DefaultMaxVolume = 100; + public const byte DefaultMinPitch = 100; + public const byte DefaultMaxPitch = 100; + public const byte DefaultMinPan2d = 50; + public const byte DefaultMaxPan2d = 50; + public const float DefaultVolumeSaturationDistance = 300.0f; + + public bool IsPreset { get; internal set; } + + public bool Is3D { get; internal set; } = DefaultIs3d; + + public bool Is2D { get; internal set; } + + public bool IsGui { get; internal set; } + + public bool IsHudVo { get; internal set; } + + public bool IsUnitResponseVo { get; internal set; } + + public bool IsAmbientVo { get; internal set; } + + public bool IsLocalized { get; internal set; } + + public SfxEvent? Preset { get; internal set; } + + public string? UsePresetName { get; internal set; } + + public bool PlaySequentially { get; internal set; } + + public IEnumerable AllSamples => PreSamples.Concat(Samples).Concat(PostSamples); + + public IReadOnlyList PreSamples { get; internal set; } = []; + + public IReadOnlyList Samples { get; internal set; } = []; + + public IReadOnlyList PostSamples { get; internal set; } = []; + + public IReadOnlyList LocalizedTextIDs { get; internal set; } = []; + + public byte Priority { get; internal set; } = DefaultPriority; + + public byte Probability { get; internal set; } = DefaultProbability; + + public sbyte PlayCount { get; internal set; } = DefaultPlayCount; + + public float LoopFadeInSeconds { get; internal set; } + + public float LoopFadeOutSeconds { get; internal set; } + + public sbyte MaxInstances { get; internal set; } = DefaultMaxInstances; + + public float VolumeSaturationDistance { get; internal set; } = DefaultVolumeSaturationDistance; + + public bool KillsPreviousObjectsSfx { get; internal set; } + + public string? OverlapTestName { get; internal set; } + + public string? ChainedSfxEventName { get; internal set; } + + public byte MinVolume + { + get => _minVolume; + internal set => _minVolume = value; + } + + public byte MaxVolume + { + get => _maxVolume; + internal set => _maxVolume = value; + } + + public byte MinPitch + { + get => _minPitch; + internal set => _minPitch = value; + } + + public byte MaxPitch + { + get => _maxPitch; + internal set => _maxPitch = value; + } + + public byte MinPan2D + { + get => _minPan2D; + internal set => _minPan2D = value; + } + + public byte MaxPan2D + { + get => _maxPan2D; + internal set => _maxPan2D = value; + } + + public uint MinPredelay + { + get => _minPredelay; + internal set => _minPredelay = value; + } + + public uint MaxPredelay + { + get => _maxPredelay; + internal set => _maxPredelay = value; + } + + public uint MinPostdelay + { + get => _minPostdelay; + internal set => _minPostdelay = value; + } + + public uint MaxPostdelay + { + get => _maxPostdelay; + internal set => _maxPostdelay = value; + } + + + internal SfxEvent(string name, Crc32 nameCrc, XmlLocationInfo location) + : base(name, nameCrc, location) + { + } + + internal override void CoerceValues() + { + AdjustMinMaxValues(ref _minVolume, ref _maxVolume); + AdjustMinMaxValues(ref _minPitch, ref _maxPitch); + AdjustMinMaxValues(ref _minPan2D, ref _maxPan2D); + AdjustMinMaxValues(ref _minPredelay, ref _maxPredelay); + AdjustMinMaxValues(ref _minPostdelay, ref _maxPostdelay); + } + + /* + * The engine also copies the of the preset (which is usually null). + * As this would cause this SFXEvent loose the information of the coded preset, we do not copy the preset's preset value. + * Example: + * + * + * Preset Yes + * 90 + * + * + * Engine Behavior: SFXEvent instance(Name: A, Use_Preset: null, Min_Volume: 90) + * PG.StarWarsGame.Engine Behavior: SFXEvent instance(Name: A, Use_Preset: Preset, Min_Volume: 90) + */ + public void ApplyPreset(SfxEvent preset) + { + Is3D = preset.Is3D; + Is2D = preset.Is2D; + IsGui = preset.IsGui; + IsHudVo = preset.IsHudVo; + IsUnitResponseVo = preset.IsUnitResponseVo; + IsAmbientVo = preset.IsAmbientVo; + IsLocalized = preset.IsLocalized; + Preset = preset; + UsePresetName = preset.Name; + PlaySequentially = preset.PlaySequentially; + PreSamples = preset.PreSamples; + Samples = preset.Samples; + PostSamples = preset.PostSamples; + LocalizedTextIDs = preset.LocalizedTextIDs; + Priority = preset.Priority; + Probability = preset.Probability; + PlayCount = preset.PlayCount; + LoopFadeInSeconds = preset.LoopFadeInSeconds; + LoopFadeOutSeconds = preset.LoopFadeOutSeconds; + MaxInstances = preset.MaxInstances; + MinPredelay = preset.MinPredelay; + MaxPredelay = preset.MaxPredelay; + MinPostdelay = preset.MinPostdelay; + MaxPostdelay = preset.MaxPostdelay; + OverlapTestName = preset.OverlapTestName; + ChainedSfxEventName = preset.ChainedSfxEventName; + MinVolume = preset.MinVolume; + MaxVolume = preset.MaxVolume; + MinPitch = preset.MinPitch; + MaxPitch = preset.MaxPitch; + MinPan2D = preset.MinPan2D; + MaxPan2D = preset.MaxPan2D; + } + + private static void AdjustMinMaxValues(ref byte minValue, ref byte maxValue) + { + if (minValue > maxValue) + minValue = maxValue; + if (maxValue < minValue) + maxValue = minValue; + } + + private static void AdjustMinMaxValues(ref uint minValue, ref uint maxValue) + { + if (minValue > maxValue) + minValue = maxValue; + if (maxValue < minValue) + maxValue = minValue; + } +} \ 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 new file mode 100644 index 0000000..0b4c407 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Localization; +using PG.StarWarsGame.Engine.Xml.Parsers; + +namespace PG.StarWarsGame.Engine.Audio.Sfx; + +internal class SfxEventGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorListener, serviceProvider), ISfxEventGameManager +{ + public IEnumerable InstalledLanguages { get; private set; } = []; + + protected override async Task InitializeCoreAsync(CancellationToken token) + { + Logger?.LogInformation("Initializing Language files..."); + InstalledLanguages = GameRepository.InitializeInstalledSfxMegFiles().ToList(); + Logger?.LogInformation("Finished initializing Language files"); + + Logger?.LogInformation("Parsing SFXEvents..."); + + var contentParser = ServiceProvider.GetRequiredService(); + contentParser.XmlParseError += OnParseError; + try + { + await Task.Run(() => contentParser.ParseEntriesFromContainerXml( + "DATA\\XML\\SFXEventFiles.XML", + ErrorListener, + GameRepository, + "DATA\\XML", + NamedEntries, + VerifyFilePathLength), + token); + } + finally + { + contentParser.XmlParseError -= OnParseError; + } + } + + private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) + { + if (e.IsContainer || e.IsError) + { + e.Continue = false; + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = GetMessage(e) + }); + } + } + + private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) + { + if (errorEventArgs.IsError) + return $"Error while parsing SFXEvent XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; + return "Could not find SFXEventFiles.xml"; + } + + private void VerifyFilePathLength(string filePath) + { + if (filePath.Length > PGConstants.MaxSFXEventDatabaseFileName) + { + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = $"SFXEvent file '{filePath}' is longer than {PGConstants.MaxSFXEventDatabaseFileName} characters." + }); + } + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs new file mode 100644 index 0000000..793eb44 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs @@ -0,0 +1,15 @@ +namespace PG.StarWarsGame.Engine.CommandBar; + +public enum CommandBarComponentType +{ + Shell, + Icon, + Button, + Text, + TextButton, + Model, + Bar, + Select, + Count, + None, +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs new file mode 100644 index 0000000..d53808e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -0,0 +1,84 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.Commons.Collections; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Xml.Parsers; + +namespace PG.StarWarsGame.Engine.CommandBar; + +public interface ICommandBarGameManager : IGameManager +{ +} + +internal class CommandBarGameManager( + GameRepository repository, + DatabaseErrorListenerWrapper errorListener, + IServiceProvider serviceProvider) + : GameManagerBase(repository, errorListener, serviceProvider), ICommandBarGameManager +{ + protected override async Task InitializeCoreAsync(CancellationToken token) + { + Logger?.LogInformation("PCreating command bar components..."); + + var contentParser = ServiceProvider.GetRequiredService(); + contentParser.XmlParseError += OnParseError; + + var parsedCommandBarComponents = new ValueListDictionary(); + + try + { + await Task.Run(() => contentParser.ParseEntriesFromContainerXml( + "DATA\\XML\\CommandBarComponentFiles.XML", + ErrorListener, + GameRepository, + ".\\DATA\\XML", + parsedCommandBarComponents, + VerifyFilePathLength), + token); + } + finally + { + contentParser.XmlParseError -= OnParseError; + } + } + + private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) + { + if (e.IsContainer || e.IsError) + { + e.Continue = false; + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = GetMessage(e) + }); + } + } + + private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) + { + if (errorEventArgs.IsError) + 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) + { + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = $"CommandBar file '{filePath}' is longer than {PGConstants.MaxCommandBarDatabaseFileName} characters." + }); + } + } +} + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs new file mode 100644 index 0000000..fb06793 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Numerics; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.CommandBar.Xml; + +public sealed class CommandBarComponentData(string name, Crc32 crc, XmlLocationInfo location) : NamedXmlObject(name, crc, location) +{ + public const float DefaultScale = 1.0f; + public const float DefaultBlinkRate = 0.2f; + public const int DefaultBaseLayer = 2; + public static readonly Vector2 DefaultOffsetWidescreenValue = new(9.9999998e17f, 9.9999998e17f); + + public IReadOnlyList SelectedTextureNames { get; internal set; } = []; + public IReadOnlyList BlankTextureNames { get; internal set; } = []; + public IReadOnlyList IconAlternateTextureNames { get; internal set; } = []; + public IReadOnlyList MouseOverTextureNames { get; internal set; } = []; + public IReadOnlyList BarTextureNames { get; internal set; } = []; + public IReadOnlyList BarOverlayNames { get; internal set; } = []; + public IReadOnlyList AlternateFontNames { get; internal set; } = []; + public IReadOnlyList TooltipTexts { get; internal set; } = []; + public IReadOnlyList LowerEffectTextureNames { get; internal set; } = []; + public IReadOnlyList UpperEffectTextureNames { get; internal set; } = []; + public IReadOnlyList OverlayTextureNames { get; internal set; } = []; + public IReadOnlyList Overlay2TextureNames { get; internal set; } = []; + + public string? IconTextureName { get; internal set; } + public string? DisabledTextureName { get; internal set; } + public string? FlashTextureName { get; internal set; } + public string? BuildTextureName { get; internal set; } + public string? ModelName { get; internal set; } + public string? BoneName { get; internal set; } + public string? CursorTextureName { get; internal set; } + public string? FontName { get; internal set; } + public string? ClickSfx { get; internal set; } + public string? MouseOverSfx { get; internal set; } + public string? RightClickSfx { get; internal set; } + public string? Type { get; internal set; } + public string? Group { get; internal set; } + public string? AssociatedText { get; internal set; } + + public bool DragAndDrop { get; internal set; } + public bool DragSelect { get; internal set; } + public bool Receptor { get; internal set; } + public bool Toggle { get; internal set; } + public bool Tab { get; internal set; } + public bool Hidden { get; internal set; } + public bool ClearColor { get; internal set; } + public bool Disabled { get; internal set; } + public bool SwapTexture { get; internal set; } + public bool DrawAdditive { get; internal set; } + public bool Editable { get; internal set; } + public bool TextOutline { get; internal set; } + public bool Stackable { get; internal set; } + public bool ModelOffsetX { get; internal set; } + public bool ModelOffsetY { get; internal set; } + public bool ScaleModelX { get; internal set; } + public bool ScaleModelY { get; internal set; } + public bool Collideable { get; internal set; } + public bool TextEmboss { get; internal set; } + public bool ShouldGhost { get; internal set; } + public bool GhostBaseOnly { get; internal set; } + public bool CrossFade { get; internal set; } + public bool LeftJustified { get; internal set; } + public bool RightJustified { get; internal set; } + public bool NoShell { get; internal set; } + public bool SnapDrag { get; internal set; } + public bool SnapLocation { get; internal set; } + public bool OffsetRender { get; internal set; } + public bool BlinkFade { get; internal set; } + public bool NoHiddenCollision { get; internal set; } + public bool ManualOffset { get; internal set; } + public bool SelectedAlpha { get; internal set; } + public bool PixelAlign { get; internal set; } + public bool CanDragStack { get; internal set; } + public bool CanAnimate { get; internal set; } + public bool LoopAnim { get; internal set; } + public bool SmoothBar { get; internal set; } + public bool OutlinedBar { get; internal set; } + public bool DragBack { get; internal set; } + public bool LowerEffectAdditive { get; internal set; } + public bool UpperEffectAdditive { get; internal set; } + public bool ClickShift { get; internal set; } + public bool TutorialScene { get; internal set; } + public bool DialogScene { get; internal set; } + public bool ShouldRenderAtDragPos { get; internal set; } + public bool DisableDarken { get; internal set; } + public bool AnimateBack { get; internal set; } + public bool AnimateUpperEffect { get; internal set; } + + public int BaseLayer { get; internal set; } = DefaultBaseLayer; + public int MaxBarLevel { get; internal set; } + + public uint MaxTextLength { get; internal set; } + public int FontPointSize { get; internal set; } + + public float ScaleDuration { get; internal set; } + public float BlinkDuration { get; internal set; } + public float MaxTextWidth { get; internal set; } + public float BlinkRate { get; internal set; } = DefaultBlinkRate; + public float Scale { get; internal set; } = DefaultScale; + public float AnimFps { get; internal set; } + + public Vector2 Size { get; internal set; } + public Vector2 TextOffset { get; internal set; } + public Vector2 TextOffset2 { get; internal set; } + public Vector2 Offset { get; internal set; } + public Vector2 DefaultOffset { get; internal set; } + public Vector2 DefaultOffsetWidescreen { get; internal set; } = DefaultOffsetWidescreenValue; + public Vector2 IconOffset { get; internal set; } + public Vector2 MouseOverOffset { get; internal set; } + public Vector2 DisabledOffset { get; internal set; } + public Vector2 BuildDialOffset { get; internal set; } + public Vector2 BuildDial2Offset { get; internal set; } + public Vector2 LowerEffectOffset { get; internal set; } + public Vector2 UpperEffectOffset { get; internal set; } + public Vector2 OverlayOffset { get; internal set; } + public Vector2 Overlay2Offset { get; internal set; } + + internal override void CoerceValues() + { + base.CoerceValues(); + if (AlternateFontNames.Count == 0) + return; + var newFontNames = new string[AlternateFontNames.Count]; + for (var i = 0; i < AlternateFontNames.Count; i++) + { + var current = AlternateFontNames[i]; + + if (current.AsSpan().IndexOf('_') != -1) + newFontNames[i] = current.Replace('_', ' '); + else + newFontNames[i] = current; + } + AlternateFontNames = new ReadOnlyCollection(newFontNames); + } +} + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameConstants.cs deleted file mode 100644 index a2a4498..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameConstants.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PG.StarWarsGame.Engine.DataTypes; - -public class GameConstants -{ - -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObject.cs deleted file mode 100644 index 6cfd4c0..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObject.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PG.Commons.Hashing; -using PG.StarWarsGame.Files.XML; - -namespace PG.StarWarsGame.Engine.DataTypes; - -public sealed class GameObject : XmlObject -{ - internal GameObject( - string type, - string name, - Crc32 nameCrc, - GameObjectType estimatedType, - IReadOnlyValueListDictionary properties, - XmlLocationInfo location) - : base(name, nameCrc, properties, location) - { - Type = type ?? throw new ArgumentNullException(nameof(type)); - EstimatedType = estimatedType; - } - - public string Type { get; } - - - public GameObjectType EstimatedType { get; } - - /// - /// Gets all model files (including particles) the game object references. - /// - public ISet Models - { - get - { - var models = XmlProperties.AggregateValues - (new HashSet - { - "Galactic_Model_Name", - "Destroyed_Galactic_Model_Name", - "Land_Model_Name", - "Space_Model_Name", - "Model_Name", - "Tactical_Model_Name", - "Galactic_Fleet_Override_Model_Name", - "GUI_Model_Name", - "GUI_Model", - // This can either be a model or a unit reference - "Land_Model_Anim_Override_Name", - "xxxSpace_Model_Name", - "Damaged_Smoke_Asset_Name" - }, v => v.EndsWith(".alo", StringComparison.OrdinalIgnoreCase), - ValueListDictionaryExtensions.AggregateStrategy.LastValuePerKey); - - var terrainMappedModels = LandTerrainModelMapping?.Select(x => x.Model); - if (terrainMappedModels is null) - return new HashSet(models, StringComparer.OrdinalIgnoreCase); - - return new HashSet(models.Concat(terrainMappedModels), StringComparer.OrdinalIgnoreCase); - } - } - - public IList<(string Terrain, string Model)>? LandTerrainModelMapping => - GetLastPropertyOrDefault>("Land_Terrain_Model_Mapping"); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/SfxEvent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/SfxEvent.cs deleted file mode 100644 index 9920449..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/SfxEvent.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Xml.Tags; -using PG.StarWarsGame.Files.XML; - -namespace PG.StarWarsGame.Engine.DataTypes; - -public sealed class SfxEvent : XmlObject -{ - public const byte MaxVolumeValue = 100; - public const byte MaxPitchValue = 200; - public const byte MinPitchValue = 50; - 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; - - // Default values which are not the default value of the type - public const byte DefaultPriority = 3; - public const bool DefaultIs3d = true; - public const byte DefaultProbability = 100; - public const sbyte DefaultPlayCount = 1; - public const sbyte DefaultMaxInstances = 1; - public const byte DefaultMinVolume = 100; - public const byte DefaultMaxVolume = 100; - public const byte DefaultMinPitch = 100; - public const byte DefaultMaxPitch = 100; - public const byte DefaultMinPan2d = 50; - public const byte DefaultMaxPan2d = 50; - public const float DefaultVolumeSaturationDistance = 300.0f; - - private SfxEvent? _preset; - private string? _presetName; - private string? _overlapTestName; - private string? _chainedSfxEvent; - private IReadOnlyList? _textIds; - private IReadOnlyList? _preSamples; - private IReadOnlyList? _samples; - private IReadOnlyList? _postSamples; - private bool? _isPreset; - private bool? _is2D; - private bool? _is3D; - private bool? _isGui; - private bool? _isHudVo; - private bool? _isUnitResponseVo; - private bool? _isAmbientVo; - private bool? _isLocalized; - private bool? _playSequentially; - private bool? _killsPreviousObjectsSfx; - private byte? _priority; - private byte? _probability; - private sbyte? _playCount; - private sbyte? _maxInstances; - private uint? _minPredelay; - private uint? _maxPredelay; - private uint? _minPostdelay; - private uint? _maxPostdelay; - private float? _loopFadeInSeconds; - private float? _loopFadeOutSeconds; - private float? _volumeSaturationDistance; - - private static readonly Func PriorityCoercion = priority => - { - if (!priority.HasValue) - return DefaultPriority; - if (priority < MinPriorityValue) - return MinPriorityValue; - if (priority > MaxPriorityValue) - return MaxPriorityValue; - return priority; - }; - - private static readonly Func MaxInstancesCoercion = maxInstances => - { - if (!maxInstances.HasValue) - return DefaultMaxInstances; - if (maxInstances < MinMaxInstances) - return MinMaxInstances; - return maxInstances; - }; - - private static readonly Func ProbabilityCoercion = probability => - { - if (!probability.HasValue) - return DefaultProbability; - if (probability > MaxProbability) - return MaxProbability; - return probability; - }; - - private static readonly Func PlayCountCoercion = playCount => - { - if (!playCount.HasValue) - return DefaultPlayCount; - if (playCount < InfinitivePlayCount) - return InfinitivePlayCount; - return playCount; - }; - - private static readonly Func LoopAndSaturationCoercion = loopSeconds => - { - if (!loopSeconds.HasValue) - return DefaultVolumeSaturationDistance; - if (loopSeconds < MinLoopSeconds) - return MinLoopSeconds; - return loopSeconds; - }; - - public bool IsPreset => LazyInitValue(ref _isPreset, SfxEventXmlTags.IsPreset, false)!.Value; - - public bool Is3D => LazyInitValue(ref _is3D, SfxEventXmlTags.Is3D, DefaultIs3d)!.Value; - - public bool Is2D => LazyInitValue(ref _is2D, SfxEventXmlTags.Is2D, false)!.Value; - - public bool IsGui => LazyInitValue(ref _isGui, SfxEventXmlTags.IsGui, false)!.Value; - - public bool IsHudVo => LazyInitValue(ref _isHudVo, SfxEventXmlTags.IsHudVo, false)!.Value; - - public bool IsUnitResponseVo => LazyInitValue(ref _isUnitResponseVo, SfxEventXmlTags.IsUnitResponseVo, false)!.Value; - - public bool IsAmbientVo => LazyInitValue(ref _isAmbientVo, SfxEventXmlTags.IsAmbientVo, false)!.Value; - - public bool IsLocalized => LazyInitValue(ref _isLocalized, SfxEventXmlTags.Localize, false)!.Value; - - public SfxEvent? Preset => LazyInitValue(ref _preset, SfxEventXmlTags.PresetXRef, null); - - public string? UsePresetName => LazyInitValue(ref _presetName, SfxEventXmlTags.UsePreset, null); - - public bool PlaySequentially => LazyInitValue(ref _playSequentially, SfxEventXmlTags.PlaySequentially, false)!.Value; - - public IEnumerable AllSamples => PreSamples.Concat(Samples).Concat(PostSamples); - - public IReadOnlyList PreSamples => LazyInitValue(ref _preSamples, SfxEventXmlTags.PreSamples, Array.Empty()); - - public IReadOnlyList Samples => LazyInitValue(ref _samples, SfxEventXmlTags.Samples, Array.Empty()); - - public IReadOnlyList PostSamples => LazyInitValue(ref _postSamples, SfxEventXmlTags.PostSamples, Array.Empty()); - - public IReadOnlyList LocalizedTextIDs => LazyInitValue(ref _textIds, SfxEventXmlTags.TextID, Array.Empty()); - - public byte Priority => LazyInitValue(ref _priority, SfxEventXmlTags.Priority, DefaultPriority, PriorityCoercion)!.Value; - - public byte Probability => LazyInitValue(ref _probability, SfxEventXmlTags.Probability, DefaultProbability, ProbabilityCoercion)!.Value; - - public sbyte PlayCount => LazyInitValue(ref _playCount, SfxEventXmlTags.PlayCount, DefaultPlayCount, PlayCountCoercion)!.Value; - - public float LoopFadeInSeconds => LazyInitValue(ref _loopFadeInSeconds, SfxEventXmlTags.LoopFadeInSeconds, 0f, LoopAndSaturationCoercion)!.Value; - - public float LoopFadeOutSeconds => LazyInitValue(ref _loopFadeOutSeconds, SfxEventXmlTags.LoopFadeOutSeconds, 0f, LoopAndSaturationCoercion)!.Value; - - public sbyte MaxInstances => LazyInitValue(ref _maxInstances, SfxEventXmlTags.MaxInstances, DefaultMaxInstances, MaxInstancesCoercion)!.Value; - - public uint MinPredelay => LazyInitValue(ref _minPredelay, SfxEventXmlTags.MinPredelay, 0u)!.Value; - - public uint MaxPredelay => LazyInitValue(ref _maxPredelay, SfxEventXmlTags.MaxPredelay, 0u)!.Value; - - public uint MinPostdelay => LazyInitValue(ref _minPostdelay, SfxEventXmlTags.MinPostdelay, 0u)!.Value; - - public uint MaxPostdelay => LazyInitValue(ref _maxPostdelay, SfxEventXmlTags.MaxPostdelay, 0u)!.Value; - - public float VolumeSaturationDistance => LazyInitValue(ref _volumeSaturationDistance, - SfxEventXmlTags.VolumeSaturationDistance, DefaultVolumeSaturationDistance, LoopAndSaturationCoercion)!.Value; - - public bool KillsPreviousObjectsSfx => LazyInitValue(ref _killsPreviousObjectsSfx, SfxEventXmlTags.KillsPreviousObjectSFX, false)!.Value; - - public string? OverlapTestName => LazyInitValue(ref _overlapTestName, SfxEventXmlTags.OverlapTest, null); - - public string? ChainedSfxEventName => LazyInitValue(ref _chainedSfxEvent, SfxEventXmlTags.ChainedSfxEvent, null); - - public byte MinVolume { get; } - - public byte MaxVolume { get; } - - public byte MinPitch { get; } - - public byte MaxPitch { get; } - - public byte MinPan2D { get; } - - public byte MaxPan2D { get; } - - internal SfxEvent(string name, Crc32 nameCrc, IReadOnlyValueListDictionary properties, - XmlLocationInfo location) - : base(name, nameCrc, properties, location) - { - var minMaxVolume = GetMinMaxVolume(properties); - MinVolume = minMaxVolume.min; - MaxVolume = minMaxVolume.max; - - var minMaxPitch = GetMinMaxPitch(properties); - MinPitch = minMaxPitch.min; - MaxPitch = minMaxPitch.max; - - var minMaxPan = GetMinMaxPan2d(properties); - MinPan2D = minMaxPan.min; - MaxPan2D = minMaxPan.max; - } - - private static (byte min, byte max) GetMinMaxVolume(IReadOnlyValueListDictionary properties) - { - return GetMinMaxValues(properties, SfxEventXmlTags.MinVolume, SfxEventXmlTags.MaxVolume, DefaultMinVolume, - DefaultMaxVolume, null, MaxVolumeValue); - } - - private static (byte min, byte max) GetMinMaxPitch(IReadOnlyValueListDictionary properties) - { - return GetMinMaxValues(properties, SfxEventXmlTags.MinPitch, SfxEventXmlTags.MaxPitch, DefaultMinPitch, - DefaultMaxPitch, MinPitchValue, MaxPitchValue); - } - - private static (byte min, byte max) GetMinMaxPan2d(IReadOnlyValueListDictionary properties) - { - return GetMinMaxValues(properties, SfxEventXmlTags.MinPan2D, SfxEventXmlTags.MaxPan2D, DefaultMinPan2d, - DefaultMaxPan2d, null, MaxPan2dValue); - } - - - private static (byte min, byte max) GetMinMaxValues( - IReadOnlyValueListDictionary properties, - string minTag, - string maxTag, - byte defaultMin, - byte defaultMax, - byte? totalMinValue, - byte? totalMaxValue) - { - var minValue = !properties.TryGetLastValue(minTag, out var minObj) ? defaultMin : Convert.ToByte(minObj); - var maxValue = !properties.TryGetLastValue(maxTag, out var maxObj) ? defaultMax : Convert.ToByte(maxObj); - - if (totalMaxValue.HasValue) - { - if (minValue > totalMaxValue) - minValue = totalMaxValue.Value; - if (maxValue > totalMaxValue) - maxValue = totalMaxValue.Value; - } - - if (totalMinValue.HasValue) - { - if (minValue < totalMinValue) - minValue = totalMinValue.Value; - if (maxValue < totalMinValue) - maxValue = totalMinValue.Value; - } - - if (minValue > maxValue) - minValue = maxValue; - - if (maxValue < minValue) - maxValue = minValue; - - return (minValue, maxValue); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/XmlObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/XmlObject.cs deleted file mode 100644 index b8161b1..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/XmlObject.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using PG.Commons.DataTypes; -using PG.Commons.Hashing; -using PG.StarWarsGame.Files.XML; - -namespace PG.StarWarsGame.Engine.DataTypes; - -public abstract class XmlObject( - string name, - Crc32 nameCrc, - IReadOnlyValueListDictionary properties, - XmlLocationInfo location) - : IHasCrc32 -{ - public XmlLocationInfo Location { get; } = location; - - public Crc32 Crc32 { get; } = nameCrc; - - public IReadOnlyValueListDictionary XmlProperties { get; } = properties ?? throw new ArgumentNullException(nameof(properties)); - - public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name)); - - public T? GetLastPropertyOrDefault(string tagName, T? defaultValue = default) - { - if (!XmlProperties.TryGetLastValue(tagName, out var value)) - return defaultValue; - return (T)value; - } - - protected T LazyInitValue(ref T? field, string tag, T defaultValue, Func? coerceFunc = null) - { - if (field is null) - { - if (XmlProperties.TryGetLastValue(tag, out var value)) - { - var tValue = (T)value; - if (coerceFunc is not null) - tValue = coerceFunc(tValue); - field = tValue; - } - else - field = defaultValue; - } - - return field; - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs new file mode 100644 index 0000000..d123437 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs @@ -0,0 +1,12 @@ +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +public abstract class DatabaseErrorListener : IDatabaseErrorListener +{ + public virtual void OnXmlError(XmlError error) + { + } + + public virtual void OnInitializationError(InitializationError error) + { + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs new file mode 100644 index 0000000..b68cfe8 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs @@ -0,0 +1,61 @@ +using System; +using AnakinRaW.CommonUtilities; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; + +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +internal class DatabaseErrorListenerWrapper : DisposableObject, IDatabaseErrorListener, IXmlParserErrorListener +{ + internal event EventHandler? InitializationError; + + private readonly IDatabaseErrorListener? _errorListener; + private IPrimitiveXmlErrorParserProvider? _primitiveXmlParserErrorProvider; + + public DatabaseErrorListenerWrapper(IDatabaseErrorListener? errorListener, IServiceProvider serviceProvider) + { + _errorListener = errorListener; + if (_errorListener is null) + return; + _primitiveXmlParserErrorProvider = serviceProvider.GetRequiredService(); + _primitiveXmlParserErrorProvider.XmlParseError += ((IXmlParserErrorListener)this).OnXmlParseError; + } + + public void OnXmlError(XmlError error) + { + _errorListener?.OnXmlError(error); + } + + public void OnInitializationError(InitializationError error) + { + InitializationError?.Invoke(this, error); + if (_errorListener is null) + return; + _errorListener.OnInitializationError(error); + } + + protected override void DisposeResources() + { + base.DisposeResources(); + if (_primitiveXmlParserErrorProvider is null) + return; + _primitiveXmlParserErrorProvider.XmlParseError -= ((IXmlParserErrorListener)this).OnXmlParseError; + _primitiveXmlParserErrorProvider = null!; + } + + void IXmlParserErrorListener.OnXmlParseError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) + { + if (_errorListener is null) + return; + + OnXmlError(new XmlError + { + FileLocation = error.Location, + Parser = parser.ToString(), + Message = error.Message, + ErrorKind = error.ErrorKind, + Element = error.Element + }); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs new file mode 100644 index 0000000..2df54c5 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs @@ -0,0 +1,7 @@ +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +public interface IDatabaseErrorListener +{ + void OnXmlError(XmlError error); + void OnInitializationError(InitializationError error); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs new file mode 100644 index 0000000..55262ea --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs @@ -0,0 +1,8 @@ +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +public sealed class InitializationError +{ + public required string GameManager { get; init; } + + public required string Message { get; init; } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs new file mode 100644 index 0000000..60b16bd --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs @@ -0,0 +1,18 @@ +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +public sealed class XmlError +{ + public required XmlLocationInfo FileLocation { get; init; } + + public required string 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/Database/GameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs index f32f347..a72feb1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs @@ -1,19 +1,27 @@ using System.Collections.Generic; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Engine.Language; -using PG.StarWarsGame.Engine.Repositories; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.CommandBar; +using PG.StarWarsGame.Engine.GameConstants; +using PG.StarWarsGame.Engine.GameObjects; +using PG.StarWarsGame.Engine.GuiDialog; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Localization; namespace PG.StarWarsGame.Engine.Database; internal class GameDatabase : IGameDatabase { + public required ICommandBarGameManager CommandBarManager { get; init; } + public required IGameRepository GameRepository { get; init; } - public required GameConstants GameConstants { get; init; } + public required IGameConstants GameConstants { get; init; } + + public required IGuiDialogManager GuiDialogManager { get; init; } - public required IXmlDatabase GameObjects { get; init; } + public required IGameObjectTypeGameManager GameObjectTypeManager { get; init; } - public required IXmlDatabase SfxEvents { get; init; } + public required ISfxEventGameManager SfxGameManager { get; init; } - public IEnumerable InstalledLanguages { get; init; } + public required IEnumerable InstalledLanguages { get; init; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs index fc1f5ca..e2122bf 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs @@ -1,51 +1,24 @@ using System; using System.Threading; using System.Threading.Tasks; -using AnakinRaW.CommonUtilities; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database.Initialization; -using PG.StarWarsGame.Engine.Repositories; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; namespace PG.StarWarsGame.Engine.Database; -internal class GameDatabaseService : DisposableObject, IXmlParserErrorListener, IGameDatabaseService +internal class GameDatabaseService(IServiceProvider serviceProvider) : IGameDatabaseService { - public event XmlErrorEventHandler? XmlParseError; - - private readonly IServiceProvider _serviceProvider; - private IPrimitiveXmlErrorParserProvider _primitiveXmlParserErrorProvider; - - public GameDatabaseService(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - _primitiveXmlParserErrorProvider = serviceProvider.GetRequiredService(); - _primitiveXmlParserErrorProvider.XmlParseError += OnXmlParseError; - } - - public async Task CreateDatabaseAsync( - GameEngineType targetEngineType, - GameLocations locations, + public Task InitializeGameAsync( + GameInitializationOptions gameInitializationOptions, CancellationToken cancellationToken = default) { - var repoFactory = _serviceProvider.GetRequiredService(); - var repository = repoFactory.Create(targetEngineType, locations, this); + var repoFactory = serviceProvider.GetRequiredService(); - var pipeline = new GameDatabaseCreationPipeline(repository, this, _serviceProvider); - await pipeline.RunAsync(cancellationToken); - return pipeline.GameDatabase; - } + using var errorListenerWrapper = new DatabaseErrorListenerWrapper(gameInitializationOptions.ErrorListener, serviceProvider); + var repository = repoFactory.Create(gameInitializationOptions.TargetEngineType, gameInitializationOptions.Locations, errorListenerWrapper); - protected override void DisposeManagedResources() - { - base.DisposeManagedResources(); - _primitiveXmlParserErrorProvider.XmlParseError -= OnXmlParseError; - _primitiveXmlParserErrorProvider = null!; - } - - public void OnXmlParseError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) - { - XmlParseError?.Invoke(parser, error); + var gameInitializer = new GameInitializer(repository, gameInitializationOptions.CancelOnError, serviceProvider); + return gameInitializer.InitializeAsync(errorListenerWrapper, cancellationToken); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs new file mode 100644 index 0000000..8c11841 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs @@ -0,0 +1,14 @@ +using PG.StarWarsGame.Engine.Database.ErrorReporting; + +namespace PG.StarWarsGame.Engine.Database; + +public class GameInitializationOptions +{ + public required GameEngineType TargetEngineType { get; init; } + + public required GameLocations Locations { get; init; } + + public bool CancelOnError { get; init; } + + public IDatabaseErrorListener? ErrorListener { get; init; } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs new file mode 100644 index 0000000..7ad3ee9 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -0,0 +1,109 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.CommandBar; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.GameObjects; +using PG.StarWarsGame.Engine.GuiDialog; +using PG.StarWarsGame.Engine.IO.Repositories; + +namespace PG.StarWarsGame.Engine.Database; + +internal class GameInitializer(GameRepository repository, bool cancelOnError, IServiceProvider serviceProvider) +{ + private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(GameInitializer)); + + private CancellationTokenSource? _cancellationTokenSource; + + public async Task InitializeAsync(DatabaseErrorListenerWrapper errorListener, CancellationToken token) + { + _logger?.LogInformation("Initializing Game Database..."); + errorListener.InitializationError += OnInitializationError; + + _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); + + try + { + // LensFlares.xml + // SurfaceFX.xml + // TerrainDecalFX.xml + // GraphicDetails.xml + // DynamicTrackFX.xml + // ShadowBlobMaterials.xml + // TacticalCameras.xml + // LightSources.xml + // StarWars3DTextCrawl.xml + // MusicEvents.xml + // SpeechEvents.xml + // GameConstantsXml.xml + // Audio.xml + // WeatherAudio.xml + // HeroClash.xml + // TradeRouteLines.xml + // RadarMap.xml + // WeatherModifiers.xml + // Movies.xml + // LightningEffectTypes.xml + // DifficultyAdjustments.xml + // WeatherScenarios.xml + // UnitAbilityTypes.xml + // BlackMarketItems.xml + // MovementClassTypeDefs.xml + // AITerrainEffectiveness.xml + + + // CONTAINER FILES: + // GameObjectFiles.xml + // TradeRouteFiles.xml + // HardPointDataFiles.xml + // CampaignFiles.xml + // FactionFiles.xml + // TargetingPrioritySetFiles.xml + // MousePointerFiles.xml + + var gameConstants = new GameConstants.GameConstants(repository, errorListener, serviceProvider); + await gameConstants.InitializeAsync( _cancellationTokenSource.Token); + + var guiDialogs = new GuiDialogGameManager(repository, errorListener, serviceProvider); + await guiDialogs.InitializeAsync(_cancellationTokenSource.Token); + + var sfxGameManager = new SfxEventGameManager(repository, errorListener, serviceProvider); + await sfxGameManager.InitializeAsync( _cancellationTokenSource.Token); + + var commandBarManager = new CommandBarGameManager(repository, errorListener, serviceProvider); + await commandBarManager.InitializeAsync( _cancellationTokenSource.Token); + + var gameObjetTypeManager = new GameObjectTypeTypeGameManager(repository, errorListener, serviceProvider); + await gameObjetTypeManager.InitializeAsync( _cancellationTokenSource.Token); + + repository.Seal(); + + return new GameDatabase + { + GameRepository = repository, + GameConstants = gameConstants, + GuiDialogManager = guiDialogs, + CommandBarManager = commandBarManager, + GameObjectTypeManager = gameObjetTypeManager, + SfxGameManager = sfxGameManager, + InstalledLanguages = sfxGameManager.InstalledLanguages + }; + } + finally + { + errorListener.InitializationError -= OnInitializationError; + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + _logger?.LogInformation("Finished initializing game database"); + } + } + + private void OnInitializationError(object sender, InitializationError e) + { + if(cancelOnError) + _cancellationTokenSource?.Cancel(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs new file mode 100644 index 0000000..3e1c18d --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Threading; +using System.Threading.Tasks; +using AnakinRaW.CommonUtilities.Collections; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.Commons.Collections; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; + +namespace PG.StarWarsGame.Engine.Database; + +internal abstract class GameManagerBase(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorListener, serviceProvider), IGameManager +{ + protected readonly ValueListDictionary NamedEntries = new(); + + public ICollection Entries => NamedEntries.Values; + + public ICollection EntryKeys => NamedEntries.Keys; + + public ReadOnlyFrugalList GetEntries(Crc32 key) + { + return NamedEntries.GetValues(key); + } +} + +internal abstract class GameManagerBase +{ + public event EventHandler? Initialized; + + private bool _initialized; + private protected readonly GameRepository GameRepository; + protected readonly IServiceProvider ServiceProvider; + protected readonly IFileSystem FileSystem; + protected readonly ILogger? Logger; + + protected readonly DatabaseErrorListenerWrapper ErrorListener; + + public bool IsInitialized => _initialized; + + protected GameManagerBase(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + { + GameRepository = repository ?? throw new ArgumentNullException(nameof(repository)); + ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + Logger = serviceProvider.GetService()?.CreateLogger(GetType()); + FileSystem = serviceProvider.GetRequiredService(); + ErrorListener = errorListener ?? throw new ArgumentNullException(nameof(errorListener)); + } + + public async Task InitializeAsync(CancellationToken token) + { + ThrowIfAlreadyInitialized(); + token.ThrowIfCancellationRequested(); + try + { + await InitializeCoreAsync(token); + _initialized = true; + } + catch (Exception e) + { + Logger?.LogError(e, $"Initialization of {this} failed: {e.Message}"); + throw; + } + OnInitialized(); + } + + public override string ToString() + { + return GetType().Name; + } + + protected abstract Task InitializeCoreAsync(CancellationToken token); + + protected void ThrowIfAlreadyInitialized() + { + if (_initialized) + throw new InvalidOperationException("Game manager is already initialized."); + } + + protected void ThrowIfNotInitialized() + { + if (!_initialized) + throw new InvalidOperationException("Game manager not initialized."); + } + + private void OnInitialized() + { + Initialized?.Invoke(this, EventArgs.Empty); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs index 15dd8a1..087b7f8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Engine.Language; -using PG.StarWarsGame.Engine.Repositories; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.GameConstants; +using PG.StarWarsGame.Engine.GameObjects; +using PG.StarWarsGame.Engine.GuiDialog; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Localization; namespace PG.StarWarsGame.Engine.Database; @@ -9,11 +12,13 @@ public interface IGameDatabase { IGameRepository GameRepository { get; } - GameConstants GameConstants { get; } + IGameConstants GameConstants { get; } - IXmlDatabase GameObjects { get; } + IGuiDialogManager GuiDialogManager { get; } - IXmlDatabase SfxEvents { get; } + IGameObjectTypeGameManager GameObjectTypeManager { get; } + + ISfxEventGameManager SfxGameManager { get; } IEnumerable InstalledLanguages { get; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs index 575c521..46cfda2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs @@ -1,14 +1,9 @@ -using System; -using System.Threading; +using System.Threading; using System.Threading.Tasks; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Engine.Database; -public interface IGameDatabaseService : IXmlParserErrorProvider, IDisposable +public interface IGameDatabaseService { - Task CreateDatabaseAsync( - GameEngineType targetEngineType, - GameLocations locations, - CancellationToken cancellationToken = default); + Task InitializeGameAsync(GameInitializationOptions gameInitializationOptions, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IXmlDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameManager.cs similarity index 75% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IXmlDatabase.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameManager.cs index c2edb8e..f331a24 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IXmlDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameManager.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; namespace PG.StarWarsGame.Engine.Database; -public interface IXmlDatabase where T : XmlObject +public interface IGameManager { ICollection Entries { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/CreateDatabaseStep.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/CreateDatabaseStep.cs deleted file mode 100644 index 2c1f20e..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/CreateDatabaseStep.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Threading; -using AnakinRaW.CommonUtilities.SimplePipeline.Steps; -using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.Repositories; - -namespace PG.StarWarsGame.Engine.Database.Initialization; - -internal abstract class CreateDatabaseStep(IGameRepository repository, IServiceProvider serviceProvider) - : PipelineStep(serviceProvider) where T : class -{ - public T Database { get; private set; } = null!; - - protected abstract string Name { get; } - - protected IGameRepository GameRepository { get; } = repository; - - protected sealed override void RunCore(CancellationToken token) - { - Logger?.LogDebug($"Creating {Name} database..."); - Database = CreateDatabase(); - } - - protected abstract T CreateDatabase(); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/GameDatabaseCreationPipeline.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/GameDatabaseCreationPipeline.cs deleted file mode 100644 index 05f6e21..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/GameDatabaseCreationPipeline.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AnakinRaW.CommonUtilities.SimplePipeline; -using AnakinRaW.CommonUtilities.SimplePipeline.Runners; -using AnakinRaW.CommonUtilities.SimplePipeline.Steps; -using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Engine.Repositories; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Engine.Database.Initialization; - -internal class GameDatabaseCreationPipeline(GameRepository repository, IXmlParserErrorListener xmlParserErrorListener, IServiceProvider serviceProvider) - : Pipeline(serviceProvider) -{ - private ParseSingletonXmlStep _parseGameConstants = null!; - private ParseXmlDatabaseFromContainerStep _parseGameObjects = null!; - private ParseXmlDatabaseFromContainerStep _parseSfxEvents = null!; - - private StepRunner _parseXmlRunner = null!; - - public GameDatabase GameDatabase { get; private set; } = null!; - - protected override Task PrepareCoreAsync() - { - _parseXmlRunner = new ParallelRunner(4, ServiceProvider); - foreach (var xmlParserStep in CreateXmlParserSteps()) - _parseXmlRunner.AddStep(xmlParserStep); - - return Task.FromResult(true); - } - - private IEnumerable CreateXmlParserSteps() - { - // TODO: Use same load order as the engine! - - yield return _parseGameConstants = new ParseSingletonXmlStep("GameConstants", - "DATA\\XML\\GAMECONSTANTS.XML", repository, xmlParserErrorListener, ServiceProvider); - - yield return _parseGameObjects = new ParseXmlDatabaseFromContainerStep("GameObjects", - "DATA\\XML\\GAMEOBJECTFILES.XML", repository, xmlParserErrorListener, ServiceProvider); - - yield return _parseSfxEvents = new ParseXmlDatabaseFromContainerStep("SFXEvents", - "DATA\\XML\\SFXEventFiles.XML", repository, xmlParserErrorListener, ServiceProvider); - - // GUIDialogs.xml - // LensFlares.xml - // SurfaceFX.xml - // TerrainDecalFX.xml - // GraphicDetails.xml - // DynamicTrackFX.xml - // ShadowBlobMaterials.xml - // TacticalCameras.xml - // LightSources.xml - // StarWars3DTextCrawl.xml - // MusicEvents.xml - // SpeechEvents.xml - // GameConstants.xml - // Audio.xml - // WeatherAudio.xml - // HeroClash.xml - // TradeRouteLines.xml - // RadarMap.xml - // WeatherModifiers.xml - // Movies.xml - // LightningEffectTypes.xml - // DifficultyAdjustments.xml - // WeatherScenarios.xml - // UnitAbilityTypes.xml - // BlackMarketItems.xml - // MovementClassTypeDefs.xml - // AITerrainEffectiveness.xml - - - // CONTAINER FILES: - // GameObjectFiles.xml - // CommandBarComponentFiles.xml - // TradeRouteFiles.xml - // HardPointDataFiles.xml - // CampaignFiles.xml - // FactionFiles.xml - // TargetingPrioritySetFiles.xml - // MousePointerFiles.xml - } - - protected override async Task RunCoreAsync(CancellationToken token) - { - Logger?.LogInformation("Creating Game Database..."); - - try - { - try - { - Logger?.LogInformation("Parsing XML Files..."); - _parseXmlRunner.Error += OnError; - await _parseXmlRunner.RunAsync(token); - } - finally - { - Logger?.LogInformation("Finished parsing XML Files"); - _parseXmlRunner.Error -= OnError; - } - - ThrowIfAnyStepsFailed(_parseXmlRunner.Steps); - - token.ThrowIfCancellationRequested(); - - - Logger?.LogInformation("Initializing Language files..."); - var installedLanguages = repository.InitializeInstalledSfxMegFiles(); - Logger?.LogInformation("Finished initializing Language files"); - - - repository.Seal(); - - GameDatabase = new GameDatabase - { - GameRepository = repository, - GameConstants = _parseGameConstants.Database, - GameObjects = _parseGameObjects.Database, - SfxEvents = _parseSfxEvents.Database, - InstalledLanguages = installedLanguages - }; - } - finally - { - Logger?.LogInformation("Finished creating game database"); - } - } - - private sealed class ParseSingletonXmlStep(string name, string xmlFile, IGameRepository repository, IXmlParserErrorListener? listener, IServiceProvider serviceProvider) - : ParseXmlDatabaseStep(xmlFile, repository, listener, serviceProvider) where T : class - { - protected override string Name => name; - - protected override T CreateDatabase(IList parsedDatabaseEntries) - { - if (parsedDatabaseEntries.Count != 1) - throw new InvalidOperationException($"There can be only one {Name} model."); - - return parsedDatabaseEntries.First(); - } - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseFromContainerStep.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseFromContainerStep.cs deleted file mode 100644 index 5dcde76..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseFromContainerStep.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.IO.Abstractions; -using System.Linq; -using System.Xml; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Engine.Repositories; -using PG.StarWarsGame.Engine.Xml; -using PG.StarWarsGame.Files.XML; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Engine.Database.Initialization; - -internal class ParseXmlDatabaseFromContainerStep( - string name, - string xmlFile, - IGameRepository repository, - IXmlParserErrorListener? listener, - IServiceProvider serviceProvider) - : CreateDatabaseStep>(repository, serviceProvider) - where T : XmlObject -{ - private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - - protected readonly IPetroglyphXmlFileParserFactory FileParserFactory = serviceProvider.GetRequiredService(); - - protected override string Name => name; - - protected sealed override IXmlDatabase CreateDatabase() - { - using var containerStream = GameRepository.OpenFile(xmlFile); - var containerParser = FileParserFactory.GetFileParser(listener); - Logger?.LogDebug($"Parsing container data '{xmlFile}'"); - var container = containerParser.ParseFile(containerStream); - - var xmlFiles = container.Files.Select(x => _fileSystem.Path.Combine("DATA\\XML", x)).ToList(); - - var parsedEntries = new ValueListDictionary(); - - foreach (var file in xmlFiles) - { - using var fileStream = GameRepository.TryOpenFile(file); - - var parser = FileParserFactory.GetFileParser(listener); - - if (fileStream is null) - { - listener?.OnXmlParseError(parser, XmlParseErrorEventArgs.FromMissingFile(file)); - Logger?.LogWarning($"Could not find XML file '{file}'"); - continue; - } - - Logger?.LogDebug($"Parsing File '{file}'"); - - try - { - parser.ParseFile(fileStream, parsedEntries); - } - catch (XmlException e) - { - listener?.OnXmlParseError(parser, new XmlParseErrorEventArgs(file, null, XmlParseErrorKind.Unknown, e.Message)); - } - } - - return new XmlDatabase(parsedEntries, Services); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseStep.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseStep.cs deleted file mode 100644 index 8050f02..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/Initialization/ParseXmlDatabaseStep.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.Repositories; -using PG.StarWarsGame.Engine.Xml; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Engine.Database.Initialization; - -internal abstract class ParseXmlDatabaseStep( - IList xmlFiles, - IGameRepository repository, - IXmlParserErrorListener? listener, - IServiceProvider serviceProvider) - : CreateDatabaseStep(repository, serviceProvider) - where T : class -{ - protected readonly IPetroglyphXmlFileParserFactory FileParserFactory = serviceProvider.GetRequiredService(); - - protected ParseXmlDatabaseStep(string xmlFile, IGameRepository repository, IXmlParserErrorListener? listener, IServiceProvider serviceProvider) - : this([xmlFile], repository, listener, serviceProvider) - { - } - - protected sealed override T CreateDatabase() - { - var parsedDatabaseEntries = new List(); - foreach (var xmlFile in xmlFiles) - { - using var fileStream = GameRepository.OpenFile(xmlFile); - - var parser = FileParserFactory.GetFileParser(listener); - Logger?.LogDebug($"Parsing File '{xmlFile}'"); - var parsedData = parser.ParseFile(fileStream)!; - parsedDatabaseEntries.Add(parsedData); - } - return CreateDatabase(parsedDatabaseEntries); - } - - protected abstract T CreateDatabase(IList parsedDatabaseEntries); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/XmlDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/XmlDatabase.cs deleted file mode 100644 index 38d0a78..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/XmlDatabase.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using AnakinRaW.CommonUtilities.Collections; -using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Files.XML; - -namespace PG.StarWarsGame.Engine.Database; - -internal class XmlDatabase(IReadOnlyValueListDictionary parsedObjects, IServiceProvider serviceProvider) : IXmlDatabase - where T : XmlObject -{ - - private readonly IServiceProvider _serviceProvider = - serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - - private readonly IReadOnlyValueListDictionary _parsedObjects = parsedObjects ?? throw new ArgumentNullException(nameof(parsedObjects)); - - public ICollection Entries => _parsedObjects.Values; - - public ICollection EntryKeys => _parsedObjects.Keys; - - public ReadOnlyFrugalList GetEntries(Crc32 key) - { - return _parsedObjects.GetValues(key); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/FocHardcodedConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/FocHardcodedConstants.cs index 72996da..d7eb4af 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/FocHardcodedConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/FocHardcodedConstants.cs @@ -5,7 +5,7 @@ namespace PG.StarWarsGame.Engine; public static class FocHardcodedConstants { /// - /// These models are hardcoded into StarWarsG.exe. + /// These models / particles are hardcoded into StarWarsG.exe. /// public static IList HardcodedModels { get; } = new List { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs new file mode 100644 index 0000000..508323c --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; + +namespace PG.StarWarsGame.Engine.GameConstants; + +internal class GameConstants(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorListener, serviceProvider), IGameConstants +{ + protected override Task InitializeCoreAsync(CancellationToken token) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs new file mode 100644 index 0000000..e98ed52 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstantsXml.cs @@ -0,0 +1,3 @@ +namespace PG.StarWarsGame.Engine.GameConstants; + +public class GameConstantsXml; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/IGameConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/IGameConstants.cs new file mode 100644 index 0000000..552e406 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/IGameConstants.cs @@ -0,0 +1,3 @@ +namespace PG.StarWarsGame.Engine.GameConstants; + +public interface IGameConstants; \ 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 new file mode 100644 index 0000000..de7ed01 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.GameObjects; + +public sealed class GameObject : NamedXmlObject +{ + internal GameObject(string type, string name, Crc32 nameCrc, GameObjectType estimatedType, XmlLocationInfo location) + : base(name, nameCrc, location) + { + Type = type ?? throw new ArgumentNullException(nameof(type)); + EstimatedType = estimatedType; + LandTerrainModelMapping = new ReadOnlyDictionary(InternalLandTerrainModelMapping); + } + + public string Type { get; } + + public GameObjectType EstimatedType { get; } + + public string? GalacticModel { get; internal set; } + + public string? DestroyedGalacticModel { get; internal set; } + + public string? LandModel { get; internal set; } + + public string? SpaceModel { get; internal set; } + + public string? TacticalModel { get; internal set; } + + public string? GalacticFleetOverrideModel { get; internal set; } + + public string? GuiModel { get; internal set; } + + public string? ModelName { get; internal set; } + + public string? LandAnimOverrideModel { get; internal set; } + + public string? XxxSpaceModeModel { get; internal set; } + + public string? DamagedSmokeAssetModel { get; internal set; } + + public IReadOnlyDictionary LandTerrainModelMapping { get; } + + internal Dictionary InternalLandTerrainModelMapping { get; } = new(StringComparer.OrdinalIgnoreCase); + + /// + /// Gets all model files (including particles) the game object references. + /// + public IEnumerable Models + { + get + { + var models = new HashSet(StringComparer.OrdinalIgnoreCase); + AddNotEmpty(models, GalacticModel); + AddNotEmpty(models, DestroyedGalacticModel); + AddNotEmpty(models, LandModel); + AddNotEmpty(models, SpaceModel); + AddNotEmpty(models, TacticalModel); + AddNotEmpty(models, GalacticFleetOverrideModel); + AddNotEmpty(models, GuiModel); + AddNotEmpty(models, ModelName); + AddNotEmpty(models, LandAnimOverrideModel, s => s.EndsWith(".alo", StringComparison.OrdinalIgnoreCase)); + AddNotEmpty(models, XxxSpaceModeModel); + AddNotEmpty(models, DamagedSmokeAssetModel); + foreach (var model in InternalLandTerrainModelMapping.Values) + models.Add(model); + + return models; + } + + } + + private static void AddNotEmpty(ISet set, string? value, Predicate? predicate = null) + { + if (value is null) + return; + if (predicate is null || predicate(value)) + set.Add(value); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObjectType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectType.cs similarity index 95% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObjectType.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectType.cs index 116fa29..0b84517 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/DataTypes/GameObjectType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectType.cs @@ -1,4 +1,4 @@ -namespace PG.StarWarsGame.Engine.DataTypes; +namespace PG.StarWarsGame.Engine.GameObjects; public enum GameObjectType { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs new file mode 100644 index 0000000..8b0abb1 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Xml.Parsers; + +namespace PG.StarWarsGame.Engine.GameObjects; + +internal class GameObjectTypeTypeGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorListener, serviceProvider), IGameObjectTypeGameManager +{ + protected override async Task InitializeCoreAsync(CancellationToken token) + { + Logger?.LogInformation("Parsing GameObjects..."); + + var contentParser = ServiceProvider.GetRequiredService(); + + await Task.Run(() => contentParser.ParseEntriesFromContainerXml( + "DATA\\XML\\GAMEOBJECTFILES.XML", + ErrorListener, + GameRepository, + ".\\DATA\\XML", + NamedEntries, + VerifyFilePathLength), token); + } + + private void VerifyFilePathLength(string filePath) + { + if (filePath.Length > PGConstants.MaxGameObjectDatabaseFileName) + { + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = $"Game object file '{filePath}' is longer than {PGConstants.MaxGameObjectDatabaseFileName} characters." + }); + } + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs new file mode 100644 index 0000000..ae22ca1 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs @@ -0,0 +1,5 @@ +using PG.StarWarsGame.Engine.Database; + +namespace PG.StarWarsGame.Engine.GameObjects; + +public interface IGameObjectTypeGameManager : IGameManager; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/ComponentTextureEntry.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/ComponentTextureEntry.cs new file mode 100644 index 0000000..f1ffb04 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/ComponentTextureEntry.cs @@ -0,0 +1,10 @@ +namespace PG.StarWarsGame.Engine.GuiDialog; + +public readonly struct ComponentTextureEntry(GuiComponentType componentType, string texture, bool isOverride) +{ + public string Texture { get; } = texture; + + public GuiComponentType ComponentType { get; } = componentType; + + public bool IsOverride { get; } = isOverride; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiComponentType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiComponentType.cs new file mode 100644 index 0000000..548cfee --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiComponentType.cs @@ -0,0 +1,93 @@ +namespace PG.StarWarsGame.Engine.GuiDialog; + +public enum GuiComponentType +{ + ButtonLeft = 0x0, + ButtonMiddle = 0x1, + ButtonRight = 0x2, + ButtonLeftMouseOver = 0x3, + ButtonMiddleMouseOver = 0x4, + ButtonRightMouseOver = 0x5, + ButtonLeftPressed = 0x6, + ButtonMiddlePressed = 0x7, + ButtonRightPressed = 0x8, + ButtonLeftDisabled = 0x9, + ButtonMiddleDisabled = 0xA, + ButtonRightDisabled = 0xB, + CheckOff = 0xC, + CheckOn = 0xD, + DialLeft = 0xE, + DialMiddle = 0xF, + DialRight = 0x10, + DialPlus = 0x11, + DialPlusMouseOver = 0x12, + DialPlusPressed = 0x13, + DialMinus = 0x14, + DialMinusMouseOver = 0x15, + DialMinusPressed = 0x16, + DialTab = 0x17, + FrameBottom = 0x18, + FrameBottomLeft = 0x19, + FrameBottomRight = 0x1A, + FrameBackground = 0x1B, + FrameLeft = 0x1C, + FrameRight = 0x1D, + FrameTop = 0x1E, + FrameTopLeft = 0x1F, + FrameTopRight = 0x20, + FrameTopTransitionLeft = 0x21, + FrameTopTransitionRight = 0x22, + FrameBottomTransitionLeft = 0x23, + FrameBottomTransitionRight = 0x24, + FrameLeftTransitionTop = 0x25, + FrameLeftTransitionBottom = 0x26, + FrameRightTransitionTop = 0x27, + FrameRightTransitionBottom = 0x28, + RadioOff = 0x29, + RadioOn = 0x2A, + RadioDisabled = 0x2B, + RadioMouseOver = 0x2C, + ScrollDownButton = 0x2D, + ScrollDownButtonPressed = 0x2E, + ScrollDownButtonMouseOver = 0x2F, + ScrollMiddle = 0x30, + ScrollTab = 0x31, + ScrollUpButton = 0x32, + ScrollUpButtonPressed = 0x33, + ScrollUpButtonMouseOver = 0x34, + ScrollUpButtonDisabled = 0x35, + ScrollDownButtonDisabled = 0x36, + ScrollMiddleDisabled = 0x37, + ScrollTabDisabled = 0x38, + TrackbarScrollDownButton = 0x39, + TrackbarScrollDownButtonPressed = 0x3A, + TrackbarScrollDownButtonMouseOver = 0x3B, + TrackbarScrollMiddle = 0x3C, + TrackbarScrollTab = 0x3D, + TrackbarScrollUpButton = 0x3E, + TrackbarScrollUpButtonPressed = 0x3F, + TrackbarScrollUpButtonMouseOver = 0x40, + TrackbarScrollUpButtonDisabled = 0x41, + TrackbarScrollDownButtonDisabled = 0x42, + TrackbarScrollMiddleDisabled = 0x43, + TrackbarScrollTabDisabled = 0x44, + SmallFrameBottom = 0x45, + SmallFrameBottomLeft = 0x46, + SmallFrameBottomRight = 0x47, + SmallFrameMiddleLeft = 0x48, + SmallFrameMiddleRight = 0x49, + SmallFrameTop = 0x4A, + SmallFrameTopLeft = 0x4B, + SmallFrameTopRight = 0x4C, + SmallFrameBackground = 0x4D, + ComboboxPopdown = 0x4E, + ComboboxPopdownMouseOver = 0x4F, + ComboboxPopdownPressed = 0x50, + ComboboxTextBox = 0x51, + ComboboxLeftCap = 0x52, + ProgressLeft = 0x53, + ProgressMiddleOff = 0x54, + ProgressMiddleOn = 0x55, + ProgressRight = 0x56, + Scanlines = 0x57, +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs new file mode 100644 index 0000000..5f20f0f --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.GuiDialog.Xml; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Files.MTD.Binary; +using PG.StarWarsGame.Files.MTD.Files; +using PG.StarWarsGame.Files.MTD.Services; + +namespace PG.StarWarsGame.Engine.GuiDialog; + +internal partial class GuiDialogGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorListener, serviceProvider), IGuiDialogManager +{ + 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 IMtdFile? _megaTexture; + private GuiDialogsXml? _guiDialogsXml; + private string? _megaTextureFileName; + private bool _megaTextureExists; + + + public IMtdFile? MtdFile + { + get + { + ThrowIfNotInitialized(); + return _megaTexture; + } + private set + { + ThrowIfAlreadyInitialized(); + _megaTexture = value; + } + } + + public GuiDialogsXml? GuiDialogsXml + { + get + { + ThrowIfNotInitialized(); + return _guiDialogsXml; + } + private set + { + ThrowIfAlreadyInitialized(); + _guiDialogsXml = value; + } + } + + public IReadOnlyCollection Components + { + get + { + ThrowIfNotInitialized(); + return _perComponentTextures.Keys; + } + } + + public IReadOnlyDictionary DefaultTextureEntries + { + get + { + ThrowIfNotInitialized(); + return _defaultTexturesRo; + } + } + + + public IReadOnlyDictionary GetTextureEntries(string component, out bool componentExist) + { + if (!_perComponentTextures.TryGetValue(component, out var textures)) + { + Logger?.LogDebug($"The component '{component}' has no overrides. Using default textures."); + componentExist = false; + return DefaultTextureEntries; + } + + componentExist = true; + return new ReadOnlyDictionary(textures); + } + + public bool TryGetTextureEntry(string component, GuiComponentType key, out ComponentTextureEntry texture) + { + if (!_perComponentTextures.TryGetValue(component, out var textures)) + { + Logger?.LogDebug($"The component '{component}' has no overrides. Using default textures."); + textures = _defaultTextures; + } + + return textures.TryGetValue(key, out texture); + } + + public bool TextureExists( + in ComponentTextureEntry textureInfo, + out GuiTextureOrigin textureOrigin, + out bool isNone, + bool buttonMiddleInRepoMode = false) + { + if (textureInfo.Texture == "none") + { + textureOrigin = default; + isNone = true; + return false; + } + + isNone = false; + + // Apparently, Scanlines only use the repository and not the MTD. + if (textureInfo.ComponentType == GuiComponentType.Scanlines) + { + textureOrigin = GuiTextureOrigin.Repository; + return GameRepository.TextureRepository.FileExists(textureInfo.Texture); + } + + // The engine uses ButtonMiddle to switch to the special button mode. + // It searches first in the repo and then falls back to MTD + // (but only for this very type; the variants do not fallback to MTD). + if (textureInfo.ComponentType == GuiComponentType.ButtonMiddle) + { + if (GameRepository.TextureRepository.FileExists(textureInfo.Texture)) + { + textureOrigin = GuiTextureOrigin.Repository; + return true; + } + } + + // The engine does not fallback to MTD once it is in this special Button mode. + if (buttonMiddleInRepoMode && textureInfo.ComponentType is + GuiComponentType.ButtonMiddleDisabled or + GuiComponentType.ButtonMiddleMouseOver or + GuiComponentType.ButtonMiddlePressed) + { + textureOrigin = GuiTextureOrigin.Repository; + return GameRepository.TextureRepository.FileExists(textureInfo.Texture); + } + + if (textureInfo.Texture.Length <= 63 && MtdFile is not null && _megaTextureExists) + { + var crc32 = _hashingService.GetCrc32Upper(textureInfo.Texture.AsSpan(), MtdFileConstants.NameEncoding); + if (MtdFile.Content.Contains(crc32)) + { + textureOrigin = GuiTextureOrigin.MegaTexture; + return true; + } + } + + // The background image for frames include a fallback the repository. + if (textureInfo.ComponentType == GuiComponentType.FrameBackground) + { + textureOrigin = GuiTextureOrigin.Repository; + return GameRepository.FileExists(textureInfo.Texture); + } + + textureOrigin = default; + return false; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs new file mode 100644 index 0000000..b96c4d7 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AnakinRaW.CommonUtilities.Collections; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.GuiDialog.Xml; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Engine.Xml.Tags; + +namespace PG.StarWarsGame.Engine.GuiDialog; + +partial class GuiDialogGameManager +{ + public const int MegaTextureMaxFilePathLength = 255; + + protected override Task InitializeCoreAsync(CancellationToken token) + { + return Task.Run(() => + { + var parserFactory = ServiceProvider.GetRequiredService(); + var guiDialogParser = parserFactory.CreateFileParser(); + + _defaultTexturesRo = new ReadOnlyDictionary(_defaultTextures); + + Logger?.LogInformation("Parsing GuiDialogs..."); + using var fileStream = GameRepository.TryOpenFile("DATA\\XML\\GUIDIALOGS.XML"); + + if (fileStream is null) + { + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = "Unable to find GuiDialogs.xml" + }); + return; + } + + var guiDialogs = guiDialogParser.ParseFile(fileStream); + if (guiDialogs is null) + { + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = "Unable to parse GuiDialogs.xml" + }); + return; + } + + GuiDialogsXml = guiDialogs; + + InitializeTextures(guiDialogs.TextureData, ErrorListener); + + }, token); + } + + private void InitializeTextures(GuiDialogsXmlTextureData textureData, DatabaseErrorListenerWrapper errorListener) + { + InitializeMegaTextures(textureData, ErrorListener); + + var textures = textureData.Textures; + + if (textures.Count == 0) + { + errorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = "No Textures defined in GuiDialogs.xml" + }); + } + 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); + + ReportInvalidComponent(in invalidKeys); + } + + foreach (var componentTextureData in textures.Skip(1)) + { + // The game only uses the *first* entry. + if (_perComponentTextures.ContainsKey(componentTextureData.Component)) + continue; + + _perComponentTextures.Add(componentTextureData.Component, InitializeComponentTextures(componentTextureData, false, out var invalidKeys)); + ReportInvalidComponent(in invalidKeys); + } + } + + private Dictionary InitializeComponentTextures(XmlComponentTextureData textureData, bool isDefaultComponent, out FrugalList invalidKeys) + { + invalidKeys = new FrugalList(); + + var result = new Dictionary(); + + if (!isDefaultComponent) + { + // This assumes that _defaultTextures is already filled + foreach (var key in _defaultTextures.Keys) + result.Add(key, _defaultTextures[key]); + } + + + foreach (var keyText in textureData.Textures.Keys) + { + if (!ComponentTextureKeyExtensions.TryConvertToKey(keyText.AsSpan(), out var key)) + { + invalidKeys.Add(keyText); + continue; + } + + var textureValue = textureData.Textures.GetLastValue(keyText); + result[key] = new ComponentTextureEntry(key, textureValue, !isDefaultComponent); + } + + return result; + } + + private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, DatabaseErrorListenerWrapper errorListener) + { + if (guiDialogs.MegaTexture is null) + { + errorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = "MtdFile is not defined in GuiDialogs.xml" + }); + } + else + { + var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{guiDialogs.MegaTexture}.mtd"); + + if (mtdPath.Length > MegaTextureMaxFilePathLength) + { + errorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = $"Mtd file path is longer than {MegaTextureMaxFilePathLength}." + }); + } + + using var megaTexture = GameRepository.TryOpenFile(mtdPath); + MtdFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); + } + + if (guiDialogs.CompressedMegaTexture is null) + { + errorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = "CompressedMegaTexture is not defined in GuiDialogs.xml" + }); + } + + + // TODO: Support using the correct texture based on desired low-RAM flag + _megaTextureFileName = guiDialogs.MegaTexture; + var textureFileNameWithExtension = $"{guiDialogs.MegaTexture}.tga"; + _megaTextureExists = GameRepository.TextureRepository.FileExists(textureFileNameWithExtension); + + if (textureFileNameWithExtension.Length > MegaTextureMaxFilePathLength) + { + errorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = $"MegaTexture file path is longer than {MegaTextureMaxFilePathLength}." + }); + } + } + + private void ReportInvalidComponent(in FrugalList invalidKeys) + { + if (invalidKeys.Count == 0) + return; + + ErrorListener.OnInitializationError(new InitializationError + { + GameManager = ToString(), + Message = $"The following XML keys are not valid to describe a GUI component: {string.Join(",", invalidKeys)}" + }); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiTextureOrigin.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiTextureOrigin.cs new file mode 100644 index 0000000..751afea --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiTextureOrigin.cs @@ -0,0 +1,7 @@ +namespace PG.StarWarsGame.Engine.GuiDialog; + +public enum GuiTextureOrigin +{ + MegaTexture, + Repository +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/IGuiDialogManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/IGuiDialogManager.cs new file mode 100644 index 0000000..1ac7632 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/IGuiDialogManager.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using PG.StarWarsGame.Engine.GuiDialog.Xml; +using PG.StarWarsGame.Files.MTD.Files; + +namespace PG.StarWarsGame.Engine.GuiDialog; + +public interface IGuiDialogManager +{ + IMtdFile? MtdFile { get; } + + GuiDialogsXml? GuiDialogsXml { get; } + + IReadOnlyCollection Components { get; } + + IReadOnlyDictionary DefaultTextureEntries { get; } + + IReadOnlyDictionary GetTextureEntries(string component, out bool componentExist); + + bool TryGetTextureEntry(string component, GuiComponentType key, out ComponentTextureEntry texture); + + bool TextureExists( + in ComponentTextureEntry textureInfo, + out GuiTextureOrigin textureOrigin, + out bool isNone, + bool buttonMiddleInRepoMode = false); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/TypeBasedComponentTextureEntryComparer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/TypeBasedComponentTextureEntryComparer.cs new file mode 100644 index 0000000..eda572e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/TypeBasedComponentTextureEntryComparer.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace PG.StarWarsGame.Engine.GuiDialog; + +public sealed class TypeBasedComponentTextureEntryComparer : IEqualityComparer +{ + public static readonly TypeBasedComponentTextureEntryComparer Instance = new(); + + private TypeBasedComponentTextureEntryComparer() + { + } + + public bool Equals(ComponentTextureEntry x, ComponentTextureEntry y) + { + return x.ComponentType.Equals(y.ComponentType); + } + + public int GetHashCode(ComponentTextureEntry obj) + { + return obj.ComponentType.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs new file mode 100644 index 0000000..72cc5a0 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXml.cs @@ -0,0 +1,9 @@ +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.GuiDialog.Xml; + +public class GuiDialogsXml(GuiDialogsXmlTextureData textureData, XmlLocationInfo location) : XmlObject(location) +{ + public GuiDialogsXmlTextureData TextureData { get; } = textureData; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs new file mode 100644 index 0000000..6496417 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/GuiDialogsXmlTextureData.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.GuiDialog.Xml; + +public class GuiDialogsXmlTextureData(IEnumerable textures, XmlLocationInfo location) : XmlObject(location) +{ + public string? MegaTexture { get; init; } + + public string? CompressedMegaTexture { get; init; } + + public IReadOnlyList Textures { get; } = textures.ToList(); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs new file mode 100644 index 0000000..2ed95ce --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/Xml/XmlComponentTextureData.cs @@ -0,0 +1,14 @@ +using System; +using PG.Commons.Collections; +using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.GuiDialog.Xml; + +public class XmlComponentTextureData(string componentId, IReadOnlyValueListDictionary textures, XmlLocationInfo location) + : XmlObject(location) +{ + public string Component { get; } = componentId ?? throw new ArgumentNullException(componentId); + + public IReadOnlyValueListDictionary Textures { get; } = textures ?? throw new ArgumentNullException(nameof(textures)); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs new file mode 100644 index 0000000..32e08dd --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs @@ -0,0 +1,103 @@ +using System; +using PG.StarWarsGame.Engine.IO.Utilities; +using PG.StarWarsGame.Engine.Utilities; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal class EffectsRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : MultiPassRepository(baseRepository, serviceProvider) +{ + private static readonly string[] LookupPaths = + [ + "DATA\\ART\\SHADERS", + "DATA\\ART\\SHADERS\\TERRAIN", + // This path is not coded to the engine + "DATA\\ART\\SHADERS\\ENGINE", + ]; + + // The engine does not support ".fxh" as a shader lookup, but as there might be some pre-compiling going on, this should be OK. + private static readonly string[] ShaderExtensions = [".fx", ".fxo", ".fxh"]; + + private protected override FileFoundInfo MultiPassAction( + ReadOnlySpan filePath, + ref ValueStringBuilder reusableStringBuilder, + ref ValueStringBuilder destination, + bool megFileOnly) + { + var strippedName = StripFileName(filePath); + + if (strippedName.Length > PGConstants.MaxEffectFileName) + return default; + + foreach (var ext in ShaderExtensions) + { + var extSpan = ext.AsSpan(); + + + var fileFoundInfo = FindEffect( + strippedName, + extSpan, + ReadOnlySpan.Empty, + ref reusableStringBuilder, + ref destination); + + if (fileFoundInfo.FileFound) + return fileFoundInfo; + + + foreach (var directory in LookupPaths) + { + fileFoundInfo = FindEffect( + strippedName, + extSpan, + directory.AsSpan(), + ref reusableStringBuilder, + ref destination); + + if (fileFoundInfo.FileFound) + return fileFoundInfo; + } + } + + + return default; + } + + private FileFoundInfo FindEffect( + ReadOnlySpan strippedName, + ReadOnlySpan extension, + ReadOnlySpan directory, + ref ValueStringBuilder multiPassStringBuilder, + ref ValueStringBuilder filePathStringBuilder) + { + multiPassStringBuilder.Length = 0; + + if (directory != ReadOnlySpan.Empty) + FileSystem.Path.Join(directory, strippedName, ref multiPassStringBuilder); + else + multiPassStringBuilder.Append(strippedName); + + multiPassStringBuilder.Append(extension); + + if (multiPassStringBuilder.Length > PGConstants.MaxEffectFileName) + return default; + + return BaseRepository.FindFile(multiPassStringBuilder.AsSpan(), ref filePathStringBuilder); + } + + private static ReadOnlySpan StripFileName(ReadOnlySpan src) + { + var destination = src; + + for (var i = src.Length - 1; i >= 0; --i) + { + + if (src[i] == '.') + destination = src.Slice(0, i); + + if (src[i] == '/' || src[i] == '\\') + break; + } + + return destination; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs new file mode 100644 index 0000000..eb1af79 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using PG.StarWarsGame.Files.MEG.Data.Entries; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal readonly ref struct FileFoundInfo +{ + public bool FileFound => FilePath != ReadOnlySpan.Empty || InMeg; + + public ReadOnlySpan FilePath { get; } + + public MegDataEntryReference? MegDataEntryReference { get; } + + [MemberNotNullWhen(true, nameof(MegDataEntryReference))] + public bool InMeg => MegDataEntryReference is not null; + + public FileFoundInfo(ReadOnlySpan filePath) + { + FilePath = filePath; + } + + public FileFoundInfo(MegDataEntryReference? megDataReference) + { + MegDataEntryReference = megDataReference; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/FocGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs similarity index 56% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/FocGameRepository.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs index a36382c..6e16e59 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/FocGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.Utilities; using PG.StarWarsGame.Files.MEG.Files; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; -namespace PG.StarWarsGame.Engine.Repositories; +namespace PG.StarWarsGame.Engine.IO.Repositories; // EaW file lookup works slightly different! internal class FocGameRepository : GameRepository { public override GameEngineType EngineType => GameEngineType.Foc; - public FocGameRepository(GameLocations gameLocations, IXmlParserErrorListener? listener, IServiceProvider serviceProvider) : base(gameLocations, listener, serviceProvider) + public FocGameRepository(GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + : base(gameLocations, errorListener, serviceProvider) { if (gameLocations == null) throw new ArgumentNullException(nameof(gameLocations)); @@ -52,49 +53,31 @@ public FocGameRepository(GameLocations gameLocations, IXmlParserErrorListener? l AddMegFiles(megsToConsider); } - - - protected override T? RepositoryFileLookup(string filePath, Func> pathAction, Func> megAction, bool megFileOnly, T? defaultValue = default) where T: default + + protected internal override FileFoundInfo FindFile(ReadOnlySpan filePath, ref ValueStringBuilder pathStringBuilder, bool megFileOnly = false) { if (!megFileOnly) { - foreach (var modPath in ModPaths) - { - var modFilePath = FileSystem.Path.Combine(modPath, filePath); + var fileFoundInfo = FileFromAltExists(filePath, ModPaths, ref pathStringBuilder); + if (fileFoundInfo.FileFound) + return fileFoundInfo; - var result = pathAction(modFilePath); - if (result.ShallReturn) - return result.Result; - } - - { - var normalFilePath = FileSystem.Path.Combine(GameDirectory, filePath); - var result = pathAction(normalFilePath); - if (result.ShallReturn) - return result.Result; - } - + fileFoundInfo = FindFileCore(filePath, ref pathStringBuilder); + if (fileFoundInfo.FileFound) + return fileFoundInfo; } + if (MasterMegArchive is not null) { - var result = megAction(filePath); - if (result.ShallReturn) - return result.Result; + var fileFoundInfo = GetFileInfoFromMasterMeg(filePath); + if (fileFoundInfo.InMeg) + return fileFoundInfo; } if (!megFileOnly) - { - foreach (var fallbackPath in FallbackPaths) - { - var fallbackFilePath = FileSystem.Path.Combine(fallbackPath, filePath); - - var result = pathAction(fallbackFilePath); - if (result.ShallReturn) - return result.Result; - } - } + return FileFromAltExists(filePath, FallbackPaths, ref pathStringBuilder); - return defaultValue; + return default; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs new file mode 100644 index 0000000..8b151c9 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using AnakinRaW.CommonUtilities.FileSystem; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine.IO.Utilities; +using PG.StarWarsGame.Engine.Utilities; +using PG.StarWarsGame.Files.MEG.Binary; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal partial class GameRepository +{ + private static readonly string[] DataPathPrefixes = ["DATA/", "DATA\\", "./DATA/", ".\\DATA\\"]; + + public bool FileExists(string filePath, string[] extensions, bool megFileOnly = false) + { + foreach (var extension in extensions) + { + var newPath = FileSystem.Path.ChangeExtension(filePath, extension); + if (FileExists(newPath, megFileOnly)) + return true; + } + return false; + } + + public bool FileExists(string filePath, bool megFileOnly = false) + { + return FileExists(filePath.AsSpan(), megFileOnly); + } + + public bool FileExists(ReadOnlySpan filePath, bool megFileOnly = false) + { + var sb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + var fileFound = FindFile(filePath, ref sb, megFileOnly); + var fileExists = fileFound.FileFound; + sb.Dispose(); + return fileExists; + } + + public Stream OpenFile(string filePath, bool megFileOnly = false) + { + return OpenFile(filePath.AsSpan(), megFileOnly); + } + + + public Stream OpenFile(ReadOnlySpan filePath, bool megFileOnly = false) + { + var stream = TryOpenFile(filePath, megFileOnly); + if (stream is null) + throw new FileNotFoundException($"Unable to find game data: {filePath.ToString()}"); + return stream; + } + + public Stream? TryOpenFile(string filePath, bool megFileOnly = false) + { + return TryOpenFile(filePath.AsSpan(), megFileOnly); + } + + public Stream? TryOpenFile(ReadOnlySpan filePath, bool megFileOnly) + { + var sb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + var fileFoundInfo = FindFile(filePath, ref sb, megFileOnly); + var fileStream = OpenFileCore(fileFoundInfo); + sb.Dispose(); + return fileStream; + } + + protected internal abstract FileFoundInfo FindFile(ReadOnlySpan filePath, + ref ValueStringBuilder pathStringBuilder, bool megFileOnly = false); + + protected FileFoundInfo GetFileInfoFromMasterMeg(ReadOnlySpan filePath) + { + Debug.Assert(MasterMegArchive is not null); + + if (filePath.Length > PGConstants.MaxMegEntryPathLength) + { + Logger.LogWarning($"Trying to open a MEG entry which is longer than 259 characters: '{filePath.ToString()}'"); + return default; + } + + Span fileNameSpan = stackalloc char[PGConstants.MaxMegEntryPathLength]; + + if (!_megPathNormalizer.TryNormalize(filePath, fileNameSpan, out var length)) + return default; + + var fileName = fileNameSpan.Slice(0, length); + + if (fileName.Length > PGConstants.MaxMegEntryPathLength) + { + Logger.LogWarning($"Trying to open a MEG entry which is longer than 259 characters after normalization: '{fileName.ToString()}'"); + return default; + } + + var crc = _crc32HashingService.GetCrc32(fileName, MegFileConstants.MegDataEntryPathEncoding); + + var entry = MasterMegArchive!.FirstEntryWithCrc(crc); + + return new FileFoundInfo(entry); + } + + protected FileFoundInfo FindFileCore(ReadOnlySpan filePath, ref ValueStringBuilder stringBuilder) + { + bool exists; + + stringBuilder.Length = 0; + + if (FileSystem.Path.IsPathFullyQualified(filePath)) + stringBuilder.Append(filePath); + else + FileSystem.Path.Join(GameDirectory.AsSpan(), filePath, ref stringBuilder); + + var actualFilePath = stringBuilder.AsSpan(); + + // We accept a *possible* difference here between platforms, + // unless it's proven the differences are too significant. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + exists = FileSystem.File.Exists(actualFilePath.ToString()); + else + { + // We *could* also use the slightly faster GetFileAttributesA. + // However, CreateFileA and GetFileAttributesA are implemented complete independent. + // The game uses CreateFileA. + // Thus, we should stick to what the game uses in order to be as close to the engine as possible + // NB: It's also important that the string builder is zero-terminated, as otherwise CreateFileA might get invalid data. + var fileHandle = CreateFile( + in stringBuilder.GetPinnableReference(true), + FileAccess.Read, + FileShare.Read, + IntPtr.Zero, + FileMode.Open, + FileAttributes.Normal, IntPtr.Zero); + + exists = IsValidAndClose(fileHandle); + } + return !exists ? new FileFoundInfo() : new FileFoundInfo(actualFilePath); + } + + protected FileFoundInfo FileFromAltExists(ReadOnlySpan filePath, IList fallbackPaths, ref ValueStringBuilder pathStringBuilder) + { + if (fallbackPaths.Count == 0) + return default; + if (!PathStartsWithDataDirectory(filePath, out var prefixLength)) + return default; + + var pathWithNormalizedData = filePath.Slice(prefixLength); + + foreach (var fallbackPath in fallbackPaths) + { + pathStringBuilder.Length = 0; + + FileSystem.Path.Join(fallbackPath.AsSpan(), pathWithNormalizedData, ref pathStringBuilder); + var newPath = pathStringBuilder.AsSpan(); + + var fileFoundInfo = FindFileCore(newPath, ref pathStringBuilder); + if (fileFoundInfo.FileFound) + return fileFoundInfo; + } + + return default; + } + + private static bool PathStartsWithDataDirectory(ReadOnlySpan path, out int cutoffLength) + { + cutoffLength = 0; + if (path.Length < 5) + return false; + foreach (var prefix in DataPathPrefixes) + { + if (path.StartsWith(prefix.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + if (path[0] == '.') + cutoffLength = 2; + return true; + } + } + return false; + } + + internal Stream? OpenFileCore(FileFoundInfo fileFoundInfo) + { + if (!fileFoundInfo.FileFound) + return null; + + if (fileFoundInfo.InMeg) + return _megExtractor.GetFileData(fileFoundInfo.MegDataEntryReference.Location); + + return FileSystem.FileStream.New(fileFoundInfo.FilePath.ToString(), FileMode.Open, FileAccess.Read, FileShare.Read); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsValidAndClose(IntPtr handle) + { + var isValid = handle != IntPtr.Zero && handle != new IntPtr(-1); + if (isValid) + CloseHandle(handle); + return isValid; + } + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern IntPtr CreateFile( + in char lpFileName, + [MarshalAs(UnmanagedType.U4)] FileAccess access, + [MarshalAs(UnmanagedType.U4)] FileShare share, + IntPtr securityAttributes, + [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, + IntPtr templateFile); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CloseHandle(IntPtr hObject); +} diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs similarity index 62% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/GameRepository.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index 48c99f0..cd6f68a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; -using System.IO; using System.IO.Abstractions; using System.Linq; -using System.Runtime.InteropServices; using AnakinRaW.CommonUtilities.FileSystem; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileSystemGlobbing; using Microsoft.Extensions.Logging; using PG.Commons.Hashing; using PG.Commons.Services; -using PG.StarWarsGame.Engine.Language; -using PG.StarWarsGame.Engine.Utilities; +using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.MEG.Data.Archives; using PG.StarWarsGame.Files.MEG.Data.Entries; @@ -20,11 +17,10 @@ using PG.StarWarsGame.Files.MEG.Services; using PG.StarWarsGame.Files.MEG.Services.Builder.Normalization; using PG.StarWarsGame.Files.XML; -using PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Engine.Repositories; +namespace PG.StarWarsGame.Engine.IO.Repositories; -internal abstract class GameRepository : ServiceBase, IGameRepository +internal abstract partial class GameRepository : ServiceBase, IGameRepository { private readonly IMegFileService _megFileService; private readonly IMegFileExtractor _megExtractor; @@ -32,7 +28,7 @@ internal abstract class GameRepository : ServiceBase, IGameRepository private readonly ICrc32HashingService _crc32HashingService; private readonly IVirtualMegArchiveBuilder _virtualMegBuilder; private readonly IGameLanguageManagerProvider _languageManagerProvider; - private readonly IXmlParserErrorListener? _xmlParserErrorListener; + private readonly DatabaseErrorListenerWrapper _errorListener; protected readonly string GameDirectory; @@ -51,7 +47,7 @@ internal abstract class GameRepository : ServiceBase, IGameRepository private readonly List _loadedMegFiles = new(); protected IVirtualMegArchive? MasterMegArchive { get; private set; } - protected GameRepository(GameLocations gameLocations, IXmlParserErrorListener? errorListener, IServiceProvider serviceProvider) : base(serviceProvider) + protected GameRepository(GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) : base(serviceProvider) { if (gameLocations == null) throw new ArgumentNullException(nameof(gameLocations)); @@ -60,9 +56,9 @@ protected GameRepository(GameLocations gameLocations, IXmlParserErrorListener? e _megFileService = serviceProvider.GetRequiredService(); _virtualMegBuilder = serviceProvider.GetRequiredService(); _crc32HashingService = serviceProvider.GetRequiredService(); - _megPathNormalizer = new PetroglyphDataEntryPathNormalizer(serviceProvider); + _megPathNormalizer = EmpireAtWarMegDataEntryPathNormalizer.Instance; _languageManagerProvider = serviceProvider.GetRequiredService(); - _xmlParserErrorListener = errorListener; + _errorListener = errorListener; foreach (var mod in gameLocations.ModPaths) { @@ -126,93 +122,6 @@ public void AddMegFile(string megFile) AddMegFiles([megArchive]); } - public bool FileExists(string filePath, bool megFileOnly = false) - { - return RepositoryFileLookup(filePath, fp => - { - if (FileSystem.File.Exists(fp)) - return new ActionResult(true, true); - return ActionResult.DoNotReturn; - }, - fp => - { - var entry = FindFileInMasterMeg(fp); - if (entry is not null) - return new ActionResult(true, true); - return ActionResult.DoNotReturn; - }, megFileOnly); - } - - public Stream? TryOpenFile(string filePath, bool megFileOnly = false) - { - return RepositoryFileLookup(filePath, fp => - { - if (FileSystem.File.Exists(fp)) - return new ActionResult(true, OpenFileRead(fp)); - return ActionResult.DoNotReturn; - }, - fp => - { - var entry = FindFileInMasterMeg(fp); - if (entry is not null) - return new ActionResult(true, _megExtractor.GetFileData(entry.Location)); - return ActionResult.DoNotReturn; - }, megFileOnly); - } - - - public Stream OpenFile(string filePath, bool megFileOnly = false) - { - var stream = TryOpenFile(filePath, megFileOnly); - if (stream is null) - throw new FileNotFoundException($"Unable to find game data: {filePath}"); - return stream; - } - - public bool FileExists(string filePath, string[] extensions, bool megFileOnly = false) - { - foreach (var extension in extensions) - { - var newPath = FileSystem.Path.ChangeExtension(filePath, extension); - if (FileExists(newPath, megFileOnly)) - return true; - } - return false; - } - - public IEnumerable FindFiles(string searchPattern, bool megFileOnly = false) - { - var files = new HashSet(); - - var matcher = new Matcher(); - matcher.AddInclude(searchPattern); - - RepositoryFileLookup(searchPattern, - pattern => - { - var path = pattern.AsSpan().TrimEnd(searchPattern.AsSpan()); - - var matcherResult = matcher.Execute(FileSystem, path.ToString()); - - foreach (var matchedFile in matcherResult.Files) - { - var normalizedFile = _megPathNormalizer.Normalize(matchedFile.Path); - files.Add(normalizedFile); - } - - return ActionResult.DoNotReturn; - }, - _ => - { - var foundFiles = MasterMegArchive!.FindAllEntries(searchPattern, true); - foreach (var x in foundFiles) - files.Add(x.FilePath); - - return ActionResult.DoNotReturn; - }, megFileOnly); - - return files; - } public bool IsLanguageInstalled(LanguageType language) { @@ -311,9 +220,11 @@ protected IList LoadMegArchivesFromXml(string lookupPath) return Array.Empty(); } - var parser = fileParserFactory.GetFileParser(_xmlParserErrorListener); + var parser = fileParserFactory.CreateFileParser(_errorListener); var megaFilesXml = parser.ParseFile(xmlStream); + if (megaFilesXml is null) + return []; var megs = new List(megaFilesXml.Files.Count); @@ -333,14 +244,14 @@ internal void Seal() _sealed = true; } - protected abstract T? RepositoryFileLookup(string filePath, Func> pathAction, - Func> megAction, bool megFileOnly, T? defaultValue = default); - protected IMegFile? LoadMegArchive(string megPath) { using var megFileStream = TryOpenFile(megPath); if (megFileStream is not FileSystemStream fileSystemStream) + { + Logger.LogWarning($"Unable to find MEG file '{megPath}'"); return null; + } var megFile = _megFileService.Load(fileSystemStream); @@ -350,63 +261,12 @@ internal void Seal() return megFile; } - protected MegDataEntryReference? FindFileInMasterMeg(string filePath) - { - Span fileNameBuffer = stackalloc char[PGConstants.MaxPathLength]; - - // TODO: Is the engine really "to-uppering" the input??? - var length = _megPathNormalizer.Normalize(filePath.AsSpan(), fileNameBuffer); - var fileName = fileNameBuffer.Slice(0, length); - var crc = _crc32HashingService.GetCrc32(fileName, PGConstants.PGCrc32Encoding); - - return MasterMegArchive?.FirstEntryWithCrc(crc); - } - - protected FileSystemStream OpenFileRead(string filePath) - { - if (!AllowOpenFile(filePath)) - throw new UnauthorizedAccessException("The data is not part of the Games!"); - return FileSystem.FileStream.New(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - } - - private bool AllowOpenFile(string filePath) - { - foreach (var modPath in ModPaths) - { - if (FileSystem.Path.IsChildOf(modPath, filePath)) - return true; - } - - if (FileSystem.Path.IsChildOf(GameDirectory, filePath)) - return true; - - foreach (var fallbackPath in FallbackPaths) - { - if (FileSystem.Path.IsChildOf(fallbackPath, filePath)) - return true; - } - - return false; - } - private void ThrowIfSealed() { if (_sealed) throw new InvalidOperationException("The object is sealed for modifications"); } - - protected readonly struct ActionResult(bool shallReturn, T? result) - { - public T? Result { get; } = result; - - public bool ShallReturn { get; } = shallReturn; - - public static ActionResult DoNotReturn => default; - } - - [StructLayout(LayoutKind.Explicit)] - private readonly struct EmptyStruct; - + private sealed class LanguageFiles { public LanguageType Language { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/GameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs similarity index 52% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/GameRepositoryFactory.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs index fe64368..fa7c760 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/GameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs @@ -1,15 +1,14 @@ using System; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; +using PG.StarWarsGame.Engine.Database.ErrorReporting; -namespace PG.StarWarsGame.Engine.Repositories; +namespace PG.StarWarsGame.Engine.IO.Repositories; internal sealed class GameRepositoryFactory(IServiceProvider serviceProvider) : IGameRepositoryFactory { - public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, IXmlParserErrorListener? xmlParserErrorListener) + public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener) { if (engineType == GameEngineType.Eaw) throw new NotImplementedException("Empire at War is currently not supported."); - return new FocGameRepository(gameLocations, xmlParserErrorListener, serviceProvider); + return new FocGameRepository(gameLocations, errorListener, serviceProvider); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepository.cs similarity index 70% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepository.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepository.cs index 62e6619..ca6bfd5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepository.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; -using PG.StarWarsGame.Engine.Language; +using PG.StarWarsGame.Engine.Localization; -namespace PG.StarWarsGame.Engine.Repositories; +namespace PG.StarWarsGame.Engine.IO.Repositories; public interface IGameRepository : IRepository { @@ -18,7 +17,5 @@ public interface IGameRepository : IRepository bool FileExists(string filePath, string[] extensions, bool megFileOnly = false); - IEnumerable FindFiles(string searchPattern, bool megFileOnly = false); - bool IsLanguageInstalled(LanguageType languageType); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs new file mode 100644 index 0000000..fed8536 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs @@ -0,0 +1,8 @@ +using PG.StarWarsGame.Engine.Database.ErrorReporting; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal interface IGameRepositoryFactory +{ + GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs new file mode 100644 index 0000000..8054340 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +public interface IRepository +{ + Stream OpenFile(string filePath, bool megFileOnly = false); + Stream OpenFile(ReadOnlySpan filePath, bool megFileOnly = false); + + bool FileExists(string filePath, bool megFileOnly = false); + bool FileExists(ReadOnlySpan filePath, bool megFileOnly = false); + + Stream? TryOpenFile(string filePath, bool megFileOnly = false); + + Stream? TryOpenFile(ReadOnlySpan filePath, bool megFileOnly = false); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs new file mode 100644 index 0000000..7dd0b6e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.IO.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine.Utilities; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal abstract class MultiPassRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : IRepository +{ + protected readonly IFileSystem FileSystem = serviceProvider.GetRequiredService(); + protected readonly GameRepository BaseRepository = baseRepository; + + public Stream OpenFile(string filePath, bool megFileOnly = false) + { + return OpenFile(filePath.AsSpan(), megFileOnly); + } + + public Stream OpenFile(ReadOnlySpan filePath, bool megFileOnly = false) + { + var fileStream = TryOpenFile(filePath, megFileOnly); + if (fileStream is null) + throw new FileNotFoundException($"Unable to find game file: {filePath.ToString()}"); + return fileStream; + } + + public bool FileExists(string filePath, bool megFileOnly = false) + { + return FileExists(filePath.AsSpan(), megFileOnly); + } + + public bool FileExists(ReadOnlySpan filePath, bool megFileOnly = false) + { + var multiPassSb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + var destinationSb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + var fileFound = MultiPassAction(filePath, ref multiPassSb, ref destinationSb, megFileOnly); + var result = fileFound.FileFound; + multiPassSb.Dispose(); + destinationSb.Dispose(); + return result; + } + + private protected abstract FileFoundInfo MultiPassAction( + ReadOnlySpan filePath, + ref ValueStringBuilder reusableStringBuilder, + ref ValueStringBuilder destination, + bool megFileOnly); + + public Stream? TryOpenFile(string filePath, bool megFileOnly = false) + { + return TryOpenFile(filePath.AsSpan(), megFileOnly); + } + + public Stream? TryOpenFile(ReadOnlySpan filePath, bool megFileOnly = false) + { + var multiPassSb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + var destinationSb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + var fileFound = MultiPassAction(filePath, ref multiPassSb, ref destinationSb, megFileOnly); + var result = BaseRepository.OpenFileCore(fileFound); + multiPassSb.Dispose(); + destinationSb.Dispose(); + return result; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs new file mode 100644 index 0000000..af714f2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs @@ -0,0 +1,69 @@ +using System; +using PG.StarWarsGame.Engine.Utilities; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal class TextureRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : MultiPassRepository(baseRepository, serviceProvider) +{ + private static readonly string DdsExtension = ".dds"; + private static readonly string TexturePath = "DATA\\ART\\TEXTURES\\"; + + + private protected override FileFoundInfo MultiPassAction( + ReadOnlySpan filePath, + ref ValueStringBuilder reusableStringBuilder, + ref ValueStringBuilder destination, + bool megFileOnly) + { + if (filePath.Length > PGConstants.MaxTextureFileName) + return default; + + reusableStringBuilder.Append(filePath); + + var foundInfo = FindTexture(ref reusableStringBuilder, ref destination); + + if (foundInfo.FileFound) + return foundInfo; + + reusableStringBuilder.Length = 0; + reusableStringBuilder.Append(filePath); + + ChangeExtensionTo(ref reusableStringBuilder, DdsExtension.AsSpan()); + + return FindTexture(ref reusableStringBuilder, ref destination); + } + + private FileFoundInfo FindTexture(ref ValueStringBuilder multiPassStringBuilder, ref ValueStringBuilder pathStringBuilder) + { + var fileInfo = BaseRepository.FindFile(multiPassStringBuilder.AsSpan(), ref pathStringBuilder); + if (fileInfo.FileFound) + return fileInfo; + + + // Only PG knows why they only search for backslash and not also forward slash, + // when in fact in other methods, they handle both. + var separatorIndex = multiPassStringBuilder.AsSpan().LastIndexOf('\\'); + if (separatorIndex != -1) + multiPassStringBuilder.Remove(0 , separatorIndex + 1); + + multiPassStringBuilder.Insert(0, TexturePath); + + return BaseRepository.FindFile(multiPassStringBuilder.AsSpan(), ref pathStringBuilder); + } + + private static void ChangeExtensionTo(ref ValueStringBuilder stringBuilder, ReadOnlySpan extension) + { + // We cannot use Path.ChangeExtension as the PG implementation supports some strange things + // like that a string "c:\\file.tga\\" ending with a directory separator. The PG result will be + // "c:\\file.dds" while Path.ChangeExtension would return "c:\\file.tga\\.dds" + + // Also, while there are many cases, where this method breaks (such as "c:/test.abc/path.dds"), + // it's the way how the engine works 🤷‍ + var firstPeriod = stringBuilder.AsSpan().IndexOf('.'); + if (firstPeriod == -1) + return; + + stringBuilder.Length = firstPeriod; + stringBuilder.Append(extension); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/DirectoryInfoGlobbingWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/DirectoryInfoGlobbingWrapper.cs similarity index 95% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/DirectoryInfoGlobbingWrapper.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/DirectoryInfoGlobbingWrapper.cs index 2f2114a..f012fdc 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/DirectoryInfoGlobbingWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/DirectoryInfoGlobbingWrapper.cs @@ -3,14 +3,14 @@ using System.IO; using System.IO.Abstractions; -namespace PG.StarWarsGame.Engine.Utilities; +namespace PG.StarWarsGame.Engine.IO.Utilities; // Taken from https://github.com/vipentti/Vipentti.IO.Abstractions.FileSystemGlobbing /// /// Wraps to be used with /// -public sealed class DirectoryInfoGlobbingWrapper : Microsoft.Extensions.FileSystemGlobbing.Abstractions.DirectoryInfoBase +internal sealed class DirectoryInfoGlobbingWrapper : Microsoft.Extensions.FileSystemGlobbing.Abstractions.DirectoryInfoBase { private readonly IFileSystem _fileSystem; private readonly IDirectoryInfo _directoryInfo; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/FileInfoGlobbingWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs similarity index 95% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/FileInfoGlobbingWrapper.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs index 744c377..df99faa 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/FileInfoGlobbingWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs @@ -1,6 +1,6 @@ using System.IO.Abstractions; -namespace PG.StarWarsGame.Engine.Utilities; +namespace PG.StarWarsGame.Engine.IO.Utilities; // Taken from https://github.com/vipentti/Vipentti.IO.Abstractions.FileSystemGlobbing diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/MatcherExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/MatcherExtensions.cs similarity index 97% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/MatcherExtensions.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/MatcherExtensions.cs index e2bde1e..48ddfa1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/MatcherExtensions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/MatcherExtensions.cs @@ -4,14 +4,14 @@ using System.Linq; using Microsoft.Extensions.FileSystemGlobbing; -namespace PG.StarWarsGame.Engine.Utilities; +namespace PG.StarWarsGame.Engine.IO.Utilities; // Taken from https://github.com/vipentti/Vipentti.IO.Abstractions.FileSystemGlobbing /// /// Provides extensions for to support /// -public static class MatcherExtensions +internal static class MatcherExtensions { /// /// Searches the directory specified for all files matching patterns added to this instance of diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs new file mode 100644 index 0000000..471b5a8 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs @@ -0,0 +1,30 @@ +using System; +using System.IO.Abstractions; +using AnakinRaW.CommonUtilities.FileSystem; +using PG.StarWarsGame.Engine.Utilities; + +namespace PG.StarWarsGame.Engine.IO.Utilities; + +internal static class PathExtensions +{ + public static void Join(this IPath _, ReadOnlySpan path1, ReadOnlySpan path2, ref ValueStringBuilder stringBuilder) + { + if (path1.Length == 0 && path2.Length == 0) + return; + + if (path1.Length == 0 || path2.Length == 0) + { + ref var pathToUse = ref path1.Length == 0 ? ref path2 : ref path1; + stringBuilder.Append(pathToUse); + return; + } + + var needsSeparator = !(_.HasTrailingDirectorySeparator(path1) || _.HasLeadingDirectorySeparator(path2)); + + stringBuilder.Append(path1); + if (needsSeparator) + stringBuilder.Append(_.DirectorySeparatorChar); + + stringBuilder.Append(path2); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/EawGameLanguageManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/EawGameLanguageManager.cs similarity index 92% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/EawGameLanguageManager.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/EawGameLanguageManager.cs index 2c720b4..ddf2be7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/EawGameLanguageManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/EawGameLanguageManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; internal sealed class EawGameLanguageManager(IServiceProvider serviceProvider) : GameLanguageManager(serviceProvider) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/FocGameLanguageManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/FocGameLanguageManager.cs similarity index 94% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/FocGameLanguageManager.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/FocGameLanguageManager.cs index 3636c40..363b1e7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/FocGameLanguageManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/FocGameLanguageManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; internal sealed class FocGameLanguageManager(IServiceProvider serviceProvider) : GameLanguageManager(serviceProvider) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/GameLanguageManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/GameLanguageManager.cs similarity index 85% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/GameLanguageManager.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/GameLanguageManager.cs index ec4d6b2..64b2222 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/GameLanguageManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/GameLanguageManager.cs @@ -5,8 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; using PG.Commons.Services; +using PG.StarWarsGame.Engine.Utilities; -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; internal abstract class GameLanguageManager(IServiceProvider serviceProvider) : ServiceBase(serviceProvider), IGameLanguageManager { @@ -113,9 +114,6 @@ private CultureInfo GetParentCultureRecursive(CultureInfo culture) public string LocalizeFileName(string fileName, LanguageType language, out bool localized) { - if (fileName.Length > PGConstants.MaxPathLength) - throw new ArgumentOutOfRangeException(nameof(fileName), "fileName is too long"); - // The game assumes that all localized audio files are referenced by using their english name. // Thus, PG takes this shortcut if (language == LanguageType.English) @@ -124,33 +122,37 @@ public string LocalizeFileName(string fileName, LanguageType language, out bool return fileName; } - - Span localizedName = stackalloc char[fileName.Length]; - var length = LocalizeFileName(fileName.AsSpan(), language, localizedName, out localized); + var stringBuilder = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); + LocalizeFileName(fileName.AsSpan(), language, ref stringBuilder, out localized); if (!localized) + { + stringBuilder.Dispose(); return fileName; + } - Debug.Assert(localizedName.Length == length); - return localizedName.ToString(); + Debug.Assert(stringBuilder.Length == fileName.Length); + return stringBuilder.ToString(); } - public int LocalizeFileName(ReadOnlySpan fileName, LanguageType language, Span destination, out bool localized) { - if (fileName.Length > PGConstants.MaxPathLength) - throw new ArgumentOutOfRangeException(nameof(fileName), "fileName is too long"); - - if (destination.Length < fileName.Length) - throw new ArgumentException("destination is too short", nameof(destination)); + var sb = new ValueStringBuilder(destination.Length); + LocalizeFileName(fileName, language, ref sb, out localized); + sb.TryCopyTo(destination, out var written); + sb.Dispose(); + return written; + } + public void LocalizeFileName(ReadOnlySpan fileName, LanguageType language, ref ValueStringBuilder stringBuilder, out bool localized) + { localized = true; // The game assumes that all localized audio files are referenced by using their english name. // Thus, PG takes this shortcut if (language == LanguageType.English) { - fileName.CopyTo(destination); - return fileName.Length; + stringBuilder.Append(fileName); + return; } var isWav = false; @@ -175,8 +177,8 @@ public int LocalizeFileName(ReadOnlySpan fileName, LanguageType language, { localized = false; Logger.LogWarning($"Unable to localize '{fileName.ToString()}'"); - fileName.CopyTo(destination); - return fileName.Length; + stringBuilder.Append(fileName); + return; } var withoutSuffix = fileName.Slice(0, engSuffixIndex); @@ -189,10 +191,8 @@ public int LocalizeFileName(ReadOnlySpan fileName, LanguageType language, else throw new InvalidOperationException(); - withoutSuffix.CopyTo(destination); - newLocalizedSuffix.CopyTo(destination.Slice(withoutSuffix.Length, newLocalizedSuffix.Length)); - - return fileName.Length; + stringBuilder.Append(withoutSuffix); + stringBuilder.Append(newLocalizedSuffix); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/GameLanguageManagerProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/GameLanguageManagerProvider.cs similarity index 93% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/GameLanguageManagerProvider.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/GameLanguageManagerProvider.cs index b6b00e8..6430c02 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/GameLanguageManagerProvider.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/GameLanguageManagerProvider.cs @@ -1,6 +1,6 @@ using System; -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; internal class GameLanguageManagerProvider(IServiceProvider serviceProvider) : IGameLanguageManagerProvider { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/IGameLanguageManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/IGameLanguageManager.cs similarity index 92% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/IGameLanguageManager.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/IGameLanguageManager.cs index b0e03da..de44657 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/IGameLanguageManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/IGameLanguageManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; public interface IGameLanguageManager { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/IGameLanguageManagerProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/IGameLanguageManagerProvider.cs similarity index 70% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/IGameLanguageManagerProvider.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/IGameLanguageManagerProvider.cs index c47d0a4..fa1e3b8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/IGameLanguageManagerProvider.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/IGameLanguageManagerProvider.cs @@ -1,4 +1,4 @@ -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; public interface IGameLanguageManagerProvider { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/SupportedLanguage.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/LanguageType.cs similarity index 80% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Language/SupportedLanguage.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/LanguageType.cs index 50e1aad..5e67d9e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Language/SupportedLanguage.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Localization/LanguageType.cs @@ -1,4 +1,4 @@ -namespace PG.StarWarsGame.Engine.Language; +namespace PG.StarWarsGame.Engine.Localization; public enum LanguageType { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 705e571..7b1bb16 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 PG.StarWarsGame.Engine PG.StarWarsGame.Engine AlamoEngineTools.PG.StarWarsGame.Engine @@ -15,14 +15,12 @@ true snupkg true + preview - - - - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -37,8 +35,10 @@ - + + + \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings new file mode 100644 index 0000000..57b1fa3 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj.DotSettings @@ -0,0 +1,7 @@ + + False + True + True + True + True + True \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs index 486aa40..c7e3de2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs @@ -4,7 +4,16 @@ namespace PG.StarWarsGame.Engine; public static class PGConstants { - public static readonly Encoding PGCrc32Encoding = Encoding.ASCII; + public static readonly Encoding DefaultPGEncoding = Encoding.ASCII; - public const int MaxPathLength = 260; + // Always reserve one character for the null-terminator + public const int MaxMegEntryPathLength = 259; + public const int MaxEffectFileName = 259; + public const int MaxTextureFileName = 259; + public const int MaxSFXEventDatabaseFileName = 259; + public const int MaxSFXEventName = 255; + public const int MaxGameObjectDatabaseFileName = 127; + public const int MaxCommandBarDatabaseFileName = 259; + + public const int MaxGuiDialogMegaTextureFileName = 255; } \ 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 ae4269b..e43a693 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs @@ -1,8 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Language; -using PG.StarWarsGame.Engine.Repositories; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine; @@ -10,10 +11,14 @@ public static class PetroglyphEngineServiceContribution { public static void ContributeServices(IServiceCollection serviceCollection) { + // Singletons serviceCollection.AddSingleton(sp => new GameRepositoryFactory(sp)); serviceCollection.AddSingleton(sp => new GameLanguageManagerProvider(sp)); serviceCollection.AddSingleton(sp => new PetroglyphXmlFileParserFactory(sp)); - serviceCollection.AddTransient(sp => new GameDatabaseService(sp)); + serviceCollection.AddSingleton(sp => new GameDatabaseService(sp)); + + // Transients + serviceCollection.AddTransient(sp => new XmlContainerContentParser(sp)); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs new file mode 100644 index 0000000..183441b --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs @@ -0,0 +1,63 @@ +using PG.Commons.Numerics; +using System; + +namespace PG.StarWarsGame.Engine.Rendering; + +public readonly struct RgbaColor(float r, float g, float b, float a) : IEquatable +{ + private readonly float _r = r; + private readonly float _g = g; + private readonly float _b = b; + private readonly float _a = a; + + public uint R => unchecked((uint)(_r * 255.0f)); + public uint G => unchecked((uint)(_g * 255.0f)); + public uint B => unchecked((uint)(_b * 255.0f)); + public uint A => unchecked((uint)(_a * 255.0f)); + + public float Rf => _r; + public float Gf => _g; + public float Bf => _b; + public float Af => _a; + + public RgbaColor() : this(1.0f, 1.0f, 1.0f, 1.0f) + { + } + + public RgbaColor(Vector4Int rgbaVector) : this(rgbaVector.First, rgbaVector.Second, rgbaVector.Third, rgbaVector.Fourth) + { + } + + public RgbaColor(int r, int g, int b, int a) : this(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f) + { + } + + public override string ToString() + { + return $"{nameof(RgbaColor)} [R={R}, G={G}, B={B}, A={A}]"; + } + + public RgbaColor Lerp(RgbaColor a, RgbaColor b, float t) + { + var red = a.Rf + (b.Rf - a.Rf) * t; + var green = a.Gf + (b.Gf - a.Gf) * t; + var blue = a.Bf + (b.Bf - a.Bf) * t; + var alpha = a.Af + (b.Af - a.Af) * t; + return new RgbaColor(red, green, blue, alpha); + } + + public bool Equals(RgbaColor other) + { + return _r.Equals(other._r) && _g.Equals(other._g) && _b.Equals(other._b) && _a.Equals(other._a); + } + + public override bool Equals(object? obj) + { + return obj is RgbaColor other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_r, _g, _b, _a); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/EffectsRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/EffectsRepository.cs deleted file mode 100644 index 4421c88..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/EffectsRepository.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace PG.StarWarsGame.Engine.Repositories; - -public class EffectsRepository(IGameRepository baseRepository, IServiceProvider serviceProvider) : MultiPassRepository(baseRepository, serviceProvider) -{ - private static readonly string[] LookupPaths = - [ - "Data\\Art\\Shaders", - "Data\\Art\\Shaders\\Terrain", - "Data\\Art\\Shaders\\Engine", - ]; - - // The engine does not support ".fxh" as a shader lookup, but as there might be som pre-compiling going on, this should be OK. - private static readonly string[] ShaderExtensions = [".fx", ".fxo", ".fxh"]; - - [return:MaybeNull] - protected override T MultiPassAction(string inputPath, Func fileAction) - { - var currExt = FileSystem.Path.GetExtension(inputPath); - if (!ShaderExtensions.Contains(currExt, StringComparer.OrdinalIgnoreCase)) - throw new ArgumentException("Invalid data extension for shader. Must be .fx, .fxh or .fxo", nameof(inputPath)); - - foreach (var directory in LookupPaths) - { - var lookupPath = FileSystem.Path.Combine(directory, inputPath); - - foreach (var ext in ShaderExtensions) - { - lookupPath = FileSystem.Path.ChangeExtension(lookupPath, ext); - - var actionResult = fileAction(lookupPath); - if (actionResult.success) - return actionResult.result; - } - } - - return default; - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepositoryFactory.cs deleted file mode 100644 index e7f25e2..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IGameRepositoryFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Repositories; - -internal interface IGameRepositoryFactory -{ - GameRepository Create(GameEngineType engineType, GameLocations gameLocations, IXmlParserErrorListener? xmlParserErrorListener); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IRepository.cs deleted file mode 100644 index fe5764f..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/IRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.IO; - -namespace PG.StarWarsGame.Engine.Repositories; - -public interface IRepository -{ - Stream OpenFile(string filePath, bool megFileOnly = false); - - bool FileExists(string filePath, bool megFileOnly = false); - - Stream? TryOpenFile(string filePath, bool megFileOnly = false); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/MultiPassRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/MultiPassRepository.cs deleted file mode 100644 index c4238bb..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/MultiPassRepository.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.IO; -using System.IO.Abstractions; -using Microsoft.Extensions.DependencyInjection; - -namespace PG.StarWarsGame.Engine.Repositories; - -public abstract class MultiPassRepository(IGameRepository baseRepository, IServiceProvider serviceProvider) : IRepository -{ - protected readonly IFileSystem FileSystem = serviceProvider.GetRequiredService(); - - public Stream OpenFile(string filePath, bool megFileOnly = false) - { - var fileStream = TryOpenFile(filePath, megFileOnly); - if (fileStream is null) - throw new FileNotFoundException($"Unable to find game file: {filePath}"); - return fileStream; - } - - public bool FileExists(string filePath, bool megFileOnly = false) - { - return MultiPassAction(filePath, actualPath => baseRepository.FileExists(actualPath, megFileOnly)); - } - - public Stream? TryOpenFile(string filePath, bool megFileOnly = false) - { - return MultiPassAction(filePath, path => - { - var stream = baseRepository.TryOpenFile(path, megFileOnly); - if (stream is null) - return (false, null); - return (true, stream); - }); - } - - protected abstract T? MultiPassAction(string inputPath, Func fileAction); - - protected bool MultiPassAction(string inputPath, Predicate fileAction) - { - return MultiPassAction(inputPath, path => - { - var result = fileAction(path); - return (result, result); - }); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/TextureRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/TextureRepository.cs deleted file mode 100644 index e5253e0..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Repositories/TextureRepository.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; - -namespace PG.StarWarsGame.Engine.Repositories; - -public class TextureRepository(IGameRepository baseRepository, IServiceProvider serviceProvider) : MultiPassRepository(baseRepository, serviceProvider) -{ - protected override T? MultiPassAction(string inputPath, Func fileAction) where T : default - { - if (FindTexture(inputPath, fileAction, out var result)) - return result; - - var ddsFilePath = ChangeExtensionTo(inputPath, ".dds"); - - if (FindTexture(ddsFilePath, fileAction, out result)) - return result; - - return default; - } - - private bool FindTexture(string inputPath, Func fileAction, out T? result) - { - result = default; - - var actionResult = fileAction(inputPath); - if (actionResult.success) - { - result = actionResult.result; - return true; - } - - var newInput = inputPath; - - // Only PG knows why they only search for backslash and not also forward slash, - // when in fact in other methods, they handle both. - var separatorIndex = inputPath.LastIndexOf('\\'); - if (separatorIndex != -1 && separatorIndex + 1 < inputPath.Length) - newInput = inputPath.Substring(separatorIndex + 1); - - var pathWithFolders = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", newInput); - actionResult = fileAction(pathWithFolders); - - if (actionResult.success) - { - result = actionResult.result; - return true; - } - - - return false; - } - - - private static string ChangeExtensionTo(string input, string extension) - { - // We cannot use Path.ChangeExtension as the PG implementation supports some strange things - // like that a string "c:\\file.tga\\" ending with a directory separator. The PG result will be - // "c:\\file.dds" while Path.ChangeExtension would return "c:\\file.tga\\.dds" - - // Also, while there are many cases, where method breaks (such as "c:/test.abc/path.dds"), - // it's the way how the engine works... - var firstPart = input.Split('.')[0]; - return firstPart + extension; - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs index e210518..8701551 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs @@ -1,25 +1,358 @@ using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace PG.StarWarsGame.Engine.Utilities; -// From https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Text/ValueStringBuilder.cs -internal ref struct ValueStringBuilder(Span initialBuffer) +internal ref struct ValueStringBuilder { - private readonly Span _chars = initialBuffer; - private int _pos = 0; + private char[]? _arrayToReturnToPool; + private Span _chars; + private int _pos; + + public ValueStringBuilder(Span initialBuffer) + { + _arrayToReturnToPool = null; + _chars = initialBuffer; + _pos = 0; + } + + public ValueStringBuilder(int initialCapacity) + { + _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity); + _chars = _arrayToReturnToPool; + _pos = 0; + } + + public int Length + { + get => _pos; + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= _chars.Length); + _pos = value; + } + } + + public int Capacity => _chars.Length; + + public void EnsureCapacity(int capacity) + { + // This is not expected to be called this with negative capacity + Debug.Assert(capacity >= 0); + + // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception. + if ((uint)capacity > (uint)_chars.Length) + Grow(capacity - _pos); + } + + /// + /// Get a pinnable reference to the builder. + /// Does not ensure there is a null char after + /// This overload is pattern matched in the C# 7.3+ compiler so you can omit + /// the explicit method call, and write eg "fixed (char* c = builder)" + /// + public ref char GetPinnableReference() + { + return ref MemoryMarshal.GetReference(_chars); + } + + /// + /// Get a pinnable reference to the builder. + /// + /// Ensures that the builder has a null char after + public ref char GetPinnableReference(bool terminate) + { + if (terminate) + { + EnsureCapacity(Length + 1); + _chars[Length] = '\0'; + } + return ref MemoryMarshal.GetReference(_chars); + } + + public ref char this[int index] + { + get + { + Debug.Assert(index < _pos); + return ref _chars[index]; + } + } public override string ToString() { - return _chars.Slice(0, _pos).ToString(); + string s = _chars.Slice(0, _pos).ToString(); + Dispose(); + return s; + } + + /// Returns the underlying storage of the builder. + public Span RawChars => _chars; + + /// + /// Returns a span around the contents of the builder. + /// + /// Ensures that the builder has a null char after + public ReadOnlySpan AsSpan(bool terminate) + { + if (terminate) + { + EnsureCapacity(Length + 1); + _chars[Length] = '\0'; + } + return _chars.Slice(0, _pos); + } + + public ReadOnlySpan AsSpan() => _chars.Slice(0, _pos); + public ReadOnlySpan AsSpan(int start) => _chars.Slice(start, _pos - start); + public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length); + + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (_chars.Slice(0, _pos).TryCopyTo(destination)) + { + charsWritten = _pos; + Dispose(); + return true; + } + else + { + charsWritten = 0; + Dispose(); + return false; + } + } + + public void Insert(int index, char value, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + _chars.Slice(index, count).Fill(value); + _pos += count; + } + + public void Insert(int index, string? s) + { + if (s == null) + { + return; + } + + int count = s.Length; + + if (_pos > (_chars.Length - count)) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + s +#if !NETCOREAPP + .AsSpan() +#endif + .CopyTo(_chars.Slice(index)); + _pos += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c) + { + int pos = _pos; + Span chars = _chars; + if ((uint)pos < (uint)chars.Length) + { + chars[pos] = c; + _pos = pos + 1; + } + else + { + GrowAndAppend(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string? s) + { + if (s == null) + { + return; + } + + int pos = _pos; + if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + { + _chars[pos] = s[0]; + _pos = pos + 1; + } + else + { + AppendSlow(s); + } + } + + private void AppendSlow(string s) + { + int pos = _pos; + if (pos > _chars.Length - s.Length) + { + Grow(s.Length); + } + + s +#if !NETCOREAPP + .AsSpan() +#endif + .CopyTo(_chars.Slice(pos)); + _pos += s.Length; + } + + public void Append(char c, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + Span dst = _chars.Slice(_pos, count); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + _pos += count; + } + + public unsafe void Append(char* value, int length) + { + int pos = _pos; + if (pos > _chars.Length - length) + { + Grow(length); + } + + Span dst = _chars.Slice(_pos, length); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = *value++; + } + _pos += length; } public void Append(scoped ReadOnlySpan value) { - var pos = _pos; + int pos = _pos; if (pos > _chars.Length - value.Length) - throw new InvalidOperationException("Value string builder is too small."); + { + Grow(value.Length); + } value.CopyTo(_chars.Slice(_pos)); _pos += value.Length; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + int origPos = _pos; + if (origPos > _chars.Length - length) + { + Grow(length); + } + + _pos = origPos + length; + return _chars.Slice(origPos, length); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowAndAppend(char c) + { + Grow(1); + Append(c); + } + + /// + /// Resize the internal buffer either by doubling current buffer size or + /// by adding to + /// whichever is greater. + /// + /// + /// Number of chars requested beyond current position. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacityBeyondPos) + { + Debug.Assert(additionalCapacityBeyondPos > 0); + Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed."); + + const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength + + // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try + // to double the size if possible, bounding the doubling to not go beyond the max array length. + int newCapacity = (int)Math.Max( + (uint)(_pos + additionalCapacityBeyondPos), + Math.Min((uint)_chars.Length * 2, ArrayMaxLength)); + + // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative. + // This could also go negative if the actual required length wraps around. + char[] poolArray = ArrayPool.Shared.Rent(newCapacity); + + _chars.Slice(0, _pos).CopyTo(poolArray); + + char[]? toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + /// + /// Removes a range of characters from this builder. + /// + /// The inclusive index from where the string gets removed. + /// The length of the slice to remove. + /// + /// This method will not affect the internal size of the string. + /// + public void Remove(int startIndex, int length) + { + if (length == 0) + return; + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), "The given length can't be negative."); + if (startIndex < 0) + throw new ArgumentOutOfRangeException(nameof(startIndex), "The given start index can't be negative."); + if (length > Length - startIndex) + throw new ArgumentOutOfRangeException(nameof(length), $"The given Span ({startIndex}..{length})length is outside the the represented string."); + if (Length == length && startIndex == 0) + { + Length = 0; + return; + } + var currentLength = Length; + var remaining = _chars.Slice(startIndex + length, currentLength - length); + var toOverwrite = _chars.Slice(startIndex); + remaining.CopyTo(toOverwrite); + _pos = currentLength - length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + char[]? toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs index 36cb12c..d5106c2 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 { - IPetroglyphXmlFileParser GetFileParser(IXmlParserErrorListener? listener = null); + IPetroglyphXmlFileParser CreateFileParser(IXmlParserErrorListener? listener = null); } \ 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 new file mode 100644 index 0000000..7cf9c4e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/NamedXmlObject.cs @@ -0,0 +1,13 @@ +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 Crc32 Crc32 { get; } = nameCrc; + + public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name)); +} \ 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/Data/CommandBarComponentParser.cs new file mode 100644 index 0000000..ca35b6e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections.ObjectModel; +using System.Xml.Linq; +using PG.Commons.Collections; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.Xml.Tags; +using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; + +public sealed class CommandBarComponentParser( + IReadOnlyValueListDictionary parsedElements, + IServiceProvider serviceProvider, + IXmlParserErrorListener? listener = null) + : XmlObjectParser(parsedElements, serviceProvider, listener) +{ + public override CommandBarComponentData Parse(XElement element, out Crc32 nameCrc) + { + var name = GetXmlObjectName(element, out nameCrc); + var component = new CommandBarComponentData(name, nameCrc, XmlLocationInfo.FromElement(element)); + Parse(component, element, default); + component.CoerceValues(); + return component; + } + + protected override bool ParseTag(XElement tag, CommandBarComponentData componentData) + { + switch (tag.Name.LocalName) + { + case CommandBarComponentTags.SelectedTextureName: + componentData.SelectedTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.BlankTextureName: + componentData.BlankTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.IconAlternateTextureName: + componentData.IconAlternateTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.MouseOverTextureName: + componentData.MouseOverTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.BarTextureName: + componentData.BarTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.BarOverlayName: + componentData.BarOverlayNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.AlternateFontName: + componentData.AlternateFontNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.TooltipText: + componentData.TooltipTexts = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.LowerEffectTextureName: + componentData.LowerEffectTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.UpperEffectTextureName: + componentData.UpperEffectTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.OverlayTextureName: + componentData.OverlayTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case CommandBarComponentTags.Overlay2TextureName: + componentData.Overlay2TextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + + case CommandBarComponentTags.IconTextureName: + componentData.IconTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.DisabledTextureName: + componentData.DisabledTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.FlashTextureName: + componentData.FlashTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.BuildTextureName: + componentData.BuildTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.ModelName: + componentData.ModelName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.BoneName: + componentData.BoneName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.CursorTextureName: + componentData.CursorTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.FontName: + componentData.FontName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.ClickSfx: + componentData.ClickSfx = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.MouseOverSfx: + componentData.MouseOverSfx = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.RightClickSfx: + componentData.RightClickSfx = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.Type: + componentData.Type = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.Group: + componentData.Group = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case CommandBarComponentTags.AssociatedText: + componentData.AssociatedText = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + + case CommandBarComponentTags.DragAndDrop: + componentData.DragAndDrop = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.DragSelect: + componentData.DragSelect = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Receptor: + componentData.Receptor = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Toggle: + componentData.Toggle = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Tab: + componentData.Tab = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Hidden: + componentData.Hidden = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ClearColor: + componentData.ClearColor = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Disabled: + componentData.Disabled = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.SwapTexture: + componentData.SwapTexture = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.DrawAdditive: + componentData.DrawAdditive = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Editable: + componentData.Editable = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.TextOutline: + componentData.TextOutline = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Stackable: + componentData.Stackable = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ModelOffsetX: + componentData.ModelOffsetX = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ModelOffsetY: + componentData.ModelOffsetY = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ScaleModelX: + componentData.ScaleModelX = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ScaleModelY: + componentData.ScaleModelY = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.Collideable: + componentData.Collideable = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.TextEmboss: + componentData.TextEmboss = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ShouldGhost: + componentData.ShouldGhost = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.GhostBaseOnly: + componentData.GhostBaseOnly = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.CrossFade: + componentData.CrossFade = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.LeftJustified: + componentData.LeftJustified = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.RightJustified: + componentData.RightJustified = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.NoShell: + componentData.NoShell = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.SnapDrag: + componentData.SnapDrag = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.SnapLocation: + componentData.SnapLocation = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.OffsetRender: + componentData.OffsetRender = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.BlinkFade: + componentData.BlinkFade = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.NoHiddenCollision: + componentData.NoHiddenCollision = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ManualOffset: + componentData.ManualOffset = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.SelectedAlpha: + componentData.SelectedAlpha = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.PixelAlign: + componentData.PixelAlign = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.CanDragStack: + componentData.CanDragStack = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.CanAnimate: + componentData.CanAnimate = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.LoopAnim: + componentData.LoopAnim = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.SmoothBar: + componentData.SmoothBar = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.OutlinedBar: + componentData.OutlinedBar = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.DragBack: + componentData.DragBack = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.LowerEffectAdditive: + componentData.LowerEffectAdditive = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.UpperEffectAdditive: + componentData.UpperEffectAdditive = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ClickShift: + componentData.ClickShift = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.TutorialScene: + componentData.TutorialScene = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.DialogScene: + componentData.DialogScene = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.ShouldRenderAtDragPos: + componentData.ShouldRenderAtDragPos = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.DisableDarken: + componentData.DisableDarken = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.AnimateBack: + componentData.AnimateBack = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case CommandBarComponentTags.AnimateUpperEffect: + componentData.AnimateUpperEffect = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + + case CommandBarComponentTags.Size: + componentData.Size = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.TextOffset: + componentData.TextOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.TextOffset2: + componentData.TextOffset2 = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.Offset: + componentData.Offset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.DefaultOffset: + componentData.DefaultOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.DefaultOffsetWidescreen: + componentData.DefaultOffsetWidescreen = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.IconOffset: + componentData.IconOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.MouseOverOffset: + componentData.MouseOverOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.DisabledOffset: + componentData.DisabledOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.BuildDialOffset: + componentData.BuildDialOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.BuildDial2Offset: + componentData.BuildDial2Offset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.LowerEffectOffset: + componentData.LowerEffectOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.UpperEffectOffset: + componentData.UpperEffectOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.OverlayOffset: + componentData.OverlayOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + case CommandBarComponentTags.Overlay2Offset: + componentData.Overlay2Offset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + return true; + + case CommandBarComponentTags.MaxTextLength: + componentData.MaxTextLength = PrimitiveParserProvider.UIntParser.Parse(tag); + return true; + case CommandBarComponentTags.FontPointSize: + componentData.FontPointSize = (int)PrimitiveParserProvider.UIntParser.Parse(tag); + return true; + + case CommandBarComponentTags.Scale: + componentData.Scale = PrimitiveParserProvider.FloatParser.Parse(tag); + return true; + case CommandBarComponentTags.BlinkRate: + componentData.BlinkRate = PrimitiveParserProvider.FloatParser.Parse(tag); + return true; + case CommandBarComponentTags.MaxTextWidth: + componentData.MaxTextWidth = PrimitiveParserProvider.FloatParser.Parse(tag); + return true; + case CommandBarComponentTags.BlinkDuration: + componentData.BlinkDuration = PrimitiveParserProvider.FloatParser.Parse(tag); + return true; + case CommandBarComponentTags.ScaleDuration: + componentData.ScaleDuration = PrimitiveParserProvider.FloatParser.Parse(tag); + return true; + case CommandBarComponentTags.AnimFps: + componentData.AnimFps = PrimitiveParserProvider.FloatParser.Parse(tag); + return true; + + case CommandBarComponentTags.BaseLayer: + componentData.BaseLayer = (int)PrimitiveParserProvider.UIntParser.Parse(tag); + return true; + case CommandBarComponentTags.MaxBarLevel: + componentData.MaxBarLevel = (int)PrimitiveParserProvider.UIntParser.Parse(tag); + return true; + + + default: return true; + } + } + + 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/GameConstantsParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs index 212bab1..7ec0cbe 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs @@ -1,22 +1,22 @@ using System; using System.Xml.Linq; +using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; internal class GameConstantsParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) : - PetroglyphXmlFileParser(serviceProvider, listener) + PetroglyphXmlFileParser(serviceProvider, listener) { - public override GameConstants Parse(XElement element) + protected override GameConstantsXml Parse(XElement element, string fileName) { - return new GameConstants(); + return new GameConstantsXml(); } - protected override void Parse(XElement element, IValueListDictionary parsedElements) + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { throw new NotSupportedException(); } 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 831cc55..ee97771 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs @@ -1,54 +1,97 @@ using System; using System.Xml.Linq; +using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; +using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; +public static class GameObjectXmlTags +{ + public const string LandTerrainModelMapping = "Land_Terrain_Model_Mapping"; + public const string GalacticModelName = "Galactic_Model_Name"; + public const string DestroyedGalacticModelName = "Destroyed_Galactic_Model_Name"; + public const string LandModelName = "Land_Model_Name"; + public const string SpaceModelName = "Space_Model_Name"; + public const string ModelName = "Model_Name"; + public const string TacticalModelName = "Tactical_Model_Name"; + public const string GalacticFleetOverrideModelName = "Galactic_Fleet_Override_Model_Name"; + public const string GuiModelName = "GUI_Model_Name"; + public const string LandModelAnimOverrideName = "Land_Model_Anim_Override_Name"; + public const string XxxSpaceModelName = "xxxSpace_Model_Name"; + public const string DamagedSmokeAssetName = "Damaged_Smoke_Asset_Name"; +} + public sealed class GameObjectParser( IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) : XmlObjectParser(parsedElements, serviceProvider, listener) -{ - protected override IPetroglyphXmlElementParser? GetParser(string tag) - { - switch (tag) - { - case "Land_Terrain_Model_Mapping": - return PrimitiveParserProvider.CommaSeparatedStringKeyValueListParser; - case "Galactic_Model_Name": - case "Destroyed_Galactic_Model_Name": - case "Land_Model_Name": - case "Space_Model_Name": - case "Model_Name": - case "Tactical_Model_Name": - case "Galactic_Fleet_Override_Model_Name": - case "GUI_Model_Name": - case "GUI_Model": - case "Land_Model_Anim_Override_Name": - case "xxxSpace_Model_Name": - case "Damaged_Smoke_Asset_Name": - return PrimitiveParserProvider.StringParser; - default: - return null; - } - } - +{ public override GameObject Parse(XElement element, out Crc32 nameCrc) { - var properties = ParseXmlElement(element); - var name = GetNameAttributeValue(element); - nameCrc = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.PGCrc32Encoding); + var name = GetXmlObjectName(element, out nameCrc); var type = GetTagName(element); var objectType = EstimateType(type); - var gameObject = new GameObject(type, name, nameCrc, objectType, properties, XmlLocationInfo.FromElement(element)); + var gameObject = new GameObject(type, name, nameCrc, objectType, XmlLocationInfo.FromElement(element)); + + Parse(gameObject, element, default); + return gameObject; } + protected override bool ParseTag(XElement tag, GameObject xmlObject) + { + switch (tag.Name.LocalName) + { + case GameObjectXmlTags.LandTerrainModelMapping: + var mappingValue = PrimitiveParserProvider.CommaSeparatedStringKeyValueListParser.Parse(tag); + var dict = xmlObject.InternalLandTerrainModelMapping; + foreach (var keyValuePair in mappingValue) + { + if (!dict.ContainsKey(keyValuePair.key)) + dict.Add(keyValuePair.key, keyValuePair.value); + } + return true; + case GameObjectXmlTags.GalacticModelName: + xmlObject.GalacticModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.DestroyedGalacticModelName: + xmlObject.DestroyedGalacticModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.LandModelName: + xmlObject.LandModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.SpaceModelName: + xmlObject.SpaceModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.ModelName: + xmlObject.ModelName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.TacticalModelName: + xmlObject.TacticalModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.GalacticFleetOverrideModelName: + xmlObject.GalacticFleetOverrideModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.GuiModelName: + xmlObject.GuiModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.LandModelAnimOverrideName: + xmlObject.LandAnimOverrideModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.XxxSpaceModelName: + xmlObject.XxxSpaceModeModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case GameObjectXmlTags.DamagedSmokeAssetName: + xmlObject.DamagedSmokeAssetModel = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + default: return true; // TODO: Once parsing is complete, switch to false. + } + } + private static GameObjectType EstimateType(string tagName) { if (tagName.StartsWith("Props_")) 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 ffb7e18..6df3ecb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs @@ -1,11 +1,12 @@ using System; +using System.Collections.ObjectModel; using System.Xml.Linq; +using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; +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; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -14,109 +15,188 @@ public sealed class SfxEventParser( IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) : XmlObjectParser(parsedElements, serviceProvider, listener) -{ - protected override IPetroglyphXmlElementParser? GetParser(string tag) +{ + public override SfxEvent Parse(XElement element, out Crc32 nameCrc) + { + var name = GetXmlObjectName(element, out nameCrc); + var sfxEvent = new SfxEvent(name, nameCrc, XmlLocationInfo.FromElement(element)); + Parse(sfxEvent, element, default); + ValidateValues(sfxEvent, element); + sfxEvent.CoerceValues(); + return sfxEvent; + } + + private void ValidateValues(SfxEvent sfxEvent, XElement element) { - switch (tag) + 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) + { + switch (tag.Name.LocalName) { - case SfxEventXmlTags.UsePreset: case SfxEventXmlTags.OverlapTest: + sfxEvent.OverlapTestName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; case SfxEventXmlTags.ChainedSfxEvent: - return PrimitiveParserProvider.StringParser; + sfxEvent.ChainedSfxEventName = PrimitiveParserProvider.StringParser.Parse(tag); + return true; + case SfxEventXmlTags.UsePreset: + { + var presetName = PrimitiveParserProvider.StringParser.Parse(tag); + var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.DefaultPGEncoding); + if (presetNameCrc != default && ParsedElements.TryGetFirstValue(presetNameCrc, out var preset)) + sfxEvent.ApplyPreset(preset); + else + { + OnParseError(new XmlParseErrorEventArgs(tag, + XmlParseErrorKind.MissingReference, $"Cannot to find preset '{presetName}' for SFXEvent '{sfxEvent.Name}'")); + } + return true; + } + case SfxEventXmlTags.IsPreset: + sfxEvent.IsPreset = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.Is3D: + sfxEvent.Is3D = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.Is2D: + sfxEvent.Is2D = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.IsGui: + sfxEvent.IsGui = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.IsHudVo: + sfxEvent.IsHudVo = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.IsUnitResponseVo: + sfxEvent.IsUnitResponseVo = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.IsAmbientVo: + sfxEvent.IsAmbientVo = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.Localize: + sfxEvent.IsLocalized = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.PlaySequentially: + sfxEvent.PlaySequentially = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; case SfxEventXmlTags.KillsPreviousObjectSFX: - return PrimitiveParserProvider.BooleanParser; + sfxEvent.KillsPreviousObjectsSfx = PrimitiveParserProvider.BooleanParser.Parse(tag); + return true; + case SfxEventXmlTags.Samples: + sfxEvent.Samples = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; case SfxEventXmlTags.PreSamples: + sfxEvent.PreSamples = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; case SfxEventXmlTags.PostSamples: + sfxEvent.PostSamples = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; case SfxEventXmlTags.TextID: - return PrimitiveParserProvider.LooseStringListParser; + sfxEvent.LocalizedTextIDs = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + return true; + case SfxEventXmlTags.Priority: + sfxEvent.Priority = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinPriorityValue, SfxEvent.MaxPriorityValue); + return true; case SfxEventXmlTags.MinPitch: + sfxEvent.MinPitch = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); + return true; case SfxEventXmlTags.MaxPitch: + sfxEvent.MaxPitch = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); + return true; case SfxEventXmlTags.MinPan2D: + sfxEvent.MinPan2D = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); + return true; case SfxEventXmlTags.MaxPan2D: + sfxEvent.MaxPan2D = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); + return true; case SfxEventXmlTags.PlayCount: + sfxEvent.PlayCount = (sbyte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.InfinitivePlayCount, sbyte.MaxValue); + return true; case SfxEventXmlTags.MaxInstances: - return PrimitiveParserProvider.IntParser; + sfxEvent.MaxInstances = (sbyte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinMaxInstances, sbyte.MaxValue); + return true; + case SfxEventXmlTags.Probability: + sfxEvent.Probability = PrimitiveParserProvider.Max100ByteParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxProbability); + return true; case SfxEventXmlTags.MinVolume: + sfxEvent.MinVolume = PrimitiveParserProvider.Max100ByteParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); + return true; case SfxEventXmlTags.MaxVolume: - return PrimitiveParserProvider.Max100ByteParser; + sfxEvent.MaxVolume = PrimitiveParserProvider.Max100ByteParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); + return true; + case SfxEventXmlTags.MinPredelay: + sfxEvent.MinPredelay = PrimitiveParserProvider.UIntParser.Parse(tag); + return true; case SfxEventXmlTags.MaxPredelay: + sfxEvent.MaxPredelay = PrimitiveParserProvider.UIntParser.Parse(tag); + return true; case SfxEventXmlTags.MinPostdelay: + sfxEvent.MinPostdelay = PrimitiveParserProvider.UIntParser.Parse(tag); + return true; case SfxEventXmlTags.MaxPostdelay: - return PrimitiveParserProvider.UIntParser; + sfxEvent.MaxPostdelay = PrimitiveParserProvider.UIntParser.Parse(tag); + return true; + case SfxEventXmlTags.LoopFadeInSeconds: + sfxEvent.LoopFadeInSeconds = PrimitiveParserProvider.FloatParser.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); + return true; case SfxEventXmlTags.LoopFadeOutSeconds: + sfxEvent.LoopFadeOutSeconds = PrimitiveParserProvider.FloatParser.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); + return true; case SfxEventXmlTags.VolumeSaturationDistance: - return PrimitiveParserProvider.FloatParser; - default: - return null; - } - } - - public override SfxEvent Parse(XElement element, out Crc32 nameCrc) - { - var name = GetNameAttributeValue(element); - nameCrc = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.PGCrc32Encoding); - - var properties = ParseXmlElement(element); - - return new SfxEvent(name, nameCrc, properties, XmlLocationInfo.FromElement(element)); - } - - protected override bool OnParsed(XElement element, string tag, object value, ValueListDictionary properties, string? outerElementName) - { - if (tag == SfxEventXmlTags.UsePreset) - { - var presetName = value as string; - var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.PGCrc32Encoding); - if (presetNameCrc != default && ParsedElements.TryGetFirstValue(presetNameCrc, out var preset)) - CopySfxPreset(properties, preset); - else - { - var location = XmlLocationInfo.FromElement(element); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.MissingReference, - $"Cannot to find preset '{presetName}' for SFXEvent '{outerElementName ?? "NONE"}'")); - } + // 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 = PrimitiveParserProvider.FloatParser.ParseAtLeast(tag, SfxEvent.MinVolumeSaturation); + return true; + default: return false; } - return true; - } - - private static void CopySfxPreset(ValueListDictionary currentXmlProperties, SfxEvent preset) - { - /* - * The engine also copies the Use_Preset *of* the preset, (which almost most cases is null) - * As this would cause that the SfxEvent using the preset, would not have a reference to its original preset, we do not copy the preset - * Example: - * - * - * Preset Yes - * 90 - * - * - * Engine Behavior: SFXEvent instance(Name: A, Use_Preset: null, Min_Volume: 90) - * PG.StarWarsGame.Engine Behavior: SFXEvent instance(Name: A, Use_Preset: Preset, Min_Volume: 90) - */ - - foreach (var keyValuePair in preset.XmlProperties) - { - if (keyValuePair.Key is SfxEventXmlTags.UsePreset or SfxEventXmlTags.IsPreset) - continue; - currentXmlProperties.Add(keyValuePair.Key, keyValuePair.Value); - } - - currentXmlProperties.Add(SfxEventXmlTags.PresetXRef, preset); } public override SfxEvent Parse(XElement element) => throw new NotSupportedException(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs new file mode 100644 index 0000000..9c82a16 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs @@ -0,0 +1,38 @@ +using System; +using System.Xml.Linq; +using PG.Commons.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, IXmlParserErrorListener? listener = null) + : PetroglyphXmlFileParser(serviceProvider, listener) +{ + private readonly IXmlParserErrorListener? _listener = listener; + + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) + { + var parser = new CommandBarComponentParser(parsedElements, ServiceProvider, _listener); + + 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); + } + } + + protected override CommandBarComponentData Parse(XElement element, string fileName) + { + throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileFileParser.cs index 4eaedc7..358077a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/GameObjectFileFileParser.cs @@ -1,9 +1,9 @@ using System; using System.Xml.Linq; +using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; +using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.Xml.Parsers.Data; -using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; @@ -14,7 +14,7 @@ internal class GameObjectFileFileParser(IServiceProvider serviceProvider, IXmlPa { private readonly IXmlParserErrorListener? _listener = listener; - protected override void Parse(XElement element, IValueListDictionary parsedElements) + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { var parser = new GameObjectParser(parsedElements, ServiceProvider, _listener); @@ -25,7 +25,7 @@ protected override void Parse(XElement element, IValueListDictionary(serviceProvider, listener) +{ + protected override GuiDialogsXml Parse(XElement element, string fileName) + { + var textures = ParseTextures(element.Element("Textures"), fileName); + return new GuiDialogsXml(textures, XmlLocationInfo.FromElement(element)); + } + + private GuiDialogsXmlTextureData ParseTextures(XElement? element, string fileName) + { + if (element is null) + { + OnParseError(new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, null), XmlParseErrorKind.MissingNode, + "Expected node is missing.")); + return new GuiDialogsXmlTextureData([], new XmlLocationInfo(fileName, null)); + } + + var textures = new List(); + + GetAttributeValue(element, "File", out var megaTexture); + GetAttributeValue(element, "Compressed_File", out var compressedMegaTexture); + + foreach (var texture in element.Elements()) + textures.Add(ParseTexture(texture)); + + if (textures.Count == 0) + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MissingNode, + "Textures must contain at least one child node.")); + + return new GuiDialogsXmlTextureData(textures, XmlLocationInfo.FromElement(element)) + { + MegaTexture = megaTexture, + CompressedMegaTexture = compressedMegaTexture + }; + } + + private XmlComponentTextureData ParseTexture(XElement texture) + { + var componentId = GetTagName(texture); + var textures = new ValueListDictionary(); + + foreach (var entry in texture.Elements()) + textures.Add(entry.Name.ToString(), PrimitiveParserProvider.StringParser.Parse(entry)); + + return new XmlComponentTextureData(componentId, textures, XmlLocationInfo.FromElement(texture)); + } + + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) + { + throw new NotSupportedException(); + } +} \ 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 index 7edc637..1de28b8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs @@ -1,10 +1,10 @@ using System; using System.Xml.Linq; using Microsoft.Extensions.Logging; +using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; +using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Xml.Parsers.Data; -using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; @@ -15,26 +15,19 @@ internal class SfxEventFileParser(IServiceProvider serviceProvider, IXmlParserEr { private readonly IXmlParserErrorListener? _listener = listener; - protected override void Parse(XElement element, IValueListDictionary parsedElements) + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { var parser = new SfxEventParser(parsedElements, ServiceProvider, _listener); if (!element.HasElements) { - OnParseError(XmlParseErrorEventArgs.FromEmptyRoot(XmlLocationInfo.FromElement(element).XmlFile, element)); + OnParseError(XmlParseErrorEventArgs.FromEmptyRoot(element)); return; } foreach (var xElement in element.Elements()) { var sfxEvent = parser.Parse(xElement, out var nameCrc); - if (nameCrc == default) - { - var location = XmlLocationInfo.FromElement(xElement); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, xElement, XmlParseErrorKind.MissingAttribute, - $"SFXEvent has no name at location '{location}'")); - } - parsedElements.Add(nameCrc, sfxEvent); } @@ -42,11 +35,11 @@ protected override void Parse(XElement element, IValueListDictionary? XmlParseError; + + void ParseEntriesFromContainerXml( + string xmlFile, + IXmlParserErrorListener listener, + IGameRepository gameRepository, + string lookupPath, + ValueListDictionary entries, + Action? onFileParseAction = null); +} \ 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 new file mode 100644 index 0000000..f7fbf79 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -0,0 +1,109 @@ +using System; +using System.Linq; +using System.Xml; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.Commons.Collections; +using PG.Commons.Hashing; +using PG.Commons.Services; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Engine.Xml.Parsers; + +internal sealed class XmlContainerContentParser(IServiceProvider serviceProvider) : ServiceBase(serviceProvider), IXmlContainerContentParser +{ + public event EventHandler? XmlParseError; + + private readonly IPetroglyphXmlFileParserFactory _fileParserFactory = serviceProvider.GetRequiredService(); + + public void ParseEntriesFromContainerXml( + string xmlFile, + IXmlParserErrorListener listener, + IGameRepository gameRepository, + string lookupPath, + ValueListDictionary entries, + Action? onFileParseAction = null) + { + var containerParser = _fileParserFactory.CreateFileParser(listener); + Logger.LogDebug($"Parsing container data '{xmlFile}'"); + + using var containerStream = gameRepository.TryOpenFile(xmlFile); + if (containerStream == null) + { + listener.OnXmlParseError(containerParser, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + Logger.LogWarning($"Could not find XML file '{xmlFile}'"); + + var args = new XmlContainerParserErrorEventArgs(xmlFile, true) + { + // No reason to continue + Continue = false + }; + XmlParseError?.Invoke(this, args); + return; + } + + XmlFileContainer? container; + try + { + container = containerParser.ParseFile(containerStream); + if (container is null) + throw new XmlException($"Unable to parse XML container file '{xmlFile}'."); + } + catch (XmlException e) + { + var args = new XmlContainerParserErrorEventArgs(e, xmlFile, 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(listener); + + foreach (var file in xmlFiles) + { + if (onFileParseAction is not null) + onFileParseAction(file); + + using var fileStream = gameRepository.TryOpenFile(file); + + if (fileStream is null) + { + listener.OnXmlParseError(parser, XmlParseErrorEventArgs.FromMissingFile(file)); + Logger.LogWarning($"Could not find XML file '{file}'"); + + var args = new XmlContainerParserErrorEventArgs(file); + XmlParseError?.Invoke(this, args); + + if (args.Continue) + continue; + return; + } + + Logger.LogDebug($"Parsing File '{file}'"); + + try + { + parser.ParseFile(fileStream, entries); + } + catch (XmlException e) + { + listener.OnXmlParseError(parser, new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); + + var args = new XmlContainerParserErrorEventArgs(e, file); + XmlParseError?.Invoke(this, args); + + if (!args.Continue) + return; + } + } + } +} \ 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/XmlContainerParserErrorEventArgs.cs new file mode 100644 index 0000000..3661c21 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs @@ -0,0 +1,40 @@ +using System.Diagnostics.CodeAnalysis; +using System.Xml; + +namespace PG.StarWarsGame.Engine.Xml.Parsers; + +internal class XmlContainerParserErrorEventArgs +{ + private bool _continue = true; + + public bool Continue + { + get => _continue; + // Once this is set to false, there is no way back. + set => _continue &= value; + } + + public bool IsContainer { get; } + + public string File { get; } + + [MemberNotNullWhen(true, nameof(Exception))] + public bool IsError => Exception is not null; + + public bool IsFileNotFound => !IsError; + + public XmlException? Exception { get; } + + public XmlContainerParserErrorEventArgs(XmlException exception, string file, bool container = false) + { + Exception = exception; + File = file; + IsContainer = container; + } + + public XmlContainerParserErrorEventArgs(string file, bool container = false) + { + File = file; + IsContainer = container; + } +} \ 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 5c3142c..ce2a89f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs @@ -1,52 +1,76 @@ using System; using System.Xml.Linq; using Microsoft.Extensions.DependencyInjection; +using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.DataTypes; -using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers; -public abstract class XmlObjectParser(IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) - : PetroglyphXmlElementParser(serviceProvider, listener) where T : XmlObject -{ - protected IReadOnlyValueListDictionary ParsedElements { get; } = parsedElements ?? throw new ArgumentNullException(nameof(parsedElements)); +public abstract class XmlObjectParser( + IReadOnlyValueListDictionary parsedElements, + IServiceProvider serviceProvider, + IXmlParserErrorListener? listener = null) + : XmlObjectParser(parsedElements, serviceProvider, listener) where TObject : XmlObject +{ + protected void Parse(TObject xmlObject, XElement element) + { + Parse(xmlObject, element, EmptyParseState.Instance); + } - protected ICrc32HashingService HashingService { get; } = serviceProvider.GetRequiredService(); + protected sealed override bool ParseTag(XElement tag, TObject xmlObject, in EmptyParseState parseState) + { + return ParseTag(tag, xmlObject); + } - public abstract T Parse(XElement element, out Crc32 nameCrc); + protected abstract bool ParseTag(XElement tag, TObject xmlObject); +} - protected abstract IPetroglyphXmlElementParser? GetParser(string tag); +public readonly struct EmptyParseState +{ + public static readonly EmptyParseState Instance = new(); +} - protected ValueListDictionary ParseXmlElement(XElement element, string? name = null) - { - var xmlProperties = new ValueListDictionary(); - foreach (var elm in element.Elements()) - { - var tagName = elm.Name.LocalName; - var parser = GetParser(tagName); +public abstract class XmlObjectParser( + IReadOnlyValueListDictionary parsedElements, + IServiceProvider serviceProvider, + IXmlParserErrorListener? listener = null) + : PetroglyphXmlElementParser(serviceProvider, listener) where TObject : XmlObject +{ + protected IReadOnlyValueListDictionary ParsedElements { get; } = + parsedElements ?? throw new ArgumentNullException(nameof(parsedElements)); + + protected ICrc32HashingService HashingService { get; } = serviceProvider.GetRequiredService(); + + public abstract TObject Parse(XElement element, out Crc32 nameCrc); - if (parser is null) + protected void Parse(TObject xmlObject, XElement element, in TParseState state) + { + foreach (var tag in element.Elements()) + { + if (!ParseTag(tag, xmlObject, state)) { - // TODO - //var nameOrPosition = name ?? XmlLocationInfo.FromElement(element).ToString(); - //Logger?.LogWarning($"Unable to find parser for tag '{tagName}' in element '{nameOrPosition}'"); - continue; + OnParseError(new XmlParseErrorEventArgs(tag, XmlParseErrorKind.UnknownNode, + $"The node '{tag.Name}' is not supported.")); + break; } - - var value = parser.Parse(elm); - - if (OnParsed(elm, tagName, value, xmlProperties, name)) - xmlProperties.Add(tagName, value); } - return xmlProperties; } - protected virtual bool OnParsed(XElement element, string tag, object value, ValueListDictionary properties, string? outerElementName) + protected abstract bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState); + + protected string GetXmlObjectName(XElement element, out Crc32 nameCrc32) { - return true; + GetNameAttributeValue(element, out var name); + nameCrc32 = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.DefaultPGEncoding); + if (nameCrc32 == default) + { + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"Name for XmlObject cannot be empty.")); + } + + return name; } } \ 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 7a283e5..d608a16 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs @@ -1,5 +1,9 @@ using System; -using PG.StarWarsGame.Engine.DataTypes; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.GameConstants; +using PG.StarWarsGame.Engine.GameObjects; +using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Engine.Xml.Parsers.Data; using PG.StarWarsGame.Engine.Xml.Parsers.File; using PG.StarWarsGame.Files.XML; @@ -11,7 +15,7 @@ namespace PG.StarWarsGame.Engine.Xml; internal sealed class PetroglyphXmlFileParserFactory(IServiceProvider serviceProvider) : IPetroglyphXmlFileParserFactory { - public IPetroglyphXmlFileParser GetFileParser(IXmlParserErrorListener? listener = null) + public IPetroglyphXmlFileParser CreateFileParser(IXmlParserErrorListener? listener = null) { return (IPetroglyphXmlFileParser)GetFileParser(typeof(T), listener); } @@ -21,15 +25,21 @@ private IPetroglyphXmlFileParser GetFileParser(Type type, IXmlParserErrorListene if (type == typeof(XmlFileContainer)) return new XmlFileContainerParser(serviceProvider, listener); - if (type == typeof(GameConstants)) + if (type == typeof(GameConstantsXml)) return new GameConstantsParser(serviceProvider, listener); + if (type == typeof(GuiDialogsXml)) + return new GuiDialogParser(serviceProvider, listener); + if (type == typeof(GameObject)) return new GameObjectFileFileParser(serviceProvider, listener); if (type == typeof(SfxEvent)) return new SfxEventFileParser(serviceProvider, listener); + if (type == typeof(CommandBarComponentData)) + return new CommandBarComponentFileParser(serviceProvider, listener); + throw new ParserNotFoundException(type); } } \ 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/Tags/CommandBarComponentTags.cs new file mode 100644 index 0000000..bc5498b --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/CommandBarComponentTags.cs @@ -0,0 +1,108 @@ +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/Tags/ComponentTextureKeyExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/ComponentTextureKeyExtensions.cs new file mode 100644 index 0000000..c072c90 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Tags/ComponentTextureKeyExtensions.cs @@ -0,0 +1,114 @@ +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/XmlObject.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObject.cs new file mode 100644 index 0000000..71840fe --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/XmlObject.cs @@ -0,0 +1,12 @@ +using PG.StarWarsGame.Files.XML; + +namespace PG.StarWarsGame.Engine.Xml; + +public abstract class XmlObject(XmlLocationInfo location) +{ + public XmlLocationInfo Location { get; } = location; + + internal virtual void CoerceValues() + { + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs index 3471ccd..ea13493 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs @@ -1,6 +1,6 @@ using System.IO; -using PG.Commons.Binary; using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; using PG.StarWarsGame.Files.ChunkFiles.Binary.Reader; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs index d43116b..9e2df93 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs @@ -3,9 +3,9 @@ using System.Diagnostics; using System.IO; using System.Text; -using PG.Commons.Binary; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Services; +using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; namespace PG.StarWarsGame.Files.ALO.Binary.Reader; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs index 1f487f7..200a918 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Text; -using PG.Commons.Binary; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Services; +using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; namespace PG.StarWarsGame.Files.ALO.Binary.Reader; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs index 43df83c..4e88947 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.CodeAnalysis; -using PG.Commons.Files; namespace PG.StarWarsGame.Files.ALO.Files; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/IAloFile.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/IAloFile.cs index 4812535..db28fe0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/IAloFile.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/IAloFile.cs @@ -1,5 +1,4 @@ -using PG.Commons.Files; -using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ChunkFiles.Files; namespace PG.StarWarsGame.Files.ALO.Files; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Models/AloModelFile.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Models/AloModelFile.cs index 07a8723..48408d1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Models/AloModelFile.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Models/AloModelFile.cs @@ -1,5 +1,4 @@ using System; -using PG.Commons.Files; using PG.StarWarsGame.Files.ALO.Data; namespace PG.StarWarsGame.Files.ALO.Files.Models; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Particles/AloParticleFile.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Particles/AloParticleFile.cs index 19008ab..2178e2d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Particles/AloParticleFile.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Particles/AloParticleFile.cs @@ -1,5 +1,4 @@ using System; -using PG.Commons.Files; using PG.StarWarsGame.Files.ALO.Data; namespace PG.StarWarsGame.Files.ALO.Files.Particles; 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 490459c..4fd3dd4 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 @@ -28,4 +28,7 @@ + + + \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs index d21191b..d98e3f4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs @@ -1,8 +1,6 @@ using System; using System.IO; -using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; -using PG.Commons.Files; using PG.Commons.Services; using PG.Commons.Utilities; using PG.StarWarsGame.Files.ALO.Binary; @@ -56,7 +54,7 @@ public IAloFile Load(Stre var alo = reader.Read(); - var filePath = GetFilePath(stream, out var isInMeg); + var filePath = stream.GetFilePath(out var isInMeg); var fileInfo = new AloFileInformation(filePath, isInMeg, contentInfo); if (alo is AlamoModel model) @@ -67,23 +65,7 @@ public IAloFile Load(Stre throw new InvalidOperationException(); } - - private static string GetFilePath(Stream stream, out bool isInMeg) - { - isInMeg = false; - if (stream is FileStream fileStream) - return fileStream.Name; - if (stream is FileSystemStream fileSystemStream) - return fileSystemStream.Name; - if (stream is IMegFileDataStream megFileDataStream) - { - isInMeg = true; - return megFileDataStream.EntryPath; - } - - throw new InvalidOperationException(); - } - + public AloContentInfo GetAloContentInfo(Stream stream) { return _aloContentIdentifier.GetContentInfo(stream); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs index ad0c099..e0cd995 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs @@ -1,5 +1,4 @@ using System.IO; -using PG.Commons.Files; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; using PG.StarWarsGame.Files.ALO.Files.Models; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkFileReaderBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkFileReaderBase.cs index 782be52..01d085a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkFileReaderBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkFileReaderBase.cs @@ -15,9 +15,9 @@ IChunkData IChunkFileReader.Read() return Read(); } - protected override void DisposeManagedResources() + protected override void DisposeResources() { - base.DisposeManagedResources(); + base.DisposeResources(); ChunkReader.Dispose(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs index 60105e5..ca188c3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs @@ -2,28 +2,20 @@ using System.IO; using System.Text; using AnakinRaW.CommonUtilities; -using PG.Commons.Utilities; +using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; namespace PG.StarWarsGame.Files.ChunkFiles.Binary.Reader; public class ChunkReader : DisposableObject { - private readonly BinaryReader _binaryReader; + private readonly PetroglyphBinaryReader _binaryReader; public ChunkReader(Stream stream, bool leaveOpen = false) { if (stream == null) throw new ArgumentNullException(nameof(stream)); - - // Using default encoding here is OK as we don't read strings using the .NET methods. - _binaryReader = new BinaryReader(stream, Encoding.Default, leaveOpen); - } - - protected override void DisposeManagedResources() - { - base.DisposeManagedResources(); - _binaryReader.Dispose(); + _binaryReader = new PetroglyphBinaryReader(stream, leaveOpen); } public ChunkMetadata ReadChunk() @@ -79,17 +71,29 @@ public void Skip(int bytesToSkip) public string ReadString(int size, Encoding encoding, bool zeroTerminated, ref int readSize) { - var value = _binaryReader.ReadString(size, encoding, zeroTerminated); + var value = ReadString(encoding, size, zeroTerminated); readSize += size; return value; } public string ReadString(int size, Encoding encoding, bool zeroTerminated) { - var value = _binaryReader.ReadString(size, encoding, zeroTerminated); + var value = ReadString(encoding, size, zeroTerminated); return value; } + private string ReadString(Encoding encoding, int size, bool zeroTerminated) + { + try + { + return _binaryReader.ReadString(encoding, size, zeroTerminated); + } + catch (Exception e) + { + throw new BinaryCorruptedException($"Unable to read string: {e.Message}", e); + } + } + public ChunkMetadata? TryReadChunk() { var _ = 0; @@ -100,16 +104,15 @@ public string ReadString(int size, Encoding encoding, bool zeroTerminated) { if (_binaryReader.BaseStream.Position == _binaryReader.BaseStream.Length) return null; - try - { - var chunk = ReadChunk(); - size += 8; - return chunk; - } - catch (EndOfStreamException e) - { - Console.WriteLine(e); - throw; - } + + var chunk = ReadChunk(); + size += 8; + return chunk; + } + + protected override void DisposeResources() + { + base.DisposeResources(); + _binaryReader.Dispose(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Files/IChunkFile.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Files/IChunkFile.cs index 619e0ba..8b03a3b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Files/IChunkFile.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Files/IChunkFile.cs @@ -1,5 +1,4 @@ -using PG.Commons.Files; -using PG.StarWarsGame.Files.ChunkFiles.Data; +using PG.StarWarsGame.Files.ChunkFiles.Data; namespace PG.StarWarsGame.Files.ChunkFiles.Files; 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 d909270..8daab61 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 @@ -16,6 +16,9 @@ snupkg - + + + + \ 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 index 1ea7d70..afaa2ce 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorEventArgs.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorEventArgs.cs @@ -6,7 +6,7 @@ namespace PG.StarWarsGame.Files.XML.ErrorHandling; public class XmlParseErrorEventArgs : EventArgs { - public string File { get; } + public XmlLocationInfo Location { get; } public XElement? Element { get; } @@ -14,24 +14,29 @@ public class XmlParseErrorEventArgs : EventArgs public string Message { get; } - public XmlParseErrorEventArgs(string file, XElement? element, XmlParseErrorKind errorKind, string message) + public XmlParseErrorEventArgs(XElement element, XmlParseErrorKind errorKind, string message) { - ThrowHelper.ThrowIfNullOrEmpty(file); - File = file; - Element = element; + 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(file, null, XmlParseErrorKind.MissingFile, $"XML file '{file}' not found."); + return new XmlParseErrorEventArgs(new XmlLocationInfo(file, null), XmlParseErrorKind.MissingFile, "XML file not found."); } - public static XmlParseErrorEventArgs FromEmptyRoot(string file, XElement element) + public static XmlParseErrorEventArgs FromEmptyRoot(XElement element) { - ThrowHelper.ThrowIfNullOrEmpty(file); - return new XmlParseErrorEventArgs(file, element, XmlParseErrorKind.EmptyRoot, $"XML file '{file}' has an empty root node."); + 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/ErrorHandling/XmlParseErrorKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs index ea329d9..8465c2f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs @@ -39,4 +39,12 @@ public enum XmlParseErrorKind /// The XML file does not start with the XML header. /// DataBeforeHeader = 8, + /// + /// The XML file is missing an expected node. + /// + MissingNode = 9, + /// + /// The XML element contains an unsupported tag. + /// + UnknownNode = 10 } \ 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 32d7562..9f9f1cb 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 @@ -14,14 +14,19 @@ true snupkg + true + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs index 2c81137..5a3875a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs @@ -1,4 +1,5 @@ using System.IO; +using PG.Commons.Collections; using PG.Commons.Hashing; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -10,7 +11,7 @@ public interface IPetroglyphXmlFileParser : IPetroglyphXmlParser public interface IPetroglyphXmlFileParser : IPetroglyphXmlParser, IPetroglyphXmlFileParser { - public new T ParseFile(Stream stream); + public new T? ParseFile(Stream stream); public void ParseFile(Stream stream, IValueListDictionary parsedEntries); } \ 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 f9b64f9..be0d855 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs @@ -4,11 +4,15 @@ using System.Text; using System.Xml; using System.Xml.Linq; -using AnakinRaW.CommonUtilities.FileSystem; using Microsoft.Extensions.DependencyInjection; using PG.Commons.Hashing; using PG.Commons.Utilities; using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.Commons.Collections; + +#if NETSTANDARD2_0 +using AnakinRaW.CommonUtilities.FileSystem; +#endif namespace PG.StarWarsGame.Files.XML.Parsers; @@ -22,22 +26,33 @@ public abstract class PetroglyphXmlFileParser(IServiceProvider serviceProvide public T ParseFile(Stream xmlStream) { - var root = GetRootElement(xmlStream); - return root is null ? default! : Parse(root); + var root = GetRootElement(xmlStream, out var fileName); + if (root is null) + OnParseError(new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, 0), XmlParseErrorKind.EmptyRoot, + "Unable to get root node from XML file.")); + return root is null ? default! : Parse(root, fileName); } public void ParseFile(Stream xmlStream, IValueListDictionary parsedEntries) { - var root = GetRootElement(xmlStream); + var root = GetRootElement(xmlStream, out var fileName); if (root is not null) - Parse(root, parsedEntries); + Parse(root, parsedEntries, fileName); } - protected abstract void Parse(XElement element, IValueListDictionary parsedElements); + public sealed override T Parse(XElement element) + { + var fileName = GetStrippedFileName(XmlLocationInfo.FromElement(element).XmlFile); + return Parse(element, fileName); + } + + protected abstract T Parse(XElement element, string fileName); + + protected abstract void Parse(XElement element, IValueListDictionary parsedElements, string fileName); - private XElement? GetRootElement(Stream xmlStream) + private XElement? GetRootElement(Stream xmlStream, out string fileName) { - var fileName = GetStrippedFileName(xmlStream.GetFilePath()); + fileName = GetStrippedFileName(xmlStream.GetFilePath()); if (string.IsNullOrEmpty(fileName)) throw new InvalidOperationException("Unable to parse XML from unnamed stream. Either parse from a file or MEG stream."); @@ -59,7 +74,7 @@ public void ParseFile(Stream xmlStream, IValueListDictionary parsedEnt return doc.Root; } - private string GetStrippedFileName(string filePath) + protected string GetStrippedFileName(string filePath) { if (!_fileSystem.Path.IsPathFullyQualified(filePath)) return filePath; @@ -87,8 +102,8 @@ private void SkipLeadingWhiteSpace(string fileName, Stream stream) } if (count != 0) - _listener?.OnXmlParseError(this, new XmlParseErrorEventArgs(fileName, null, - XmlParseErrorKind.DataBeforeHeader, $"XML header is not the first entry of the file '{fileName}'")); + _listener?.OnXmlParseError(this, new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, 0), + XmlParseErrorKind.DataBeforeHeader, $"XML header is not the first entry of the XML file.")); stream.Position = count; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs index 21b4b43..cdebbf3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers.Primitives; +using System.Linq; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -32,8 +33,34 @@ protected virtual void OnParseError(XmlParseErrorEventArgs e) _errorListener?.OnXmlParseError(this, e); } + protected string GetTagName(XElement element) + { + return element.Name.LocalName; + } + + protected bool GetNameAttributeValue(XElement element, out string value) + { + return GetAttributeValue(element, "Name", out value!, string.Empty); + } + + protected bool GetAttributeValue(XElement element, string attribute, out string? value, string? defaultValue = null) + { + 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}'")); + return false; + } + + value = nameAttribute.Value; + return true; + } + object IPetroglyphXmlParser.Parse(XElement element) { - return Parse(element); + return Parse(element)!; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs index 3be1a0f..0725d5e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs @@ -18,5 +18,7 @@ public interface IPrimitiveParserProvider PetroglyphXmlBooleanParser BooleanParser { get; } + PetroglyphXmlVector2FParser Vector2FParser { get; } + CommaSeparatedStringKeyValueListParser CommaSeparatedStringKeyValueListParser { get; } } \ No newline at end of file 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 68d8ec7..541a148 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs @@ -18,9 +18,8 @@ public override byte Parse(XElement element) var asByte = (byte)intValue; if (intValue != asByte) { - var location = XmlLocationInfo.FromElement(element); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 255) but got value '{intValue}' at {location}")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"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 6691061..8c60c60 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs @@ -12,19 +12,37 @@ internal PetroglyphXmlFloatParser(IServiceProvider serviceProvider, IPrimitiveXm { } - public override float Parse(XElement element) + public float Parse(string value, XElement element) { // The engine always loads FP numbers a long double and then converts that result to float - if (!double.TryParse(element.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue)) + if (!double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue)) { - var location = XmlLocationInfo.FromElement(element); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.MalformedValue, - $"Expected double but got value '{element.Value}' at {location}")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, + $"Expected double but got value '{value}'.")); return 0.0f; } + return (float)doubleValue; } + public override float Parse(XElement element) + { + return Parse(element.Value, element); + } + + public float ParseAtLeast(XElement element, float minValue) + { + var value = Parse(element); + 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}'.")); + } + + return corrected; + } + protected override void OnParseError(XmlParseErrorEventArgs e) { Logger?.LogWarning(e.Message); 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 02400b8..a360211 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -2,6 +2,7 @@ using System; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; +using System.Runtime.CompilerServices; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; @@ -19,18 +20,53 @@ public override int Parse(XElement element) if (!int.TryParse(element.Value, out var i)) { - var location = XmlLocationInfo.FromElement(element); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.MalformedValue, - $"Expected integer but got '{element.Value}' at {location}")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, + $"Expected integer but got '{element.Value}'.")); return 0; } 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) + { + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"Expected integer between {minValue} and {maxValue} but got value '{value}'.")); + } + return clamped; + } + protected override void OnParseError(XmlParseErrorEventArgs e) { Logger?.LogWarning(e.Message); base.OnParseError(e); } -} \ No newline at end of file +} + +internal static class PGMath +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(int value, int min, int max) + { + if (min > max) + throw new ArgumentException("min cannot be larger than max."); + if (value < min) + return min; + return value > max ? max : value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(byte value, byte min, byte max) + { + if (min > max) + throw new ArgumentException("min cannot be larger than max."); + if (value < min) + return min; + return value > max ? max : value; + } +} + 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 82fbb38..6948952 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; @@ -31,8 +32,7 @@ public override IList Parse(XElement element) if (trimmedValued.Length > 0x2000) { - var location = XmlLocationInfo.FromElement(element); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.TooLongData, + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, $"Input value is too long '{trimmedValued.Length}' at {XmlLocationInfo.FromElement(element)}")); return Array.Empty(); 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 af72d8b..8c6df64 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs @@ -9,7 +9,6 @@ public sealed class PetroglyphXmlMax100ByteParser : PetroglyphXmlPrimitiveElemen { internal PetroglyphXmlMax100ByteParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) { - } public override byte Parse(XElement element) @@ -22,23 +21,36 @@ public override byte Parse(XElement element) var asByte = (byte)intValue; if (intValue != asByte) { - var location = XmlLocationInfo.FromElement(element); - - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 255) but got value '{intValue}' at {location}")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"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) { - var location = XmlLocationInfo.FromElement(element); - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 100) but got value '{asByte}' at {location}")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"Expected a byte value (0 - 100) but got value '{asByte}'.")); } return asByte; } + public byte ParseWithRange(XElement element, byte minValue, byte maxValue) + { + if (maxValue > 100) + Logger?.LogWarning("Upper bound for clamp range is above 100 for a parser that is meant to be capped to value 100."); + + var value = Parse(element); + + 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}'.")); + } + return clamped; + } + protected override void OnParseError(XmlParseErrorEventArgs e) { Logger?.LogWarning(e.Message); 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 2206f12..c80fddf 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs @@ -18,10 +18,8 @@ public override uint Parse(XElement element) var asUint = (uint)intValue; if (intValue != asUint) { - var location = XmlLocationInfo.FromElement(element); - - OnParseError(new XmlParseErrorEventArgs(location.XmlFile, element, XmlParseErrorKind.InvalidValue, - $"Expected unsigned integer but got '{intValue}' at {location}")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"Expected unsigned integer but got '{intValue}'.")); } return asUint; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs new file mode 100644 index 0000000..a099ad1 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs @@ -0,0 +1,34 @@ +using System; +using System.Numerics; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; + +public sealed class PetroglyphXmlVector2FParser : PetroglyphXmlPrimitiveElementParser +{ + internal PetroglyphXmlVector2FParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + { + } + + public override Vector2 Parse(XElement element) + { + var listOfValues = PrimitiveParserProvider.LooseStringListParser.Parse(element); + + if (listOfValues.Count == 0) + return default; + + var floatParser = PrimitiveParserProvider.FloatParser; + + if (listOfValues.Count == 1) + { + var value = floatParser.Parse(listOfValues[0], element); + return new Vector2(value, 0.0f); + } + + var value1 = floatParser.Parse(listOfValues[0], element); + var value2 = floatParser.Parse(listOfValues[1], element); + + return new Vector2(value1, value2); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs index 747c83a..3d17ebc 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs @@ -17,6 +17,8 @@ internal class PrimitiveParserProvider(IServiceProvider serviceProvider) : IPrim private PetroglyphXmlByteParser _byteParser = null!; private PetroglyphXmlMax100ByteParser _max100ByteParser = null!; private PetroglyphXmlBooleanParser _booleanParser = null!; + private PetroglyphXmlVector2FParser _vector2FParser = null!; + private CommaSeparatedStringKeyValueListParser _commaSeparatedStringKeyValueListParser = null!; public PetroglyphXmlStringParser StringParser => @@ -43,6 +45,9 @@ internal class PrimitiveParserProvider(IServiceProvider serviceProvider) : IPrim public PetroglyphXmlBooleanParser BooleanParser => LazyInitializer.EnsureInitialized(ref _booleanParser, () => new PetroglyphXmlBooleanParser(serviceProvider, _primitiveParserErrorListener)); + public PetroglyphXmlVector2FParser Vector2FParser => + LazyInitializer.EnsureInitialized(ref _vector2FParser, () => new PetroglyphXmlVector2FParser(serviceProvider, _primitiveParserErrorListener)); + public CommaSeparatedStringKeyValueListParser CommaSeparatedStringKeyValueListParser => LazyInitializer.EnsureInitialized(ref _commaSeparatedStringKeyValueListParser, () => new CommaSeparatedStringKeyValueListParser(serviceProvider, _primitiveParserErrorListener)); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs index 20aeace..3769213 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Xml.Linq; +using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Files.XML.ErrorHandling; @@ -11,12 +12,12 @@ public class XmlFileContainerParser(IServiceProvider serviceProvider, IXmlParser { protected override bool LoadLineInfo => false; - protected override void Parse(XElement element, IValueListDictionary parsedElements) + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { throw new NotSupportedException(); } - public override XmlFileContainer Parse(XElement element) + protected override XmlFileContainer Parse(XElement element, string fileNaem) { var files = new List(); foreach (var child in element.Elements()) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ValueListDictionary.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ValueListDictionary.cs deleted file mode 100644 index 73f8aab..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ValueListDictionary.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using AnakinRaW.CommonUtilities.Collections; - -namespace PG.StarWarsGame.Files.XML; - - -public interface IReadOnlyValueListDictionary : IEnumerable> where TKey : notnull -{ - ICollection Values { get; } - - ICollection Keys { get; } - - int Count { get; } - - TKey this[int index] { get; } - - TValue GetValueAtIndex(int index); - - bool ContainsKey(TKey key); - - ReadOnlyFrugalList GetValues(TKey key); - - TValue GetLastValue(TKey key); - - TValue GetFirstValue(TKey key); - - bool TryGetFirstValue(TKey key, [NotNullWhen(true)] out TValue value); - - bool TryGetLastValue(TKey key, [NotNullWhen(true)] out TValue value); - - bool TryGetValues(TKey key, out ReadOnlyFrugalList values); -} - -public interface IValueListDictionary : IReadOnlyValueListDictionary where TKey : notnull -{ - bool Add(TKey key, TValue value); -} - -// NOT THREAD-SAFE! -public class ValueListDictionary : IValueListDictionary where TKey : notnull -{ - private readonly List _insertionTrackingList = new(); - private readonly Dictionary _singleValueDictionary = new (); - private readonly Dictionary> _multiValueDictionary = new(); - - private readonly EqualityComparer _equalityComparer = EqualityComparer.Default; - - public int Count => _insertionTrackingList.Count; - - public TKey this[int index] => _insertionTrackingList[index]; - - public ICollection Keys => _singleValueDictionary.Keys.Concat(_multiValueDictionary.Keys).ToList(); - - public ICollection Values => this.Select(x => x.Value).ToList(); - - public TValue GetValueAtIndex(int index) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index)); - - var key = this[index]; - if (_singleValueDictionary.TryGetValue(key, out var value)) - return value; - - if (index == 0) - return _multiValueDictionary[key].First(); - - if (index == Count - 1) - return _multiValueDictionary[key].Last(); - - var keyCount = 0; - foreach (var k in _insertionTrackingList.Take(index + 1)) - { - if (_equalityComparer.Equals(key, k)) - keyCount++; - } - - return _multiValueDictionary[key][keyCount - 1]; - } - - public bool ContainsKey(TKey key) - { - return _singleValueDictionary.ContainsKey(key) || _multiValueDictionary.ContainsKey(key); - } - - public bool Add(TKey key, TValue value) - { - if (key is null) - throw new ArgumentNullException(nameof(key)); - - _insertionTrackingList.Add(key); - - if (!_singleValueDictionary.ContainsKey(key)) - { - if (!_multiValueDictionary.TryGetValue(key, out var list)) - { - _singleValueDictionary.Add(key, value); - return false; - } - - list.Add(value); - return true; - } - - Debug.Assert(_multiValueDictionary.ContainsKey(key) == false); - - var firstValue = _singleValueDictionary[key]; - _singleValueDictionary.Remove(key); - - _multiValueDictionary.Add(key, [ - firstValue, - value - ]); - - return true; - } - - public TValue GetLastValue(TKey key) - { - if (_singleValueDictionary.TryGetValue(key, out var value)) - return value; - - if (_multiValueDictionary.TryGetValue(key, out var valueList)) - return valueList.Last(); - - throw new KeyNotFoundException($"The key '{key}' was not found."); - } - - public TValue GetFirstValue(TKey key) - { - if (_singleValueDictionary.TryGetValue(key, out var value)) - return value; - - if (_multiValueDictionary.TryGetValue(key, out var valueList)) - return valueList.First(); - - throw new KeyNotFoundException($"The key '{key}' was not found."); - } - - public ReadOnlyFrugalList GetValues(TKey key) - { - if (TryGetValues(key, out var values)) - return values; - - throw new KeyNotFoundException($"The key '{key}' was not found."); - - } - - public bool TryGetFirstValue(TKey key, [NotNullWhen(true)] out TValue value) - { - if (_singleValueDictionary.TryGetValue(key, out value!)) - return true; - - if (_multiValueDictionary.TryGetValue(key, out var valueList)) - { - value = valueList.First()!; - return true; - } - - return false; - } - - public bool TryGetLastValue(TKey key, [NotNullWhen(true)] out TValue value) - { - if (_singleValueDictionary.TryGetValue(key, out value!)) - return true; - - if (_multiValueDictionary.TryGetValue(key, out var valueList)) - { - value = valueList.Last()!; - return true; - } - - return false; - } - - public bool TryGetValues(TKey key, out ReadOnlyFrugalList values) - { - if (_singleValueDictionary.TryGetValue(key, out var value)) - { - values = new ReadOnlyFrugalList(value); - return true; - } - - if (_multiValueDictionary.TryGetValue(key, out var valueList)) - { - values = new ReadOnlyFrugalList(valueList); - return true; - } - - values = ReadOnlyFrugalList.Empty; - return false; - } - - public IEnumerator> GetEnumerator() - { - return new Enumerator(this); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public struct Enumerator : IEnumerator> - { - private Dictionary.Enumerator _singleEnumerator; - private Dictionary>.Enumerator _multiEnumerator; - private List.Enumerator _currentListEnumerator = default; - private bool _isMultiEnumeratorActive = false; - - internal Enumerator(ValueListDictionary valueListDictionary) - { - _singleEnumerator = valueListDictionary._singleValueDictionary.GetEnumerator(); - _multiEnumerator = valueListDictionary._multiValueDictionary.GetEnumerator(); - } - - public KeyValuePair Current => - _isMultiEnumeratorActive - ? new KeyValuePair(_multiEnumerator.Current.Key, _currentListEnumerator.Current) - : _singleEnumerator.Current; - - object IEnumerator.Current => Current; - - public bool MoveNext() - { - if (_singleEnumerator.MoveNext()) - return true; - - if (_isMultiEnumeratorActive) - { - if (_currentListEnumerator.MoveNext()) - return true; - _isMultiEnumeratorActive = false; - } - - if (_multiEnumerator.MoveNext()) - { - _currentListEnumerator = _multiEnumerator.Current.Value.GetEnumerator(); - _isMultiEnumeratorActive = true; - return _currentListEnumerator.MoveNext(); - } - - return false; - } - - public void Reset() - { - throw new NotSupportedException(); - } - - public void Dispose() - { - _singleEnumerator.Dispose(); - _multiEnumerator.Dispose(); - } - } -} - -public static class ValueListDictionaryExtensions -{ - public static IEnumerable AggregateValues( - this IReadOnlyValueListDictionary valueListDictionary, - ISet keys, Predicate filter, - AggregateStrategy aggregateStrategy) - where TKey : notnull - where T : TValue - { - foreach (var key in keys) - { - if (!valueListDictionary.ContainsKey(key)) - continue; - if (aggregateStrategy == AggregateStrategy.MultipleValuesPerKey) - { - foreach (var value in valueListDictionary.GetValues(key)) - { - if (value is not null) - { - var typedValue = (T)value; - if (filter(typedValue)) - yield return typedValue; - } - - } - } - else - { - var value = aggregateStrategy == AggregateStrategy.FirstValuePerKey - ? valueListDictionary.GetFirstValue(key) - : valueListDictionary.GetLastValue(key); - if (value is not null) - { - var typedValue = (T)value; - if (filter(typedValue)) - yield return typedValue; - } - } - } - } - - public enum AggregateStrategy - { - FirstValuePerKey, - LastValuePerKey, - MultipleValuesPerKey, - } -} \ No newline at end of file From 30950e21b6cafda01563a42aa24580a7a7057d69 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Feb 2025 19:05:16 +0100 Subject: [PATCH 02/34] update dep --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 82ddda9..2dd57ff 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -20,14 +20,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all @@ -70,6 +70,4 @@ - - From e87b0767e27e979240fc5b7fa1a2b7aad3e59081 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Feb 2025 19:31:38 +0100 Subject: [PATCH 03/34] update to net 9.0 --- .../PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 7b1bb16..d927b2b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -1,6 +1,6 @@  - netstandard2.0;netstandard2.1;net8.0 + netstandard2.0;netstandard2.1;net9.0 PG.StarWarsGame.Engine PG.StarWarsGame.Engine AlamoEngineTools.PG.StarWarsGame.Engine From 868f3426edb25c42ffd951b204e390b910b1b373 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 4 Feb 2025 11:05:45 +0100 Subject: [PATCH 04/34] rename --- .../PG.StarWarsGame.Engine/Database/GameInitializer.cs | 2 +- ...bjectTypeTypeGameManager.cs => GameObjectTypeGameManager.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/{GameObjectTypeTypeGameManager.cs => GameObjectTypeGameManager.cs} (90%) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index 7ad3ee9..5ba85cd 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -76,7 +76,7 @@ public async Task InitializeAsync(DatabaseErrorListenerWrapper er var commandBarManager = new CommandBarGameManager(repository, errorListener, serviceProvider); await commandBarManager.InitializeAsync( _cancellationTokenSource.Token); - var gameObjetTypeManager = new GameObjectTypeTypeGameManager(repository, errorListener, serviceProvider); + var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorListener, serviceProvider); await gameObjetTypeManager.InitializeAsync( _cancellationTokenSource.Token); repository.Seal(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs similarity index 90% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 8b0abb1..6bb10a6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -10,7 +10,7 @@ namespace PG.StarWarsGame.Engine.GameObjects; -internal class GameObjectTypeTypeGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) +internal class GameObjectTypeGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) : GameManagerBase(repository, errorListener, serviceProvider), IGameObjectTypeGameManager { protected override async Task InitializeCoreAsync(CancellationToken token) From 7cd6411fea20d8a28e4a416428e9f6b5eee9eef7 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 4 Feb 2025 11:09:46 +0100 Subject: [PATCH 05/34] rename --- src/ModVerify.CliApp/Program.cs | 4 ++-- .../PG.StarWarsGame.Files.ALO/AloServiceContribution.cs | 2 +- .../PG.StarWarsGame.Files.XML/XmlServiceContribution.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index a8da9eb..b397068 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -109,9 +109,9 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl serviceCollection.SupportMTD(); serviceCollection.SupportMEG(); - AloServiceContribution.ContributeServices(serviceCollection); + serviceCollection.SupportALO(); + serviceCollection.SupportXML(); PetroglyphCommons.ContributeServices(serviceCollection); - XmlServiceContribution.ContributeServices(serviceCollection); PetroglyphEngineServiceContribution.ContributeServices(serviceCollection); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/AloServiceContribution.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/AloServiceContribution.cs index 2b5b880..98b608b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/AloServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/AloServiceContribution.cs @@ -7,7 +7,7 @@ namespace PG.StarWarsGame.Files.ALO; public static class AloServiceContribution { - public static void ContributeServices(IServiceCollection serviceCollection) + public static void SupportALO(this IServiceCollection serviceCollection) { serviceCollection.AddSingleton(sp => new AloFileService(sp)); serviceCollection.AddSingleton(sp => new AloFileReaderFactory(sp)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs index 6fb18d4..7c431be 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs @@ -9,7 +9,7 @@ namespace PG.StarWarsGame.Files.XML; public static class XmlServiceContribution { - public static void ContributeServices(IServiceCollection serviceCollection) + public static void SupportXML(this IServiceCollection serviceCollection) { serviceCollection.AddSingleton(_ => new PrimitiveXmlParserErrorBroker()); serviceCollection.AddSingleton(sp => sp.GetRequiredService()); From cfb4e1708f166c50f50b631683c9d155fc51da8f Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 4 Feb 2025 11:17:29 +0100 Subject: [PATCH 06/34] move --- .../IO/Repositories/GameRepository.cs | 2 +- .../Xml/Parsers/XmlContainerContentParser.cs | 1 + .../Xml/PetroglyphXmlParserFactory.cs | 2 +- .../{ => Data}/XmlFileContainer.cs | 2 +- .../Primitives/PetroglyphXmlIntegerParser.cs | 28 ++----------------- .../PetroglyphXmlMax100ByteParser.cs | 1 + .../Primitives/XmlFileContainerParser.cs | 1 + .../Utilities/PGMath.cs | 27 ++++++++++++++++++ 8 files changed, 35 insertions(+), 29 deletions(-) rename src/PetroglyphTools/PG.StarWarsGame.Files.XML/{ => Data}/XmlFileContainer.cs (77%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index cd6f68a..5b9051a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -16,7 +16,7 @@ using PG.StarWarsGame.Files.MEG.Files; using PG.StarWarsGame.Files.MEG.Services; using PG.StarWarsGame.Files.MEG.Services.Builder.Normalization; -using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; namespace PG.StarWarsGame.Engine.IO.Repositories; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs index f7fbf79..d84dd96 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -8,6 +8,7 @@ using PG.Commons.Services; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Engine.Xml.Parsers; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs index d608a16..0162c20 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs @@ -6,7 +6,7 @@ using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Engine.Xml.Parsers.Data; using PG.StarWarsGame.Engine.Xml.Parsers.File; -using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; using PG.StarWarsGame.Files.XML.Parsers.Primitives; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileContainer.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileContainer.cs similarity index 77% rename from src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileContainer.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileContainer.cs index d42d8ad..fad88d4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlFileContainer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileContainer.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace PG.StarWarsGame.Files.XML; +namespace PG.StarWarsGame.Files.XML.Data; public class XmlFileContainer(IList files) { 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 a360211..7a0407c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -2,7 +2,7 @@ using System; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; -using System.Runtime.CompilerServices; +using PG.StarWarsGame.Files.XML.Utilities; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; @@ -45,28 +45,4 @@ protected override void OnParseError(XmlParseErrorEventArgs e) Logger?.LogWarning(e.Message); base.OnParseError(e); } -} - -internal static class PGMath -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Clamp(int value, int min, int max) - { - if (min > max) - throw new ArgumentException("min cannot be larger than max."); - if (value < min) - return min; - return value > max ? max : value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte Clamp(byte value, byte min, byte max) - { - if (min > max) - throw new ArgumentException("min cannot be larger than max."); - if (value < min) - return min; - return value > max ? max : value; - } -} - +} \ 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 8c6df64..7f6d2ad 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs @@ -2,6 +2,7 @@ using System; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Utilities; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs index 3769213..11308e2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs @@ -3,6 +3,7 @@ using System.Xml.Linq; using PG.Commons.Collections; using PG.Commons.Hashing; +using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs new file mode 100644 index 0000000..bc40317 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Utilities/PGMath.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.CompilerServices; + +namespace PG.StarWarsGame.Files.XML.Utilities; + +internal static class PGMath +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(int value, int min, int max) + { + if (min > max) + throw new ArgumentException("min cannot be larger than max."); + if (value < min) + return min; + return value > max ? max : value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(byte value, byte min, byte max) + { + if (min > max) + throw new ArgumentException("min cannot be larger than max."); + if (value < min) + return min; + return value > max ? max : value; + } +} \ No newline at end of file From 7a9abee15f1c7b08de81275b286bb8230ea2dfcd Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Thu, 6 Feb 2025 19:53:18 +0100 Subject: [PATCH 07/34] new xml parser structure --- .../Properties/launchSettings.json | 2 +- ...=> ConcurrentGameDatabaseErrorReporter.cs} | 6 +- src/ModVerify/VerifyGamePipeline.cs | 2 +- .../Audio/Sfx/SfxEventGameManager.cs | 12 +- .../CommandBar/CommandBarGameManager.cs | 12 +- .../ErrorReporting/DatabaseErrorListener.cs | 12 -- .../DatabaseErrorListenerWrapper.cs | 61 ------ .../ErrorReporting/DatabaseErrorReporter.cs | 12 ++ .../DatabaseErrorReporterWrapper.cs | 52 +++++ .../ErrorReporting/IDatabaseErrorListener.cs | 7 - .../ErrorReporting/IDatabaseErrorReporter.cs | 8 + .../Database/GameDatabaseService.cs | 2 +- .../Database/GameInitializationOptions.cs | 2 +- .../Database/GameInitializer.cs | 16 +- .../Database/GameManagerBase.cs | 10 +- .../GameConstants/GameConstants.cs | 4 +- .../GameObjects/GameObjectTypeGameManager.cs | 10 +- .../GuiDialog/GuiDialogGameManager.cs | 4 +- .../GuiDialogGameManager_Initialization.cs | 30 ++- .../IO/Repositories/FocGameRepository.cs | 4 +- .../IO/Repositories/GameRepository.cs | 12 +- .../IO/Repositories/GameRepositoryFactory.cs | 4 +- .../IO/Repositories/IGameRepositoryFactory.cs | 2 +- .../Xml/IPetroglyphXmlFileParserFactory.cs | 2 +- .../Parsers/Data/CommandBarComponentParser.cs | 203 +++++++++--------- .../Xml/Parsers/Data/GameConstantsParser.cs | 11 +- .../Xml/Parsers/Data/GameObjectParser.cs | 29 +-- .../Xml/Parsers/Data/SfxEventParser.cs | 73 +++---- .../File/CommandBarComponentFileParser.cs | 13 +- ...eFileParser.cs => GameObjectFileParser.cs} | 13 +- .../Xml/Parsers/File/GuiDialogParser.cs | 13 +- .../Xml/Parsers/File/SfxEventFileParser.cs | 22 +- .../Xml/Parsers/IXmlContainerContentParser.cs | 4 +- .../Xml/Parsers/XmlContainerContentParser.cs | 20 +- .../Xml/Parsers/XmlObjectParser.cs | 8 +- .../Xml/PetroglyphXmlParserFactory.cs | 34 +-- ...leContainer.cs => XmlFileListContainer.cs} | 2 +- .../IPrimitiveXmlErrorParserProvider.cs | 3 - .../IPrimitiveXmlParserErrorListener.cs | 3 - .../ErrorHandling/IXmlParserErrorListener.cs | 8 - .../ErrorHandling/IXmlParserErrorReporter.cs | 6 + .../PrimitiveXmlErrorReporter.cs | 21 ++ .../PrimitiveXmlParserErrorBroker.cs | 13 -- .../ErrorHandling/XmlErrorEventHandler.cs | 6 +- .../ErrorHandling/XmlErrorReporter.cs | 28 +++ .../PG.StarWarsGame.Files.XML.csproj | 3 - ....StarWarsGame.Files.XML.csproj.DotSettings | 2 + .../Base/IPetroglyphXmlElementParser.cs | 8 + .../Parsers/Base/IPetroglyphXmlFileParser.cs | 8 + .../Base/PetroglyphXmlFileParserBase.cs | 84 ++++++++ .../Parsers/Base/PetroglyphXmlParserBase.cs | 53 +++++ .../Parsers/IPetroglyphXmlElementParser.cs | 5 - .../IPetroglyphXmlFileContainerParser.cs | 10 + .../Parsers/IPetroglyphXmlFileParser.cs | 17 -- .../Parsers/IPetroglyphXmlParser.cs | 13 -- .../Parsers/PetroglyphXmlElementParser.cs | 19 +- .../PetroglyphXmlFileContainerParser.cs | 21 ++ .../Parsers/PetroglyphXmlFileParser.cs | 108 +--------- .../Parsers/PetroglyphXmlParser.cs | 66 ------ .../PetroglyphXmlPrimitiveElementParser.cs | 12 -- .../CommaSeparatedStringKeyValueListParser.cs | 10 +- .../Primitives/IPrimitiveParserProvider.cs | 24 --- .../PetroglyphPrimitiveXmlParser.cs | 10 + .../Primitives/PetroglyphXmlBooleanParser.cs | 7 +- .../Primitives/PetroglyphXmlByteParser.cs | 18 +- .../Primitives/PetroglyphXmlFloatParser.cs | 13 +- .../Primitives/PetroglyphXmlIntegerParser.cs | 18 +- .../PetroglyphXmlLooseStringListParser.cs | 18 +- .../PetroglyphXmlMax100ByteParser.cs | 28 +-- .../Primitives/PetroglyphXmlStringParser.cs | 10 +- .../PetroglyphXmlUnsignedIntegerParser.cs | 18 +- .../Primitives/PetroglyphXmlVector2FParser.cs | 23 +- .../Primitives/PrimitiveParserProvider.cs | 53 ----- .../Primitives/XmlFileContainerParser.cs | 34 --- .../Parsers/XmlFileListParser.cs | 28 +++ .../XmlServiceContribution.cs | 5 - 76 files changed, 695 insertions(+), 872 deletions(-) rename src/ModVerify/Reporting/{ConcurrentGameDatabaseErrorListener.cs => ConcurrentGameDatabaseErrorReporter.cs} (72%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/{GameObjectFileFileParser.cs => GameObjectFileParser.cs} (63%) rename src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/{XmlFileContainer.cs => XmlFileListContainer.cs} (70%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlErrorParserProvider.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlParserErrorListener.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorListener.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlParserErrorBroker.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlElementParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileContainerParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlPrimitiveElementParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index d84156a..7f5c34d 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive": { "commandName": "Project", - "commandLineArgs": "-o verifyResults --minFailSeverity Information --baseline c:/test/focBaseline.json" + "commandLineArgs": "-o verifyResults --minFailSeverity Information" }, "FromModPath": { diff --git a/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs b/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorReporter.cs similarity index 72% rename from src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs rename to src/ModVerify/Reporting/ConcurrentGameDatabaseErrorReporter.cs index 95461ad..f85ebd2 100644 --- a/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorListener.cs +++ b/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorReporter.cs @@ -5,7 +5,7 @@ namespace AET.ModVerify.Reporting; -internal class ConcurrentGameDatabaseErrorListener : DatabaseErrorListener, IDatabaseErrorCollection +internal class ConcurrentGameDatabaseErrorReporter : DatabaseErrorReporter, IDatabaseErrorCollection { private readonly ConcurrentBag _xmlErrors = new(); @@ -14,12 +14,12 @@ internal class ConcurrentGameDatabaseErrorListener : DatabaseErrorListener, IDat public IEnumerable XmlErrors => _xmlErrors.ToList(); public IEnumerable InitializationErrors => _initializationErrors.ToList(); - public override void OnXmlError(XmlError error) + public override void Report(XmlError error) { _xmlErrors.Add(error); } - public override void OnInitializationError(InitializationError error) + public override void Report(InitializationError error) { _initializationErrors.Add(error); } diff --git a/src/ModVerify/VerifyGamePipeline.cs b/src/ModVerify/VerifyGamePipeline.cs index 6eda635..86307a7 100644 --- a/src/ModVerify/VerifyGamePipeline.cs +++ b/src/ModVerify/VerifyGamePipeline.cs @@ -52,7 +52,7 @@ protected sealed override async Task RunCoreAsync(CancellationToken token) { var databaseService = ServiceProvider.GetRequiredService(); - var initializationErrorListener = new ConcurrentGameDatabaseErrorListener(); + var initializationErrorListener = new ConcurrentGameDatabaseErrorReporter(); var initOptions = new GameInitializationOptions { Locations = _gameLocations, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index 0b4c407..84989ea 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -13,8 +13,8 @@ namespace PG.StarWarsGame.Engine.Audio.Sfx; -internal class SfxEventGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorListener, serviceProvider), ISfxEventGameManager +internal class SfxEventGameManager(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorReporter, serviceProvider), ISfxEventGameManager { public IEnumerable InstalledLanguages { get; private set; } = []; @@ -30,9 +30,9 @@ protected override async Task InitializeCoreAsync(CancellationToken token) contentParser.XmlParseError += OnParseError; try { - await Task.Run(() => contentParser.ParseEntriesFromContainerXml( + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\SFXEventFiles.XML", - ErrorListener, + ErrorReporter, GameRepository, "DATA\\XML", NamedEntries, @@ -50,7 +50,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) if (e.IsContainer || e.IsError) { e.Continue = false; - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = GetMessage(e) @@ -69,7 +69,7 @@ private void VerifyFilePathLength(string filePath) { if (filePath.Length > PGConstants.MaxSFXEventDatabaseFileName) { - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"SFXEvent file '{filePath}' is longer than {PGConstants.MaxSFXEventDatabaseFileName} characters." diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index d53808e..c0dea28 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -19,9 +19,9 @@ public interface ICommandBarGameManager : IGameManager internal class CommandBarGameManager( GameRepository repository, - DatabaseErrorListenerWrapper errorListener, + DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorListener, serviceProvider), ICommandBarGameManager + : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager { protected override async Task InitializeCoreAsync(CancellationToken token) { @@ -34,9 +34,9 @@ protected override async Task InitializeCoreAsync(CancellationToken token) try { - await Task.Run(() => contentParser.ParseEntriesFromContainerXml( + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\CommandBarComponentFiles.XML", - ErrorListener, + ErrorReporter, GameRepository, ".\\DATA\\XML", parsedCommandBarComponents, @@ -54,7 +54,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) if (e.IsContainer || e.IsError) { e.Continue = false; - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = GetMessage(e) @@ -73,7 +73,7 @@ private void VerifyFilePathLength(string filePath) { if (filePath.Length > PGConstants.MaxCommandBarDatabaseFileName) { - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"CommandBar file '{filePath}' is longer than {PGConstants.MaxCommandBarDatabaseFileName} characters." diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs deleted file mode 100644 index d123437..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListener.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; - -public abstract class DatabaseErrorListener : IDatabaseErrorListener -{ - public virtual void OnXmlError(XmlError error) - { - } - - public virtual void OnInitializationError(InitializationError error) - { - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs deleted file mode 100644 index b68cfe8..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorListenerWrapper.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using AnakinRaW.CommonUtilities; -using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; - -internal class DatabaseErrorListenerWrapper : DisposableObject, IDatabaseErrorListener, IXmlParserErrorListener -{ - internal event EventHandler? InitializationError; - - private readonly IDatabaseErrorListener? _errorListener; - private IPrimitiveXmlErrorParserProvider? _primitiveXmlParserErrorProvider; - - public DatabaseErrorListenerWrapper(IDatabaseErrorListener? errorListener, IServiceProvider serviceProvider) - { - _errorListener = errorListener; - if (_errorListener is null) - return; - _primitiveXmlParserErrorProvider = serviceProvider.GetRequiredService(); - _primitiveXmlParserErrorProvider.XmlParseError += ((IXmlParserErrorListener)this).OnXmlParseError; - } - - public void OnXmlError(XmlError error) - { - _errorListener?.OnXmlError(error); - } - - public void OnInitializationError(InitializationError error) - { - InitializationError?.Invoke(this, error); - if (_errorListener is null) - return; - _errorListener.OnInitializationError(error); - } - - protected override void DisposeResources() - { - base.DisposeResources(); - if (_primitiveXmlParserErrorProvider is null) - return; - _primitiveXmlParserErrorProvider.XmlParseError -= ((IXmlParserErrorListener)this).OnXmlParseError; - _primitiveXmlParserErrorProvider = null!; - } - - void IXmlParserErrorListener.OnXmlParseError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) - { - if (_errorListener is null) - return; - - OnXmlError(new XmlError - { - FileLocation = error.Location, - Parser = parser.ToString(), - Message = error.Message, - ErrorKind = error.ErrorKind, - Element = error.Element - }); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs new file mode 100644 index 0000000..9027396 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs @@ -0,0 +1,12 @@ +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +public abstract class DatabaseErrorReporter : IDatabaseErrorReporter +{ + public virtual void Report(XmlError error) + { + } + + public virtual void Report(InitializationError error) + { + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs new file mode 100644 index 0000000..fc42e69 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +internal sealed class DatabaseErrorReporterWrapper : XmlErrorReporter, IDatabaseErrorReporter +{ + internal event EventHandler? InitializationError; + + private readonly IDatabaseErrorReporter? _errorListener; + private readonly ILogger? _logger; + + public DatabaseErrorReporterWrapper(IDatabaseErrorReporter? errorListener, IServiceProvider serviceProvider) + { + if (errorListener is null) + return; + _errorListener = errorListener; + _logger = serviceProvider.GetService()?.CreateLogger(GetType()); + } + + public void Report(XmlError error) + { + _errorListener?.Report(error); + } + + public void Report(InitializationError error) + { + InitializationError?.Invoke(this, error); + if (_errorListener is null) + return; + _errorListener.Report(error); + } + + public override void Report(string parser, XmlParseErrorEventArgs error) + { + if (_errorListener is null) + return; + + _logger?.LogWarning($"Xml parser '{parser}' reported error: {error}"); + + 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/Database/ErrorReporting/IDatabaseErrorListener.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs deleted file mode 100644 index 2df54c5..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorListener.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; - -public interface IDatabaseErrorListener -{ - void OnXmlError(XmlError error); - void OnInitializationError(InitializationError error); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs new file mode 100644 index 0000000..c404ac0 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs @@ -0,0 +1,8 @@ +namespace PG.StarWarsGame.Engine.Database.ErrorReporting; + +public interface IDatabaseErrorReporter +{ + void Report(XmlError error); + + void Report(InitializationError error); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs index e2122bf..77eea6f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs @@ -15,7 +15,7 @@ public Task InitializeGameAsync( { var repoFactory = serviceProvider.GetRequiredService(); - using var errorListenerWrapper = new DatabaseErrorListenerWrapper(gameInitializationOptions.ErrorListener, serviceProvider); + using var errorListenerWrapper = new DatabaseErrorReporterWrapper(gameInitializationOptions.ErrorListener); var repository = repoFactory.Create(gameInitializationOptions.TargetEngineType, gameInitializationOptions.Locations, errorListenerWrapper); var gameInitializer = new GameInitializer(repository, gameInitializationOptions.CancelOnError, serviceProvider); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs index 8c11841..5c82c01 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs @@ -10,5 +10,5 @@ public class GameInitializationOptions public bool CancelOnError { get; init; } - public IDatabaseErrorListener? ErrorListener { get; init; } + public IDatabaseErrorReporter? ErrorListener { get; init; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index 5ba85cd..f8e9f30 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -18,10 +18,10 @@ internal class GameInitializer(GameRepository repository, bool cancelOnError, IS private CancellationTokenSource? _cancellationTokenSource; - public async Task InitializeAsync(DatabaseErrorListenerWrapper errorListener, CancellationToken token) + public async Task InitializeAsync(DatabaseErrorReporterWrapper errorReporter, CancellationToken token) { _logger?.LogInformation("Initializing Game Database..."); - errorListener.InitializationError += OnInitializationError; + errorReporter.InitializationError += OnInitializationError; _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); @@ -64,19 +64,19 @@ public async Task InitializeAsync(DatabaseErrorListenerWrapper er // TargetingPrioritySetFiles.xml // MousePointerFiles.xml - var gameConstants = new GameConstants.GameConstants(repository, errorListener, serviceProvider); + var gameConstants = new GameConstants.GameConstants(repository, errorReporter, serviceProvider); await gameConstants.InitializeAsync( _cancellationTokenSource.Token); - var guiDialogs = new GuiDialogGameManager(repository, errorListener, serviceProvider); + var guiDialogs = new GuiDialogGameManager(repository, errorReporter, serviceProvider); await guiDialogs.InitializeAsync(_cancellationTokenSource.Token); - var sfxGameManager = new SfxEventGameManager(repository, errorListener, serviceProvider); + var sfxGameManager = new SfxEventGameManager(repository, errorReporter, serviceProvider); await sfxGameManager.InitializeAsync( _cancellationTokenSource.Token); - var commandBarManager = new CommandBarGameManager(repository, errorListener, serviceProvider); + var commandBarManager = new CommandBarGameManager(repository, errorReporter, serviceProvider); await commandBarManager.InitializeAsync( _cancellationTokenSource.Token); - var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorListener, serviceProvider); + var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorReporter, serviceProvider); await gameObjetTypeManager.InitializeAsync( _cancellationTokenSource.Token); repository.Seal(); @@ -94,7 +94,7 @@ public async Task InitializeAsync(DatabaseErrorListenerWrapper er } finally { - errorListener.InitializationError -= OnInitializationError; + errorReporter.InitializationError -= OnInitializationError; _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; _logger?.LogInformation("Finished initializing game database"); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs index 3e1c18d..bec742c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs @@ -13,8 +13,8 @@ namespace PG.StarWarsGame.Engine.Database; -internal abstract class GameManagerBase(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorListener, serviceProvider), IGameManager +internal abstract class GameManagerBase(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorReporter, serviceProvider), IGameManager { protected readonly ValueListDictionary NamedEntries = new(); @@ -38,17 +38,17 @@ internal abstract class GameManagerBase protected readonly IFileSystem FileSystem; protected readonly ILogger? Logger; - protected readonly DatabaseErrorListenerWrapper ErrorListener; + protected readonly DatabaseErrorReporterWrapper ErrorReporter; public bool IsInitialized => _initialized; - protected GameManagerBase(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) + protected GameManagerBase(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) { GameRepository = repository ?? throw new ArgumentNullException(nameof(repository)); ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); Logger = serviceProvider.GetService()?.CreateLogger(GetType()); FileSystem = serviceProvider.GetRequiredService(); - ErrorListener = errorListener ?? throw new ArgumentNullException(nameof(errorListener)); + ErrorReporter = errorReporter ?? throw new ArgumentNullException(nameof(errorReporter)); } public async Task InitializeAsync(CancellationToken token) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs index 508323c..a170f9a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs @@ -7,8 +7,8 @@ namespace PG.StarWarsGame.Engine.GameConstants; -internal class GameConstants(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorListener, serviceProvider), IGameConstants +internal class GameConstants(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorReporter, serviceProvider), IGameConstants { protected override Task InitializeCoreAsync(CancellationToken token) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 6bb10a6..dd716c4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -10,8 +10,8 @@ namespace PG.StarWarsGame.Engine.GameObjects; -internal class GameObjectTypeGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorListener, serviceProvider), IGameObjectTypeGameManager +internal class GameObjectTypeGameManager(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorReporter, serviceProvider), IGameObjectTypeGameManager { protected override async Task InitializeCoreAsync(CancellationToken token) { @@ -19,9 +19,9 @@ protected override async Task InitializeCoreAsync(CancellationToken token) var contentParser = ServiceProvider.GetRequiredService(); - await Task.Run(() => contentParser.ParseEntriesFromContainerXml( + await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\GAMEOBJECTFILES.XML", - ErrorListener, + ErrorReporter, GameRepository, ".\\DATA\\XML", NamedEntries, @@ -32,7 +32,7 @@ private void VerifyFilePathLength(string filePath) { if (filePath.Length > PGConstants.MaxGameObjectDatabaseFileName) { - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"Game object file '{filePath}' is longer than {PGConstants.MaxGameObjectDatabaseFileName} characters." diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index 5f20f0f..bbf90a1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -14,8 +14,8 @@ namespace PG.StarWarsGame.Engine.GuiDialog; -internal partial class GuiDialogGameManager(GameRepository repository, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorListener, serviceProvider), IGuiDialogManager +internal partial class GuiDialogGameManager(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : GameManagerBase(repository, errorReporter, serviceProvider), IGuiDialogManager { private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index b96c4d7..c9f3e6d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -5,11 +5,10 @@ using System.Threading; using System.Threading.Tasks; using AnakinRaW.CommonUtilities.Collections; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Database.ErrorReporting; using PG.StarWarsGame.Engine.GuiDialog.Xml; -using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Engine.Xml.Parsers.File; using PG.StarWarsGame.Engine.Xml.Tags; namespace PG.StarWarsGame.Engine.GuiDialog; @@ -22,8 +21,7 @@ protected override Task InitializeCoreAsync(CancellationToken token) { return Task.Run(() => { - var parserFactory = ServiceProvider.GetRequiredService(); - var guiDialogParser = parserFactory.CreateFileParser(); + var guiDialogParser = new GuiDialogParser(ServiceProvider, ErrorReporter); _defaultTexturesRo = new ReadOnlyDictionary(_defaultTextures); @@ -32,7 +30,7 @@ protected override Task InitializeCoreAsync(CancellationToken token) if (fileStream is null) { - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = "Unable to find GuiDialogs.xml" @@ -43,7 +41,7 @@ protected override Task InitializeCoreAsync(CancellationToken token) var guiDialogs = guiDialogParser.ParseFile(fileStream); if (guiDialogs is null) { - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = "Unable to parse GuiDialogs.xml" @@ -53,20 +51,20 @@ protected override Task InitializeCoreAsync(CancellationToken token) GuiDialogsXml = guiDialogs; - InitializeTextures(guiDialogs.TextureData, ErrorListener); + InitializeTextures(guiDialogs.TextureData, ErrorReporter); }, token); } - private void InitializeTextures(GuiDialogsXmlTextureData textureData, DatabaseErrorListenerWrapper errorListener) + private void InitializeTextures(GuiDialogsXmlTextureData textureData, DatabaseErrorReporterWrapper errorReporter) { - InitializeMegaTextures(textureData, ErrorListener); + InitializeMegaTextures(textureData, ErrorReporter); var textures = textureData.Textures; if (textures.Count == 0) { - errorListener.OnInitializationError(new InitializationError + errorReporter.Report(new InitializationError { GameManager = ToString(), Message = "No Textures defined in GuiDialogs.xml" @@ -124,11 +122,11 @@ private Dictionary InitializeComponentT return result; } - private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, DatabaseErrorListenerWrapper errorListener) + private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, DatabaseErrorReporterWrapper errorReporter) { if (guiDialogs.MegaTexture is null) { - errorListener.OnInitializationError(new InitializationError + errorReporter.Report(new InitializationError { GameManager = ToString(), Message = "MtdFile is not defined in GuiDialogs.xml" @@ -140,7 +138,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, Databas if (mtdPath.Length > MegaTextureMaxFilePathLength) { - errorListener.OnInitializationError(new InitializationError + errorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"Mtd file path is longer than {MegaTextureMaxFilePathLength}." @@ -153,7 +151,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, Databas if (guiDialogs.CompressedMegaTexture is null) { - errorListener.OnInitializationError(new InitializationError + errorReporter.Report(new InitializationError { GameManager = ToString(), Message = "CompressedMegaTexture is not defined in GuiDialogs.xml" @@ -168,7 +166,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, Databas if (textureFileNameWithExtension.Length > MegaTextureMaxFilePathLength) { - errorListener.OnInitializationError(new InitializationError + errorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"MegaTexture file path is longer than {MegaTextureMaxFilePathLength}." @@ -181,7 +179,7 @@ private void ReportInvalidComponent(in FrugalList invalidKeys) if (invalidKeys.Count == 0) return; - ErrorListener.OnInitializationError(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"The following XML keys are not valid to describe a GUI component: {string.Join(",", invalidKeys)}" diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs index 6e16e59..a02e6e1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs @@ -12,8 +12,8 @@ internal class FocGameRepository : GameRepository { public override GameEngineType EngineType => GameEngineType.Foc; - public FocGameRepository(GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) - : base(gameLocations, errorListener, serviceProvider) + public FocGameRepository(GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : base(gameLocations, errorReporter, serviceProvider) { if (gameLocations == null) throw new ArgumentNullException(nameof(gameLocations)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index 5b9051a..6d24048 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -16,7 +16,7 @@ using PG.StarWarsGame.Files.MEG.Files; using PG.StarWarsGame.Files.MEG.Services; using PG.StarWarsGame.Files.MEG.Services.Builder.Normalization; -using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.IO.Repositories; @@ -28,7 +28,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository private readonly ICrc32HashingService _crc32HashingService; private readonly IVirtualMegArchiveBuilder _virtualMegBuilder; private readonly IGameLanguageManagerProvider _languageManagerProvider; - private readonly DatabaseErrorListenerWrapper _errorListener; + private readonly DatabaseErrorReporterWrapper _errorReporter; protected readonly string GameDirectory; @@ -47,7 +47,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository private readonly List _loadedMegFiles = new(); protected IVirtualMegArchive? MasterMegArchive { get; private set; } - protected GameRepository(GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener, IServiceProvider serviceProvider) : base(serviceProvider) + protected GameRepository(GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(serviceProvider) { if (gameLocations == null) throw new ArgumentNullException(nameof(gameLocations)); @@ -58,7 +58,7 @@ protected GameRepository(GameLocations gameLocations, DatabaseErrorListenerWrapp _crc32HashingService = serviceProvider.GetRequiredService(); _megPathNormalizer = EmpireAtWarMegDataEntryPathNormalizer.Instance; _languageManagerProvider = serviceProvider.GetRequiredService(); - _errorListener = errorListener; + _errorReporter = errorReporter; foreach (var mod in gameLocations.ModPaths) { @@ -210,8 +210,6 @@ protected IList LoadMegArchivesFromXml(string lookupPath) { var megFilesXmlPath = FileSystem.Path.Combine(lookupPath, "Data\\MegaFiles.xml"); - var fileParserFactory = Services.GetRequiredService(); - using var xmlStream = TryOpenFile(megFilesXmlPath); if (xmlStream is null) @@ -220,7 +218,7 @@ protected IList LoadMegArchivesFromXml(string lookupPath) return Array.Empty(); } - var parser = fileParserFactory.CreateFileParser(_errorListener); + var parser = new XmlFileListParser(Services ,_errorReporter); var megaFilesXml = parser.ParseFile(xmlStream); if (megaFilesXml is null) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs index fa7c760..802c51b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs @@ -5,10 +5,10 @@ namespace PG.StarWarsGame.Engine.IO.Repositories; internal sealed class GameRepositoryFactory(IServiceProvider serviceProvider) : IGameRepositoryFactory { - public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener) + public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter) { if (engineType == GameEngineType.Eaw) throw new NotImplementedException("Empire at War is currently not supported."); - return new FocGameRepository(gameLocations, errorListener, serviceProvider); + return new FocGameRepository(gameLocations, errorReporter, serviceProvider); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs index fed8536..ae54c37 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs @@ -4,5 +4,5 @@ namespace PG.StarWarsGame.Engine.IO.Repositories; internal interface IGameRepositoryFactory { - GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorListenerWrapper errorListener); + GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/IPetroglyphXmlFileParserFactory.cs index d5106c2..4532365 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 { - IPetroglyphXmlFileParser CreateFileParser(IXmlParserErrorListener? listener = null); + IPetroglyphXmlFileContainerParser CreateFileParser(IXmlParserErrorReporter? errorReporter) where T : notnull; } \ 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/Data/CommandBarComponentParser.cs index ca35b6e..bc824da 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -7,14 +7,15 @@ using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers.Primitives; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; public sealed class CommandBarComponentParser( IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, - IXmlParserErrorListener? listener = null) - : XmlObjectParser(parsedElements, serviceProvider, listener) + IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { public override CommandBarComponentData Parse(XElement element, out Crc32 nameCrc) { @@ -30,307 +31,307 @@ protected override bool ParseTag(XElement tag, CommandBarComponentData component switch (tag.Name.LocalName) { case CommandBarComponentTags.SelectedTextureName: - componentData.SelectedTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.SelectedTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.BlankTextureName: - componentData.BlankTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.BlankTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.IconAlternateTextureName: - componentData.IconAlternateTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.IconAlternateTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.MouseOverTextureName: - componentData.MouseOverTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.MouseOverTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.BarTextureName: - componentData.BarTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.BarTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.BarOverlayName: - componentData.BarOverlayNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.BarOverlayNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.AlternateFontName: - componentData.AlternateFontNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.AlternateFontNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.TooltipText: - componentData.TooltipTexts = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.TooltipTexts = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.LowerEffectTextureName: - componentData.LowerEffectTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.LowerEffectTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.UpperEffectTextureName: - componentData.UpperEffectTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.UpperEffectTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.OverlayTextureName: - componentData.OverlayTextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.OverlayTextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.Overlay2TextureName: - componentData.Overlay2TextureNames = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + componentData.Overlay2TextureNames = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case CommandBarComponentTags.IconTextureName: - componentData.IconTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.IconTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DisabledTextureName: - componentData.DisabledTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.DisabledTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.FlashTextureName: - componentData.FlashTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.FlashTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BuildTextureName: - componentData.BuildTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.BuildTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ModelName: - componentData.ModelName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.ModelName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BoneName: - componentData.BoneName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.BoneName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.CursorTextureName: - componentData.CursorTextureName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.CursorTextureName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.FontName: - componentData.FontName = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.FontName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ClickSfx: - componentData.ClickSfx = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.ClickSfx = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.MouseOverSfx: - componentData.MouseOverSfx = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.MouseOverSfx = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.RightClickSfx: - componentData.RightClickSfx = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.RightClickSfx = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Type: - componentData.Type = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.Type = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Group: - componentData.Group = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.Group = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.AssociatedText: - componentData.AssociatedText = PrimitiveParserProvider.StringParser.Parse(tag); + componentData.AssociatedText = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DragAndDrop: - componentData.DragAndDrop = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.DragAndDrop = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DragSelect: - componentData.DragSelect = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.DragSelect = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Receptor: - componentData.Receptor = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Receptor = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Toggle: - componentData.Toggle = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Toggle = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Tab: - componentData.Tab = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Tab = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Hidden: - componentData.Hidden = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Hidden = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ClearColor: - componentData.ClearColor = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ClearColor = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Disabled: - componentData.Disabled = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Disabled = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.SwapTexture: - componentData.SwapTexture = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.SwapTexture = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DrawAdditive: - componentData.DrawAdditive = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.DrawAdditive = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Editable: - componentData.Editable = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Editable = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.TextOutline: - componentData.TextOutline = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.TextOutline = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Stackable: - componentData.Stackable = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Stackable = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ModelOffsetX: - componentData.ModelOffsetX = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ModelOffsetX = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ModelOffsetY: - componentData.ModelOffsetY = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ModelOffsetY = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ScaleModelX: - componentData.ScaleModelX = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ScaleModelX = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ScaleModelY: - componentData.ScaleModelY = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ScaleModelY = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Collideable: - componentData.Collideable = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.Collideable = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.TextEmboss: - componentData.TextEmboss = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.TextEmboss = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ShouldGhost: - componentData.ShouldGhost = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ShouldGhost = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.GhostBaseOnly: - componentData.GhostBaseOnly = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.GhostBaseOnly = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.CrossFade: - componentData.CrossFade = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.CrossFade = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.LeftJustified: - componentData.LeftJustified = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.LeftJustified = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.RightJustified: - componentData.RightJustified = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.RightJustified = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.NoShell: - componentData.NoShell = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.NoShell = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.SnapDrag: - componentData.SnapDrag = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.SnapDrag = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.SnapLocation: - componentData.SnapLocation = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.SnapLocation = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.OffsetRender: - componentData.OffsetRender = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.OffsetRender = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BlinkFade: - componentData.BlinkFade = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.BlinkFade = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.NoHiddenCollision: - componentData.NoHiddenCollision = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.NoHiddenCollision = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ManualOffset: - componentData.ManualOffset = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ManualOffset = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.SelectedAlpha: - componentData.SelectedAlpha = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.SelectedAlpha = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.PixelAlign: - componentData.PixelAlign = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.PixelAlign = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.CanDragStack: - componentData.CanDragStack = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.CanDragStack = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.CanAnimate: - componentData.CanAnimate = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.CanAnimate = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.LoopAnim: - componentData.LoopAnim = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.LoopAnim = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.SmoothBar: - componentData.SmoothBar = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.SmoothBar = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.OutlinedBar: - componentData.OutlinedBar = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.OutlinedBar = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DragBack: - componentData.DragBack = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.DragBack = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.LowerEffectAdditive: - componentData.LowerEffectAdditive = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.LowerEffectAdditive = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.UpperEffectAdditive: - componentData.UpperEffectAdditive = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.UpperEffectAdditive = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ClickShift: - componentData.ClickShift = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ClickShift = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.TutorialScene: - componentData.TutorialScene = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.TutorialScene = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DialogScene: - componentData.DialogScene = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.DialogScene = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ShouldRenderAtDragPos: - componentData.ShouldRenderAtDragPos = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.ShouldRenderAtDragPos = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DisableDarken: - componentData.DisableDarken = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.DisableDarken = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.AnimateBack: - componentData.AnimateBack = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.AnimateBack = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.AnimateUpperEffect: - componentData.AnimateUpperEffect = PrimitiveParserProvider.BooleanParser.Parse(tag); + componentData.AnimateUpperEffect = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Size: - componentData.Size = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.Size = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.TextOffset: - componentData.TextOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.TextOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.TextOffset2: - componentData.TextOffset2 = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.TextOffset2 = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Offset: - componentData.Offset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.Offset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DefaultOffset: - componentData.DefaultOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.DefaultOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DefaultOffsetWidescreen: - componentData.DefaultOffsetWidescreen = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.DefaultOffsetWidescreen = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.IconOffset: - componentData.IconOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.IconOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.MouseOverOffset: - componentData.MouseOverOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.MouseOverOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.DisabledOffset: - componentData.DisabledOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.DisabledOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BuildDialOffset: - componentData.BuildDialOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.BuildDialOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BuildDial2Offset: - componentData.BuildDial2Offset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.BuildDial2Offset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.LowerEffectOffset: - componentData.LowerEffectOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.LowerEffectOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.UpperEffectOffset: - componentData.UpperEffectOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.UpperEffectOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.OverlayOffset: - componentData.OverlayOffset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.OverlayOffset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Overlay2Offset: - componentData.Overlay2Offset = PrimitiveParserProvider.Vector2FParser.Parse(tag); + componentData.Overlay2Offset = PetroglyphXmlVector2FParser.Instance.Parse(tag); return true; case CommandBarComponentTags.MaxTextLength: - componentData.MaxTextLength = PrimitiveParserProvider.UIntParser.Parse(tag); + componentData.MaxTextLength = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case CommandBarComponentTags.FontPointSize: - componentData.FontPointSize = (int)PrimitiveParserProvider.UIntParser.Parse(tag); + componentData.FontPointSize = (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case CommandBarComponentTags.Scale: - componentData.Scale = PrimitiveParserProvider.FloatParser.Parse(tag); + componentData.Scale = PetroglyphXmlFloatParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BlinkRate: - componentData.BlinkRate = PrimitiveParserProvider.FloatParser.Parse(tag); + componentData.BlinkRate = PetroglyphXmlFloatParser.Instance.Parse(tag); return true; case CommandBarComponentTags.MaxTextWidth: - componentData.MaxTextWidth = PrimitiveParserProvider.FloatParser.Parse(tag); + componentData.MaxTextWidth = PetroglyphXmlFloatParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BlinkDuration: - componentData.BlinkDuration = PrimitiveParserProvider.FloatParser.Parse(tag); + componentData.BlinkDuration = PetroglyphXmlFloatParser.Instance.Parse(tag); return true; case CommandBarComponentTags.ScaleDuration: - componentData.ScaleDuration = PrimitiveParserProvider.FloatParser.Parse(tag); + componentData.ScaleDuration = PetroglyphXmlFloatParser.Instance.Parse(tag); return true; case CommandBarComponentTags.AnimFps: - componentData.AnimFps = PrimitiveParserProvider.FloatParser.Parse(tag); + componentData.AnimFps = PetroglyphXmlFloatParser.Instance.Parse(tag); return true; case CommandBarComponentTags.BaseLayer: - componentData.BaseLayer = (int)PrimitiveParserProvider.UIntParser.Parse(tag); + componentData.BaseLayer = (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case CommandBarComponentTags.MaxBarLevel: - componentData.MaxBarLevel = (int)PrimitiveParserProvider.UIntParser.Parse(tag); + componentData.MaxBarLevel = (int)PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs index 7ec0cbe..64de5ec 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameConstantsParser.cs @@ -1,23 +1,16 @@ using System; using System.Xml.Linq; -using PG.Commons.Collections; -using PG.Commons.Hashing; using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; -internal class GameConstantsParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) : - PetroglyphXmlFileParser(serviceProvider, listener) +internal class GameConstantsParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : + PetroglyphXmlFileParser(serviceProvider, errorReporter) { protected override GameConstantsXml Parse(XElement element, string fileName) { return new GameConstantsXml(); } - - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) - { - 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 ee97771..e555af7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs @@ -5,6 +5,7 @@ using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers.Primitives; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -27,8 +28,8 @@ public static class GameObjectXmlTags public sealed class GameObjectParser( IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, - IXmlParserErrorListener? listener = null) - : XmlObjectParser(parsedElements, serviceProvider, listener) + IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { public override GameObject Parse(XElement element, out Crc32 nameCrc) { @@ -47,7 +48,7 @@ protected override bool ParseTag(XElement tag, GameObject xmlObject) switch (tag.Name.LocalName) { case GameObjectXmlTags.LandTerrainModelMapping: - var mappingValue = PrimitiveParserProvider.CommaSeparatedStringKeyValueListParser.Parse(tag); + var mappingValue = CommaSeparatedStringKeyValueListParser.Instance.Parse(tag); var dict = xmlObject.InternalLandTerrainModelMapping; foreach (var keyValuePair in mappingValue) { @@ -56,37 +57,37 @@ protected override bool ParseTag(XElement tag, GameObject xmlObject) } return true; case GameObjectXmlTags.GalacticModelName: - xmlObject.GalacticModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.GalacticModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.DestroyedGalacticModelName: - xmlObject.DestroyedGalacticModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.DestroyedGalacticModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.LandModelName: - xmlObject.LandModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.LandModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.SpaceModelName: - xmlObject.SpaceModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.SpaceModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.ModelName: - xmlObject.ModelName = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.ModelName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.TacticalModelName: - xmlObject.TacticalModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.TacticalModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.GalacticFleetOverrideModelName: - xmlObject.GalacticFleetOverrideModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.GalacticFleetOverrideModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.GuiModelName: - xmlObject.GuiModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.GuiModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.LandModelAnimOverrideName: - xmlObject.LandAnimOverrideModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.LandAnimOverrideModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.XxxSpaceModelName: - xmlObject.XxxSpaceModeModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.XxxSpaceModeModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case GameObjectXmlTags.DamagedSmokeAssetName: - xmlObject.DamagedSmokeAssetModel = PrimitiveParserProvider.StringParser.Parse(tag); + xmlObject.DamagedSmokeAssetModel = PetroglyphXmlStringParser.Instance.Parse(tag); return true; default: return true; // TODO: Once parsing is complete, switch to false. } 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 6df3ecb..d5966c8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs @@ -7,14 +7,15 @@ using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers.Primitives; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; public sealed class SfxEventParser( IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, - IXmlParserErrorListener? listener = null) - : XmlObjectParser(parsedElements, serviceProvider, listener) + IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { public override SfxEvent Parse(XElement element, out Crc32 nameCrc) { @@ -76,14 +77,14 @@ protected override bool ParseTag(XElement tag, SfxEvent sfxEvent) switch (tag.Name.LocalName) { case SfxEventXmlTags.OverlapTest: - sfxEvent.OverlapTestName = PrimitiveParserProvider.StringParser.Parse(tag); + sfxEvent.OverlapTestName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case SfxEventXmlTags.ChainedSfxEvent: - sfxEvent.ChainedSfxEventName = PrimitiveParserProvider.StringParser.Parse(tag); + sfxEvent.ChainedSfxEventName = PetroglyphXmlStringParser.Instance.Parse(tag); return true; case SfxEventXmlTags.UsePreset: { - var presetName = PrimitiveParserProvider.StringParser.Parse(tag); + var presetName = PetroglyphXmlStringParser.Instance.Parse(tag); var presetNameCrc = HashingService.GetCrc32Upper(presetName.AsSpan(), PGConstants.DefaultPGEncoding); if (presetNameCrc != default && ParsedElements.TryGetFirstValue(presetNameCrc, out var preset)) sfxEvent.ApplyPreset(preset); @@ -96,104 +97,104 @@ protected override bool ParseTag(XElement tag, SfxEvent sfxEvent) } case SfxEventXmlTags.IsPreset: - sfxEvent.IsPreset = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.IsPreset = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.Is3D: - sfxEvent.Is3D = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.Is3D = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.Is2D: - sfxEvent.Is2D = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.Is2D = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.IsGui: - sfxEvent.IsGui = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.IsGui = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.IsHudVo: - sfxEvent.IsHudVo = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.IsHudVo = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.IsUnitResponseVo: - sfxEvent.IsUnitResponseVo = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.IsUnitResponseVo = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.IsAmbientVo: - sfxEvent.IsAmbientVo = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.IsAmbientVo = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.Localize: - sfxEvent.IsLocalized = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.IsLocalized = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.PlaySequentially: - sfxEvent.PlaySequentially = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.PlaySequentially = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.KillsPreviousObjectSFX: - sfxEvent.KillsPreviousObjectsSfx = PrimitiveParserProvider.BooleanParser.Parse(tag); + sfxEvent.KillsPreviousObjectsSfx = PetroglyphXmlBooleanParser.Instance.Parse(tag); return true; case SfxEventXmlTags.Samples: - sfxEvent.Samples = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + sfxEvent.Samples = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case SfxEventXmlTags.PreSamples: - sfxEvent.PreSamples = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + sfxEvent.PreSamples = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case SfxEventXmlTags.PostSamples: - sfxEvent.PostSamples = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + sfxEvent.PostSamples = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case SfxEventXmlTags.TextID: - sfxEvent.LocalizedTextIDs = new ReadOnlyCollection(PrimitiveParserProvider.LooseStringListParser.Parse(tag)); + sfxEvent.LocalizedTextIDs = new ReadOnlyCollection(PetroglyphXmlLooseStringListParser.Instance.Parse(tag)); return true; case SfxEventXmlTags.Priority: - sfxEvent.Priority = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinPriorityValue, SfxEvent.MaxPriorityValue); + sfxEvent.Priority = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinPriorityValue, SfxEvent.MaxPriorityValue); return true; case SfxEventXmlTags.MinPitch: - sfxEvent.MinPitch = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); + sfxEvent.MinPitch = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); return true; case SfxEventXmlTags.MaxPitch: - sfxEvent.MaxPitch = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); + sfxEvent.MaxPitch = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinPitchValue, SfxEvent.MaxPitchValue); return true; case SfxEventXmlTags.MinPan2D: - sfxEvent.MinPan2D = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); + sfxEvent.MinPan2D = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); return true; case SfxEventXmlTags.MaxPan2D: - sfxEvent.MaxPan2D = (byte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); + sfxEvent.MaxPan2D = (byte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxPan2dValue); return true; case SfxEventXmlTags.PlayCount: - sfxEvent.PlayCount = (sbyte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.InfinitivePlayCount, sbyte.MaxValue); + sfxEvent.PlayCount = (sbyte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.InfinitivePlayCount, sbyte.MaxValue); return true; case SfxEventXmlTags.MaxInstances: - sfxEvent.MaxInstances = (sbyte)PrimitiveParserProvider.IntParser.ParseWithRange(tag, SfxEvent.MinMaxInstances, sbyte.MaxValue); + sfxEvent.MaxInstances = (sbyte)PetroglyphXmlIntegerParser.Instance.ParseWithRange(tag, SfxEvent.MinMaxInstances, sbyte.MaxValue); return true; case SfxEventXmlTags.Probability: - sfxEvent.Probability = PrimitiveParserProvider.Max100ByteParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxProbability); + sfxEvent.Probability = PetroglyphXmlMax100ByteParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxProbability); return true; case SfxEventXmlTags.MinVolume: - sfxEvent.MinVolume = PrimitiveParserProvider.Max100ByteParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); + sfxEvent.MinVolume = PetroglyphXmlMax100ByteParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); return true; case SfxEventXmlTags.MaxVolume: - sfxEvent.MaxVolume = PrimitiveParserProvider.Max100ByteParser.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); + sfxEvent.MaxVolume = PetroglyphXmlMax100ByteParser.Instance.ParseWithRange(tag, byte.MinValue, SfxEvent.MaxVolumeValue); return true; case SfxEventXmlTags.MinPredelay: - sfxEvent.MinPredelay = PrimitiveParserProvider.UIntParser.Parse(tag); + sfxEvent.MinPredelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case SfxEventXmlTags.MaxPredelay: - sfxEvent.MaxPredelay = PrimitiveParserProvider.UIntParser.Parse(tag); + sfxEvent.MaxPredelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case SfxEventXmlTags.MinPostdelay: - sfxEvent.MinPostdelay = PrimitiveParserProvider.UIntParser.Parse(tag); + sfxEvent.MinPostdelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case SfxEventXmlTags.MaxPostdelay: - sfxEvent.MaxPostdelay = PrimitiveParserProvider.UIntParser.Parse(tag); + sfxEvent.MaxPostdelay = PetroglyphXmlUnsignedIntegerParser.Instance.Parse(tag); return true; case SfxEventXmlTags.LoopFadeInSeconds: - sfxEvent.LoopFadeInSeconds = PrimitiveParserProvider.FloatParser.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); + sfxEvent.LoopFadeInSeconds = PetroglyphXmlFloatParser.Instance.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); return true; case SfxEventXmlTags.LoopFadeOutSeconds: - sfxEvent.LoopFadeOutSeconds = PrimitiveParserProvider.FloatParser.ParseAtLeast(tag, SfxEvent.MinLoopSeconds); + 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 = PrimitiveParserProvider.FloatParser.ParseAtLeast(tag, SfxEvent.MinVolumeSaturation); + sfxEvent.VolumeSaturationDistance = PetroglyphXmlFloatParser.Instance.ParseAtLeast(tag, SfxEvent.MinVolumeSaturation); return true; default: return false; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs index 9c82a16..53cdb88 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs @@ -9,14 +9,12 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.File; -internal class CommandBarComponentFileParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) - : PetroglyphXmlFileParser(serviceProvider, listener) +internal class CommandBarComponentFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) { - private readonly IXmlParserErrorListener? _listener = listener; - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { - var parser = new CommandBarComponentParser(parsedElements, ServiceProvider, _listener); + var parser = new CommandBarComponentParser(parsedElements, ServiceProvider, ErrorReporter); if (!element.HasElements) { @@ -30,9 +28,4 @@ protected override void Parse(XElement element, IValueListDictionary(serviceProvider, listener) +internal class GameObjectFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) { - private readonly IXmlParserErrorListener? _listener = listener; - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { - var parser = new GameObjectParser(parsedElements, ServiceProvider, _listener); + var parser = new GameObjectParser(parsedElements, ServiceProvider, ErrorReporter); foreach (var xElement in element.Elements()) { @@ -24,9 +22,4 @@ protected override void Parse(XElement element, IValueListDictionary(serviceProvider, listener) +internal class GuiDialogParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : + PetroglyphXmlFileParser(serviceProvider, errorReporter) { protected override GuiDialogsXml Parse(XElement element, string fileName) { @@ -53,13 +53,8 @@ private XmlComponentTextureData ParseTexture(XElement texture) var textures = new ValueListDictionary(); foreach (var entry in texture.Elements()) - textures.Add(entry.Name.ToString(), PrimitiveParserProvider.StringParser.Parse(entry)); + textures.Add(entry.Name.ToString(), PetroglyphXmlStringParser.Instance.Parse(entry)); return new XmlComponentTextureData(componentId, textures, XmlLocationInfo.FromElement(texture)); } - - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) - { - throw new NotSupportedException(); - } } \ 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 index 1de28b8..f40dd1a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/SfxEventFileParser.cs @@ -1,6 +1,5 @@ using System; using System.Xml.Linq; -using Microsoft.Extensions.Logging; using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Audio.Sfx; @@ -10,14 +9,12 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers.File; -internal class SfxEventFileParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) - : PetroglyphXmlFileParser(serviceProvider, listener) +internal class SfxEventFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlFileContainerParser(serviceProvider, errorReporter) { - private readonly IXmlParserErrorListener? _listener = listener; - - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) + protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) { - var parser = new SfxEventParser(parsedElements, ServiceProvider, _listener); + var parser = new SfxEventParser(parsedElements, ServiceProvider, ErrorReporter); if (!element.HasElements) { @@ -32,15 +29,4 @@ protected override void Parse(XElement element, IValueListDictionary? XmlParseError; - void ParseEntriesFromContainerXml( + void ParseEntriesFromFileListXml( string xmlFile, - IXmlParserErrorListener listener, + IXmlParserErrorReporter reporter, IGameRepository gameRepository, string lookupPath, ValueListDictionary entries, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs index d84dd96..2d8cd2a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -10,6 +10,7 @@ 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; @@ -19,21 +20,21 @@ internal sealed class XmlContainerContentParser(IServiceProvider serviceProvider private readonly IPetroglyphXmlFileParserFactory _fileParserFactory = serviceProvider.GetRequiredService(); - public void ParseEntriesFromContainerXml( + public void ParseEntriesFromFileListXml( string xmlFile, - IXmlParserErrorListener listener, + IXmlParserErrorReporter reporter, IGameRepository gameRepository, string lookupPath, ValueListDictionary entries, Action? onFileParseAction = null) { - var containerParser = _fileParserFactory.CreateFileParser(listener); + var containerParser = new XmlFileListParser(Services, reporter); Logger.LogDebug($"Parsing container data '{xmlFile}'"); using var containerStream = gameRepository.TryOpenFile(xmlFile); if (containerStream == null) { - listener.OnXmlParseError(containerParser, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + reporter.Report(containerParser.ToString(), XmlParseErrorEventArgs.FromMissingFile(xmlFile)); Logger.LogWarning($"Could not find XML file '{xmlFile}'"); var args = new XmlContainerParserErrorEventArgs(xmlFile, true) @@ -45,10 +46,11 @@ public void ParseEntriesFromContainerXml( return; } - XmlFileContainer? container; + XmlFileListContainer? container; + try { - container = containerParser.ParseFile(containerStream); + container = containerParser.ParseFile(containerStream); if (container is null) throw new XmlException($"Unable to parse XML container file '{xmlFile}'."); } @@ -67,7 +69,7 @@ public void ParseEntriesFromContainerXml( var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); - var parser = _fileParserFactory.CreateFileParser(listener); + var parser = _fileParserFactory.CreateFileParser(reporter); foreach (var file in xmlFiles) { @@ -78,7 +80,7 @@ public void ParseEntriesFromContainerXml( if (fileStream is null) { - listener.OnXmlParseError(parser, XmlParseErrorEventArgs.FromMissingFile(file)); + reporter.Report(parser.ToString(), XmlParseErrorEventArgs.FromMissingFile(file)); Logger.LogWarning($"Could not find XML file '{file}'"); var args = new XmlContainerParserErrorEventArgs(file); @@ -97,7 +99,7 @@ public void ParseEntriesFromContainerXml( } catch (XmlException e) { - listener.OnXmlParseError(parser, new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); + reporter.Report(parser.ToString(), new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); var args = new XmlContainerParserErrorEventArgs(e, file); XmlParseError?.Invoke(this, args); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs index ce2a89f..6bd5dbb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs @@ -11,8 +11,8 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; public abstract class XmlObjectParser( IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, - IXmlParserErrorListener? listener = null) - : XmlObjectParser(parsedElements, serviceProvider, listener) where TObject : XmlObject + IXmlParserErrorReporter? errorReporter = null) + : XmlObjectParser(parsedElements, serviceProvider, errorReporter) where TObject : XmlObject { protected void Parse(TObject xmlObject, XElement element) { @@ -36,8 +36,8 @@ public readonly struct EmptyParseState public abstract class XmlObjectParser( IReadOnlyValueListDictionary parsedElements, IServiceProvider serviceProvider, - IXmlParserErrorListener? listener = null) - : PetroglyphXmlElementParser(serviceProvider, listener) where TObject : XmlObject + IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlElementParser(errorReporter) where TObject : XmlObject { protected IReadOnlyValueListDictionary ParsedElements { get; } = parsedElements ?? throw new ArgumentNullException(nameof(parsedElements)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs index 0162c20..550f459 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/PetroglyphXmlParserFactory.cs @@ -1,45 +1,27 @@ using System; using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.CommandBar.Xml; -using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.GameObjects; -using PG.StarWarsGame.Engine.GuiDialog.Xml; -using PG.StarWarsGame.Engine.Xml.Parsers.Data; using PG.StarWarsGame.Engine.Xml.Parsers.File; -using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; namespace PG.StarWarsGame.Engine.Xml; internal sealed class PetroglyphXmlFileParserFactory(IServiceProvider serviceProvider) : IPetroglyphXmlFileParserFactory { - public IPetroglyphXmlFileParser CreateFileParser(IXmlParserErrorListener? listener = null) + public IPetroglyphXmlFileContainerParser CreateFileParser(IXmlParserErrorReporter? errorReporter) where T : notnull { - return (IPetroglyphXmlFileParser)GetFileParser(typeof(T), listener); - } - - private IPetroglyphXmlFileParser GetFileParser(Type type, IXmlParserErrorListener? listener) - { - if (type == typeof(XmlFileContainer)) - return new XmlFileContainerParser(serviceProvider, listener); - - if (type == typeof(GameConstantsXml)) - return new GameConstantsParser(serviceProvider, listener); - - if (type == typeof(GuiDialogsXml)) - return new GuiDialogParser(serviceProvider, listener); + if (typeof(T) == typeof(SfxEvent)) + return (IPetroglyphXmlFileContainerParser) new SfxEventFileParser(serviceProvider, errorReporter); - if (type == typeof(GameObject)) - return new GameObjectFileFileParser(serviceProvider, listener); + if (typeof(T) == typeof(CommandBarComponentData)) + return (IPetroglyphXmlFileContainerParser)new CommandBarComponentFileParser(serviceProvider, errorReporter); - if (type == typeof(SfxEvent)) - return new SfxEventFileParser(serviceProvider, listener); + if (typeof(T) == typeof(GameObject)) + return (IPetroglyphXmlFileContainerParser)new GameObjectFileParser(serviceProvider, errorReporter); - if (type == typeof(CommandBarComponentData)) - return new CommandBarComponentFileParser(serviceProvider, listener); - throw new ParserNotFoundException(type); + throw new NotImplementedException($"Unable to get parser for type {typeof(T)}"); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileContainer.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs similarity index 70% rename from src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileContainer.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs index fad88d4..31a16cc 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileContainer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Data/XmlFileListContainer.cs @@ -2,7 +2,7 @@ namespace PG.StarWarsGame.Files.XML.Data; -public class XmlFileContainer(IList files) +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/IPrimitiveXmlErrorParserProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlErrorParserProvider.cs deleted file mode 100644 index 3ed446e..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlErrorParserProvider.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -public interface IPrimitiveXmlErrorParserProvider : IXmlParserErrorProvider; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlParserErrorListener.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlParserErrorListener.cs deleted file mode 100644 index 5157546..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IPrimitiveXmlParserErrorListener.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -internal interface IPrimitiveXmlParserErrorListener : IXmlParserErrorListener, IPrimitiveXmlErrorParserProvider; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorListener.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorListener.cs deleted file mode 100644 index 59b395d..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorListener.cs +++ /dev/null @@ -1,8 +0,0 @@ -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -public interface IXmlParserErrorListener -{ - public void OnXmlParseError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error); -} \ 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 new file mode 100644 index 0000000..86b3356 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs @@ -0,0 +1,6 @@ +namespace PG.StarWarsGame.Files.XML.ErrorHandling; + +public interface IXmlParserErrorReporter +{ + void Report(string 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 new file mode 100644 index 0000000..7b254be --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs @@ -0,0 +1,21 @@ +using System; + +namespace PG.StarWarsGame.Files.XML.ErrorHandling; + +internal sealed class PrimitiveXmlErrorReporter : IXmlParserErrorReporter, IXmlParserErrorProvider +{ + public event XmlErrorEventHandler? XmlParseError; + + private static readonly Lazy LazyInstance = new(() => new PrimitiveXmlErrorReporter()); + + public static PrimitiveXmlErrorReporter Instance => LazyInstance.Value; + + private PrimitiveXmlErrorReporter() + { + } + + public void Report(string parser, XmlParseErrorEventArgs error) + { + XmlParseError?.Invoke(parser, error); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlParserErrorBroker.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlParserErrorBroker.cs deleted file mode 100644 index c66ba6b..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlParserErrorBroker.cs +++ /dev/null @@ -1,13 +0,0 @@ -using PG.StarWarsGame.Files.XML.Parsers; - -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -internal class PrimitiveXmlParserErrorBroker : IPrimitiveXmlParserErrorListener -{ - public event XmlErrorEventHandler? XmlParseError; - - public void OnXmlParseError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) - { - XmlParseError?.Invoke(parser, error); - } -} \ 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 index 69962e3..7001a03 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs @@ -1,5 +1,3 @@ -using PG.StarWarsGame.Files.XML.Parsers; +namespace PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Files.XML.ErrorHandling; - -public delegate void XmlErrorEventHandler(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error); \ No newline at end of file +public delegate void XmlErrorEventHandler(string 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 new file mode 100644 index 0000000..5e5f9a5 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs @@ -0,0 +1,28 @@ +using AnakinRaW.CommonUtilities; + +namespace PG.StarWarsGame.Files.XML.ErrorHandling; + +public class XmlErrorReporter : DisposableObject, IXmlParserErrorReporter, IXmlParserErrorProvider +{ + public event XmlErrorEventHandler? XmlParseError; + + public XmlErrorReporter() + { + PrimitiveXmlErrorReporter.Instance.XmlParseError += OnPrimitiveError; + } + + public virtual void Report(string parser, XmlParseErrorEventArgs error) + { + XmlParseError?.Invoke(parser, error); + } + + protected override void DisposeResources() + { + PrimitiveXmlErrorReporter.Instance.XmlParseError -= OnPrimitiveError; + } + + private void OnPrimitiveError(string parser, XmlParseErrorEventArgs error) + { + Report(parser, error); + } +} \ 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 9f9f1cb..740bd97 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 @@ -23,9 +23,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj.DotSettings b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj.DotSettings index 2dedfd6..62d0b15 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj.DotSettings +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/PG.StarWarsGame.Files.XML.csproj.DotSettings @@ -1,6 +1,8 @@  False True + True True True + True True \ 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 new file mode 100644 index 0000000..e63aa76 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlElementParser.cs @@ -0,0 +1,8 @@ +using System.Xml.Linq; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public interface IPetroglyphXmlElementParser where T : notnull +{ + T Parse(XElement element); +} \ 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 new file mode 100644 index 0000000..327d6d2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileParser.cs @@ -0,0 +1,8 @@ +using System.IO; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public interface IPetroglyphXmlFileParser where T : notnull +{ + 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 new file mode 100644 index 0000000..6ffba48 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlFileParserBase.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.IO.Abstractions; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Extensions.DependencyInjection; +using PG.Commons.Utilities; +using PG.StarWarsGame.Files.XML.ErrorHandling; +#if NETSTANDARD2_0 +using AnakinRaW.CommonUtilities.FileSystem; +#endif + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class PetroglyphXmlFileParserBase(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter) + : PetroglyphXmlParserBase(errorReporter) +{ + protected readonly IServiceProvider ServiceProvider = + serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + + protected readonly IFileSystem FileSystem = serviceProvider.GetRequiredService(); + + protected virtual bool LoadLineInfo => true; + + protected XElement? GetRootElement(Stream xmlStream, out string fileName) + { + fileName = GetStrippedFileName(xmlStream.GetFilePath()); + + if (string.IsNullOrEmpty(fileName)) + throw new InvalidOperationException("Unable to parse XML from unnamed stream. Either parse from a file or MEG stream."); + + SkipLeadingWhiteSpace(fileName, xmlStream); + + var xmlReader = XmlReader.Create(xmlStream, new XmlReaderSettings + { + IgnoreWhitespace = true, + IgnoreComments = true, + IgnoreProcessingInstructions = true + }, fileName); + + var options = LoadOptions.SetBaseUri; + if (LoadLineInfo) + options |= LoadOptions.SetLineInfo; + + var doc = XDocument.Load(xmlReader, options); + return doc.Root; + } + + private string GetStrippedFileName(string filePath) + { + if (!FileSystem.Path.IsPathFullyQualified(filePath)) + return filePath; + + var pathPartIndex = filePath.LastIndexOf("DATA\\XML\\", StringComparison.OrdinalIgnoreCase); + + if (pathPartIndex == -1) + return filePath; + + return filePath.Substring(pathPartIndex); + } + + + private void SkipLeadingWhiteSpace(string fileName, Stream stream) + { + using var r = new StreamReader(stream, Encoding.ASCII, false, 10, true); + var count = 0; + + while (true) + { + var c = (char)r.Read(); + if (!char.IsWhiteSpace(c)) + break; + count++; + } + + if (count != 0) + { + OnParseError(new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, 0), + XmlParseErrorKind.DataBeforeHeader, $"XML header is not the first entry of the XML file."));} + + stream.Position = count; + } +} \ 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 new file mode 100644 index 0000000..8d4c02d --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs @@ -0,0 +1,53 @@ +using System.Linq; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using System.Xml.Linq; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class PetroglyphXmlParserBase(IXmlParserErrorReporter? errorReporter) +{ + protected readonly IXmlParserErrorReporter? ErrorReporter = errorReporter; + + public sealed override string ToString() + { + return GetType().FullName!; + } + + protected string GetTagName(XElement element) + { + return element.Name.LocalName; + } + + protected string GetNameAttributeValue(XElement element) + { + var nameAttribute = element.Attributes() + .FirstOrDefault(a => a.Name.LocalName == "Name"); + return nameAttribute is null ? string.Empty : nameAttribute.Value; + } + + protected bool GetNameAttributeValue(XElement element, out string value) + { + return GetAttributeValue(element, "Name", out value!, string.Empty); + } + + protected bool GetAttributeValue(XElement element, string attribute, out string? value, string? defaultValue = null) + { + 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}'")); + return false; + } + + value = nameAttribute.Value; + return true; + } + + protected virtual void OnParseError(XmlParseErrorEventArgs error) + { + ErrorReporter?.Report(ToString(), error); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlElementParser.cs deleted file mode 100644 index e699b80..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlElementParser.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlElementParser : IPetroglyphXmlParser; - -public interface IPetroglyphXmlElementParser : IPetroglyphXmlElementParser, IPetroglyphXmlParser; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileContainerParser.cs new file mode 100644 index 0000000..62be16b --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileContainerParser.cs @@ -0,0 +1,10 @@ +using System.IO; +using PG.Commons.Collections; +using PG.Commons.Hashing; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public interface IPetroglyphXmlFileContainerParser where T : notnull +{ + void ParseFile(Stream xmlStream, IValueListDictionary parsedEntries); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs deleted file mode 100644 index 5a3875a..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileParser.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.IO; -using PG.Commons.Collections; -using PG.Commons.Hashing; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlFileParser : IPetroglyphXmlParser -{ - public object? ParseFile(Stream stream); -} - -public interface IPetroglyphXmlFileParser : IPetroglyphXmlParser, IPetroglyphXmlFileParser -{ - public new T? ParseFile(Stream stream); - - public void ParseFile(Stream stream, IValueListDictionary parsedEntries); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlParser.cs deleted file mode 100644 index 220fa05..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlParser.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Xml.Linq; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public interface IPetroglyphXmlParser -{ - object Parse(XElement element); -} - -public interface IPetroglyphXmlParser : IPetroglyphXmlParser -{ - new T Parse(XElement element); -} \ 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 index 53ce6cb..77afc74 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs @@ -1,22 +1,11 @@ -using System; -using System.Linq; +using System.Linq; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphXmlElementParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) - : PetroglyphXmlParser(serviceProvider, listener) +public abstract class PetroglyphXmlElementParser(IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlParserBase(errorReporter), IPetroglyphXmlElementParser where T : notnull { - protected string GetTagName(XElement element) - { - return element.Name.LocalName; - } - - protected string GetNameAttributeValue(XElement element) - { - var nameAttribute = element.Attributes() - .FirstOrDefault(a => a.Name.LocalName == "Name"); - return nameAttribute is null ? string.Empty : nameAttribute.Value; - } + public abstract T Parse(XElement element); } \ 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 new file mode 100644 index 0000000..4e371e2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileContainerParser.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Xml.Linq; +using PG.Commons.Collections; +using PG.Commons.Hashing; +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public abstract class PetroglyphXmlFileContainerParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? listener = null) + : PetroglyphXmlFileParserBase(serviceProvider, listener), IPetroglyphXmlFileContainerParser where T : notnull +{ + public void ParseFile(Stream xmlStream, IValueListDictionary parsedEntries) + { + var root = GetRootElement(xmlStream, out var fileName); + if (root is not null) + Parse(root, parsedEntries, fileName); + } + + protected abstract void Parse(XElement element, IValueListDictionary parsedElements, string fileName); +} \ 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 be0d855..986cfef 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlFileParser.cs @@ -1,116 +1,24 @@ using System; using System.IO; -using System.IO.Abstractions; -using System.Text; -using System.Xml; using System.Xml.Linq; -using Microsoft.Extensions.DependencyInjection; -using PG.Commons.Hashing; -using PG.Commons.Utilities; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.Commons.Collections; - -#if NETSTANDARD2_0 -using AnakinRaW.CommonUtilities.FileSystem; -#endif namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphXmlFileParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) : - PetroglyphXmlParser(serviceProvider, listener), IPetroglyphXmlFileParser +public abstract class PetroglyphXmlFileParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) + : PetroglyphXmlFileParserBase(serviceProvider, errorReporter), IPetroglyphXmlFileParser where T : notnull { - private readonly IXmlParserErrorListener? _listener = listener; - private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); - - protected virtual bool LoadLineInfo => true; - - public T ParseFile(Stream xmlStream) + public T? ParseFile(Stream xmlStream) { var root = GetRootElement(xmlStream, out var fileName); if (root is null) - OnParseError(new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, 0), XmlParseErrorKind.EmptyRoot, - "Unable to get root node from XML file.")); - return root is null ? default! : Parse(root, fileName); - } - - public void ParseFile(Stream xmlStream, IValueListDictionary parsedEntries) - { - var root = GetRootElement(xmlStream, out var fileName); - if (root is not null) - Parse(root, parsedEntries, fileName); - } - - public sealed override T Parse(XElement element) - { - var fileName = GetStrippedFileName(XmlLocationInfo.FromElement(element).XmlFile); - return Parse(element, fileName); - } - - protected abstract T Parse(XElement element, string fileName); - - protected abstract void Parse(XElement element, IValueListDictionary parsedElements, string fileName); - - private XElement? GetRootElement(Stream xmlStream, out string fileName) - { - fileName = GetStrippedFileName(xmlStream.GetFilePath()); - - if (string.IsNullOrEmpty(fileName)) - throw new InvalidOperationException("Unable to parse XML from unnamed stream. Either parse from a file or MEG stream."); - - SkipLeadingWhiteSpace(fileName, xmlStream); - - var xmlReader = XmlReader.Create(xmlStream, new XmlReaderSettings { - IgnoreWhitespace = true, - IgnoreComments = true, - IgnoreProcessingInstructions = true - }, fileName); - - var options = LoadOptions.SetBaseUri; - if (LoadLineInfo) - options |= LoadOptions.SetLineInfo; - - var doc = XDocument.Load(xmlReader, options); - return doc.Root; - } - - protected string GetStrippedFileName(string filePath) - { - if (!_fileSystem.Path.IsPathFullyQualified(filePath)) - return filePath; - - var pathPartIndex = filePath.LastIndexOf("DATA\\XML\\", StringComparison.OrdinalIgnoreCase); - - if (pathPartIndex == -1) - return filePath; - - return filePath.Substring(pathPartIndex); - } - - - private void SkipLeadingWhiteSpace(string fileName, Stream stream) - { - using var r = new StreamReader(stream, Encoding.ASCII, false, 10, true); - var count = 0; - - while (true) - { - var c = (char)r.Read(); - if (!char.IsWhiteSpace(c)) - break; - count++; + var location = new XmlLocationInfo(fileName, 0); + OnParseError(new XmlParseErrorEventArgs(location, XmlParseErrorKind.EmptyRoot, + "Unable to get root node from XML file.")); } - - if (count != 0) - _listener?.OnXmlParseError(this, new XmlParseErrorEventArgs(new XmlLocationInfo(fileName, 0), - XmlParseErrorKind.DataBeforeHeader, $"XML header is not the first entry of the XML file.")); - - stream.Position = count; + return root is null ? default : Parse(root, fileName); } - - object? IPetroglyphXmlFileParser.ParseFile(Stream stream) - { - return ParseFile(stream); - } + protected abstract T Parse(XElement element, string fileName); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs deleted file mode 100644 index cdebbf3..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlParser.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Xml.Linq; -using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; -using System.Linq; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public abstract class PetroglyphXmlParser : IPetroglyphXmlParser -{ - private readonly IXmlParserErrorListener? _errorListener; - - protected IServiceProvider ServiceProvider { get; } - - protected ILogger? Logger { get; } - - protected IPrimitiveParserProvider PrimitiveParserProvider { get; } - - protected PetroglyphXmlParser(IServiceProvider serviceProvider, IXmlParserErrorListener? errorListener = null) - { - ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - Logger = serviceProvider.GetService()?.CreateLogger(GetType()); - PrimitiveParserProvider = serviceProvider.GetRequiredService(); - _errorListener = errorListener; - } - - public abstract T Parse(XElement element); - - protected virtual void OnParseError(XmlParseErrorEventArgs e) - { - _errorListener?.OnXmlParseError(this, e); - } - - protected string GetTagName(XElement element) - { - return element.Name.LocalName; - } - - protected bool GetNameAttributeValue(XElement element, out string value) - { - return GetAttributeValue(element, "Name", out value!, string.Empty); - } - - protected bool GetAttributeValue(XElement element, string attribute, out string? value, string? defaultValue = null) - { - 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}'")); - return false; - } - - value = nameAttribute.Value; - return true; - } - - object IPetroglyphXmlParser.Parse(XElement element) - { - return Parse(element)!; - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlPrimitiveElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlPrimitiveElementParser.cs deleted file mode 100644 index cd97dd8..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlPrimitiveElementParser.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Files.XML.Parsers; - -public abstract class PetroglyphXmlPrimitiveElementParser : PetroglyphXmlParser, IPetroglyphXmlElementParser -{ - private protected PetroglyphXmlPrimitiveElementParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : - base(serviceProvider, listener) - { - } -} \ No newline at end of file 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 a0418db..5597e5e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; @@ -9,9 +7,11 @@ namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; // Format: Key, Value, Key, Value // There might be arbitrary spaces, tabs and newlines // TODO: This class is not yet implemented, compliant to the engine -public sealed class CommaSeparatedStringKeyValueListParser : PetroglyphXmlPrimitiveElementParser> +public sealed class CommaSeparatedStringKeyValueListParser : PetroglyphPrimitiveXmlParser> { - internal CommaSeparatedStringKeyValueListParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly CommaSeparatedStringKeyValueListParser Instance = new(); + + private CommaSeparatedStringKeyValueListParser() { } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs deleted file mode 100644 index 0725d5e..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/IPrimitiveParserProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; - -public interface IPrimitiveParserProvider -{ - PetroglyphXmlStringParser StringParser { get; } - - PetroglyphXmlUnsignedIntegerParser UIntParser { get; } - - PetroglyphXmlLooseStringListParser LooseStringListParser { get; } - - PetroglyphXmlIntegerParser IntParser { get; } - - PetroglyphXmlFloatParser FloatParser { get; } - - PetroglyphXmlByteParser ByteParser { get; } - - PetroglyphXmlMax100ByteParser Max100ByteParser { get; } - - PetroglyphXmlBooleanParser BooleanParser { get; } - - PetroglyphXmlVector2FParser Vector2FParser { get; } - - CommaSeparatedStringKeyValueListParser CommaSeparatedStringKeyValueListParser { get; } -} \ 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 new file mode 100644 index 0000000..59e6edf --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -0,0 +1,10 @@ +using PG.StarWarsGame.Files.XML.ErrorHandling; + +namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; + +public abstract class PetroglyphPrimitiveXmlParser : PetroglyphXmlElementParser where T : notnull +{ + private protected PetroglyphPrimitiveXmlParser() : base(PrimitiveXmlErrorReporter.Instance) + { + } +} \ 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 f0124a8..4dc6dd8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs @@ -1,12 +1,13 @@ using System; using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlBooleanParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlBooleanParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlBooleanParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlBooleanParser Instance = new(); + + private PetroglyphXmlBooleanParser() { } 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 541a148..30fb335 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs @@ -1,19 +1,19 @@ -using System; -using System.Xml.Linq; -using Microsoft.Extensions.Logging; +using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlByteParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlByteParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlByteParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlByteParser Instance = new(); + + private PetroglyphXmlByteParser() { } public override byte Parse(XElement element) { - var intValue = PrimitiveParserProvider.IntParser.Parse(element); + var intValue = PetroglyphXmlIntegerParser.Instance.Parse(element); var asByte = (byte)intValue; if (intValue != asByte) @@ -24,10 +24,4 @@ public override byte Parse(XElement element) return asByte; } - - protected override void OnParseError(XmlParseErrorEventArgs e) - { - Logger?.LogWarning(e.Message); - base.OnParseError(e); - } } \ 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 8c60c60..de2f33c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs @@ -1,14 +1,15 @@ using System; using System.Globalization; using System.Xml.Linq; -using Microsoft.Extensions.Logging; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlFloatParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlFloatParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlFloatParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlFloatParser Instance = new(); + + private PetroglyphXmlFloatParser() { } @@ -42,10 +43,4 @@ public float ParseAtLeast(XElement element, float minValue) return corrected; } - - protected override void OnParseError(XmlParseErrorEventArgs e) - { - Logger?.LogWarning(e.Message); - base.OnParseError(e); - } } \ No newline at end of file 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 7a0407c..caffc2d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -1,14 +1,14 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Xml.Linq; +using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Utilities; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlIntegerParser : PetroglyphXmlPrimitiveElementParser -{ - internal PetroglyphXmlIntegerParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) +public sealed class PetroglyphXmlIntegerParser : PetroglyphPrimitiveXmlParser +{ + public static readonly PetroglyphXmlIntegerParser Instance = new(); + + private PetroglyphXmlIntegerParser() { } @@ -39,10 +39,4 @@ public int ParseWithRange(XElement element, int minValue, int maxValue) } return clamped; } - - protected override void OnParseError(XmlParseErrorEventArgs e) - { - Logger?.LogWarning(e.Message); - base.OnParseError(e); - } } \ 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 6948952..8710e35 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -1,13 +1,11 @@ -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Generic; -using System.Linq; using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlLooseStringListParser : PetroglyphXmlPrimitiveElementParser> +public sealed class PetroglyphXmlLooseStringListParser : PetroglyphPrimitiveXmlParser> { // These are the characters the engine uses as a generic list separator private static readonly char[] Separators = @@ -18,8 +16,10 @@ public sealed class PetroglyphXmlLooseStringListParser : PetroglyphXmlPrimitiveE '\n', '\r' ]; - - internal PetroglyphXmlLooseStringListParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + + public static readonly PetroglyphXmlLooseStringListParser Instance = new(); + + private PetroglyphXmlLooseStringListParser() { } @@ -42,10 +42,4 @@ public override IList Parse(XElement element) return entries; } - - protected override void OnParseError(XmlParseErrorEventArgs e) - { - Logger?.LogWarning(e.Message); - base.OnParseError(e); - } } \ 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 7f6d2ad..3090f16 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs @@ -1,20 +1,20 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Xml.Linq; +using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Utilities; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlMax100ByteParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlMax100ByteParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlMax100ByteParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlMax100ByteParser Instance = new(); + + private PetroglyphXmlMax100ByteParser() { } public override byte Parse(XElement element) { - var intValue = PrimitiveParserProvider.IntParser.Parse(element); + var intValue = PetroglyphXmlIntegerParser.Instance.Parse(element); if (intValue > 100) intValue = 100; @@ -38,8 +38,14 @@ public override byte Parse(XElement element) public byte ParseWithRange(XElement element, byte minValue, byte maxValue) { - if (maxValue > 100) - Logger?.LogWarning("Upper bound for clamp range is above 100 for a parser that is meant to be capped to value 100."); + if (maxValue > 100) + { + OnParseError(new XmlParseErrorEventArgs( + element, XmlParseErrorKind.InvalidValue, + $"The provided maxValue '{maxValue}' is above 100.")); + } + + // TODO: Do we need to coerce maxValue??? var value = Parse(element); @@ -51,10 +57,4 @@ public byte ParseWithRange(XElement element, byte minValue, byte maxValue) } return clamped; } - - protected override void OnParseError(XmlParseErrorEventArgs e) - { - Logger?.LogWarning(e.Message); - base.OnParseError(e); - } } \ 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 bc8ca42..f31745f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs @@ -1,12 +1,12 @@ -using System; -using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; +using System.Xml.Linq; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlStringParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlStringParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlStringParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlStringParser Instance = new(); + + private PetroglyphXmlStringParser() { } 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 c80fddf..0a97b75 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs @@ -1,19 +1,19 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Xml.Linq; +using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlUnsignedIntegerParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlUnsignedIntegerParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlUnsignedIntegerParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlUnsignedIntegerParser Instance = new(); + + private PetroglyphXmlUnsignedIntegerParser() { } public override uint Parse(XElement element) { - var intValue = PrimitiveParserProvider.IntParser.Parse(element); + var intValue = PetroglyphXmlIntegerParser.Instance.Parse(element); var asUint = (uint)intValue; if (intValue != asUint) @@ -24,10 +24,4 @@ public override uint Parse(XElement element) return asUint; } - - protected override void OnParseError(XmlParseErrorEventArgs e) - { - Logger?.LogWarning(e.Message); - base.OnParseError(e); - } } \ No newline at end of file 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 a099ad1..ec24512 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs @@ -1,33 +1,34 @@ -using System; -using System.Numerics; +using System.Numerics; using System.Xml.Linq; -using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; -public sealed class PetroglyphXmlVector2FParser : PetroglyphXmlPrimitiveElementParser +public sealed class PetroglyphXmlVector2FParser : PetroglyphPrimitiveXmlParser { - internal PetroglyphXmlVector2FParser(IServiceProvider serviceProvider, IPrimitiveXmlParserErrorListener listener) : base(serviceProvider, listener) + public static readonly PetroglyphXmlVector2FParser Instance = new(); + + private static readonly PetroglyphXmlFloatParser FloatParser = PetroglyphXmlFloatParser.Instance; + private static readonly PetroglyphXmlLooseStringListParser LooseStringListParser = PetroglyphXmlLooseStringListParser.Instance; + + private PetroglyphXmlVector2FParser() { } public override Vector2 Parse(XElement element) { - var listOfValues = PrimitiveParserProvider.LooseStringListParser.Parse(element); + var listOfValues = LooseStringListParser.Parse(element); if (listOfValues.Count == 0) return default; - var floatParser = PrimitiveParserProvider.FloatParser; - if (listOfValues.Count == 1) { - var value = floatParser.Parse(listOfValues[0], element); + var value = FloatParser.Parse(listOfValues[0], element); return new Vector2(value, 0.0f); } - var value1 = floatParser.Parse(listOfValues[0], element); - var value2 = floatParser.Parse(listOfValues[1], element); + var value1 = FloatParser.Parse(listOfValues[0], element); + var value2 = FloatParser.Parse(listOfValues[1], element); return new Vector2(value1, value2); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs deleted file mode 100644 index 3d17ebc..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PrimitiveParserProvider.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Threading; -using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; - -internal class PrimitiveParserProvider(IServiceProvider serviceProvider) : IPrimitiveParserProvider -{ - private readonly IPrimitiveXmlParserErrorListener _primitiveParserErrorListener = serviceProvider.GetRequiredService(); - - private PetroglyphXmlStringParser _stringParser = null!; - private PetroglyphXmlUnsignedIntegerParser _uintParser = null!; - private PetroglyphXmlLooseStringListParser _looseStringListParser = null!; - private PetroglyphXmlIntegerParser _intParser = null!; - private PetroglyphXmlFloatParser _floatParser = null!; - private PetroglyphXmlByteParser _byteParser = null!; - private PetroglyphXmlMax100ByteParser _max100ByteParser = null!; - private PetroglyphXmlBooleanParser _booleanParser = null!; - private PetroglyphXmlVector2FParser _vector2FParser = null!; - - private CommaSeparatedStringKeyValueListParser _commaSeparatedStringKeyValueListParser = null!; - - public PetroglyphXmlStringParser StringParser => - LazyInitializer.EnsureInitialized(ref _stringParser, () => new PetroglyphXmlStringParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlUnsignedIntegerParser UIntParser => - LazyInitializer.EnsureInitialized(ref _uintParser, () => new PetroglyphXmlUnsignedIntegerParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlLooseStringListParser LooseStringListParser => - LazyInitializer.EnsureInitialized(ref _looseStringListParser, () => new PetroglyphXmlLooseStringListParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlIntegerParser IntParser => - LazyInitializer.EnsureInitialized(ref _intParser, () => new PetroglyphXmlIntegerParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlFloatParser FloatParser => - LazyInitializer.EnsureInitialized(ref _floatParser, () => new PetroglyphXmlFloatParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlByteParser ByteParser => - LazyInitializer.EnsureInitialized(ref _byteParser, () => new PetroglyphXmlByteParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlMax100ByteParser Max100ByteParser => - LazyInitializer.EnsureInitialized(ref _max100ByteParser, () => new PetroglyphXmlMax100ByteParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlBooleanParser BooleanParser => - LazyInitializer.EnsureInitialized(ref _booleanParser, () => new PetroglyphXmlBooleanParser(serviceProvider, _primitiveParserErrorListener)); - - public PetroglyphXmlVector2FParser Vector2FParser => - LazyInitializer.EnsureInitialized(ref _vector2FParser, () => new PetroglyphXmlVector2FParser(serviceProvider, _primitiveParserErrorListener)); - - public CommaSeparatedStringKeyValueListParser CommaSeparatedStringKeyValueListParser => - LazyInitializer.EnsureInitialized(ref _commaSeparatedStringKeyValueListParser, () => new CommaSeparatedStringKeyValueListParser(serviceProvider, _primitiveParserErrorListener)); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs deleted file mode 100644 index 11308e2..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/XmlFileContainerParser.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; -using PG.Commons.Collections; -using PG.Commons.Hashing; -using PG.StarWarsGame.Files.XML.Data; -using PG.StarWarsGame.Files.XML.ErrorHandling; - -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; - -public class XmlFileContainerParser(IServiceProvider serviceProvider, IXmlParserErrorListener? listener = null) : - PetroglyphXmlFileParser(serviceProvider, listener) -{ - protected override bool LoadLineInfo => false; - - protected override void Parse(XElement element, IValueListDictionary parsedElements, string fileName) - { - throw new NotSupportedException(); - } - - protected override XmlFileContainer Parse(XElement element, string fileNaem) - { - var files = new List(); - foreach (var child in element.Elements()) - { - if (child.Name == "File") - { - var file = PrimitiveParserProvider.StringParser.Parse(child); - files.Add(file); - } - } - return new XmlFileContainer(files); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs new file mode 100644 index 0000000..4bed776 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.Data; +using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers.Primitives; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public sealed class XmlFileListParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null) : + PetroglyphXmlFileParser(serviceProvider, errorReporter) +{ + protected override bool LoadLineInfo => false; + + protected override XmlFileListContainer Parse(XElement element, string fileName) + { + var files = new List(); + foreach (var child in element.Elements()) + { + if (child.Name == "File") + { + var file = PetroglyphXmlStringParser.Instance.Parse(child); + files.Add(file); + } + } + return new XmlFileListContainer(files); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs index 7c431be..9e7c853 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlServiceContribution.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for details. using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; namespace PG.StarWarsGame.Files.XML; @@ -11,8 +9,5 @@ public static class XmlServiceContribution { public static void SupportXML(this IServiceCollection serviceCollection) { - serviceCollection.AddSingleton(_ => new PrimitiveXmlParserErrorBroker()); - serviceCollection.AddSingleton(sp => sp.GetRequiredService()); - serviceCollection.AddSingleton(sp => new PrimitiveParserProvider(sp)); } } \ No newline at end of file From 9c713666cb074f16678d78acfb67afa82c2144a7 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 11 Feb 2025 16:20:11 +0100 Subject: [PATCH 08/34] use field --- .../GuiDialog/GuiDialogGameManager.cs | 14 ++++++-------- .../IO/Repositories/EffectsRepository.cs | 3 ++- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index bbf90a1..66522e0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -25,23 +25,21 @@ internal partial class GuiDialogGameManager(GameRepository repository, DatabaseE private readonly Dictionary _defaultTextures = new(); private ReadOnlyDictionary _defaultTexturesRo = null!; - private IMtdFile? _megaTexture; - private GuiDialogsXml? _guiDialogsXml; - private string? _megaTextureFileName; private bool _megaTextureExists; + private string? _megaTextureFileName; + - public IMtdFile? MtdFile { get { ThrowIfNotInitialized(); - return _megaTexture; + return field; } private set { ThrowIfAlreadyInitialized(); - _megaTexture = value; + field = value; } } @@ -50,12 +48,12 @@ public GuiDialogsXml? GuiDialogsXml get { ThrowIfNotInitialized(); - return _guiDialogsXml; + return field; } private set { ThrowIfAlreadyInitialized(); - _guiDialogsXml = value; + field = value; } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs index 32e08dd..22f4949 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs @@ -4,7 +4,8 @@ namespace PG.StarWarsGame.Engine.IO.Repositories; -internal class EffectsRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : MultiPassRepository(baseRepository, serviceProvider) +internal class EffectsRepository(GameRepository baseRepository, IServiceProvider serviceProvider) + : MultiPassRepository(baseRepository, serviceProvider) { private static readonly string[] LookupPaths = [ From c77b52bb5ad320fe9a61392f353109f096c3c293 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Thu, 13 Feb 2025 09:47:35 +0100 Subject: [PATCH 09/34] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 10 +++++----- src/ModVerify/ModVerify.csproj | 2 +- .../Database/GameDatabaseService.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 2dd57ff..538885e 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -33,11 +33,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 0649885..c72b0dd 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -32,7 +32,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs index 77eea6f..774ce8b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs @@ -15,7 +15,7 @@ public Task InitializeGameAsync( { var repoFactory = serviceProvider.GetRequiredService(); - using var errorListenerWrapper = new DatabaseErrorReporterWrapper(gameInitializationOptions.ErrorListener); + using var errorListenerWrapper = new DatabaseErrorReporterWrapper(gameInitializationOptions.ErrorListener, serviceProvider); var repository = repoFactory.Create(gameInitializationOptions.TargetEngineType, gameInitializationOptions.Locations, errorListenerWrapper); var gameInitializer = new GameInitializer(repository, gameInitializationOptions.CancelOnError, serviceProvider); From f27d82f6b343748a19a143c275860ea87b2fd31e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 15 Feb 2025 12:35:27 +0100 Subject: [PATCH 10/34] start implementing command bar --- .../Audio/Sfx/SfxEventGameManager.cs | 4 +-- .../CommandBar/CommandBarGameManager.cs | 6 ++-- .../GuiDialogGameManager_Initialization.cs | 18 +++++----- .../Parsers/Data/CommandBarComponentParser.cs | 6 ++-- .../Xml/Parsers/Data/GameObjectParser.cs | 6 ++-- .../Xml/Parsers/Data/SfxEventParser.cs | 6 ++-- .../Xml/Parsers/IXmlContainerContentParser.cs | 2 +- .../Xml/Parsers/XmlContainerContentParser.cs | 19 +++++++---- .../XmlContainerParserErrorEventArgs.cs | 33 +++++-------------- .../Xml/Parsers/XmlObjectParser.cs | 8 ++--- .../Parsers/XmlFileListParser.cs | 9 ++++- 11 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index 84989ea..ac2e105 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -47,7 +47,7 @@ await Task.Run(() => contentParser.ParseEntriesFromFileListXml( private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) { - if (e.IsContainer || e.IsError) + if (e.ErrorInXmlFileList || e.HasException) { e.Continue = false; ErrorReporter.Report(new InitializationError @@ -60,7 +60,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) { - if (errorEventArgs.IsError) + if (errorEventArgs.HasException) return $"Error while parsing SFXEvent XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; return "Could not find SFXEventFiles.xml"; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index c0dea28..b7dbf4d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -25,7 +25,7 @@ internal class CommandBarGameManager( { protected override async Task InitializeCoreAsync(CancellationToken token) { - Logger?.LogInformation("PCreating command bar components..."); + Logger?.LogInformation("Creating command bar components..."); var contentParser = ServiceProvider.GetRequiredService(); contentParser.XmlParseError += OnParseError; @@ -51,7 +51,7 @@ await Task.Run(() => contentParser.ParseEntriesFromFileListXml( private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) { - if (e.IsContainer || e.IsError) + if (e.ErrorInXmlFileList || e.HasException) { e.Continue = false; ErrorReporter.Report(new InitializationError @@ -64,7 +64,7 @@ private void OnParseError(object sender, XmlContainerParserErrorEventArgs e) private static string GetMessage(XmlContainerParserErrorEventArgs errorEventArgs) { - if (errorEventArgs.IsError) + if (errorEventArgs.HasException) return $"Error while parsing CommandBar XML file '{errorEventArgs.File}': {errorEventArgs.Exception.Message}"; return "Could not find CommandBarComponentFiles.xml"; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index c9f3e6d..d023dca 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -51,20 +51,20 @@ protected override Task InitializeCoreAsync(CancellationToken token) GuiDialogsXml = guiDialogs; - InitializeTextures(guiDialogs.TextureData, ErrorReporter); + InitializeTextures(guiDialogs.TextureData); }, token); } - private void InitializeTextures(GuiDialogsXmlTextureData textureData, DatabaseErrorReporterWrapper errorReporter) + private void InitializeTextures(GuiDialogsXmlTextureData textureData) { - InitializeMegaTextures(textureData, ErrorReporter); + InitializeMegaTextures(textureData); var textures = textureData.Textures; if (textures.Count == 0) { - errorReporter.Report(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = "No Textures defined in GuiDialogs.xml" @@ -122,11 +122,11 @@ private Dictionary InitializeComponentT return result; } - private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, DatabaseErrorReporterWrapper errorReporter) + private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs) { if (guiDialogs.MegaTexture is null) { - errorReporter.Report(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = "MtdFile is not defined in GuiDialogs.xml" @@ -138,7 +138,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, Databas if (mtdPath.Length > MegaTextureMaxFilePathLength) { - errorReporter.Report(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"Mtd file path is longer than {MegaTextureMaxFilePathLength}." @@ -151,7 +151,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, Databas if (guiDialogs.CompressedMegaTexture is null) { - errorReporter.Report(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = "CompressedMegaTexture is not defined in GuiDialogs.xml" @@ -166,7 +166,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs, Databas if (textureFileNameWithExtension.Length > MegaTextureMaxFilePathLength) { - errorReporter.Report(new InitializationError + ErrorReporter.Report(new InitializationError { GameManager = ToString(), Message = $"MegaTexture file path is longer than {MegaTextureMaxFilePathLength}." 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 bc824da..1db211c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -17,10 +17,10 @@ public sealed class CommandBarComponentParser( IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { - public override CommandBarComponentData Parse(XElement element, out Crc32 nameCrc) + public override CommandBarComponentData Parse(XElement element, out Crc32 upperNameCrc) { - var name = GetXmlObjectName(element, out nameCrc); - var component = new CommandBarComponentData(name, nameCrc, XmlLocationInfo.FromElement(element)); + var name = GetXmlObjectName(element, out upperNameCrc); + var component = new CommandBarComponentData(name, upperNameCrc, XmlLocationInfo.FromElement(element)); Parse(component, element, default); component.CoerceValues(); return component; 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 e555af7..8035d7c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs @@ -31,12 +31,12 @@ public sealed class GameObjectParser( IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { - public override GameObject Parse(XElement element, out Crc32 nameCrc) + public override GameObject Parse(XElement element, out Crc32 upperNameCrc) { - var name = GetXmlObjectName(element, out nameCrc); + var name = GetXmlObjectName(element, out upperNameCrc); var type = GetTagName(element); var objectType = EstimateType(type); - var gameObject = new GameObject(type, name, nameCrc, objectType, XmlLocationInfo.FromElement(element)); + var gameObject = new GameObject(type, name, upperNameCrc, objectType, XmlLocationInfo.FromElement(element)); Parse(gameObject, element, default); 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 d5966c8..7a94fe3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs @@ -17,10 +17,10 @@ public sealed class SfxEventParser( IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { - public override SfxEvent Parse(XElement element, out Crc32 nameCrc) + public override SfxEvent Parse(XElement element, out Crc32 upperNameCrc) { - var name = GetXmlObjectName(element, out nameCrc); - var sfxEvent = new SfxEvent(name, nameCrc, XmlLocationInfo.FromElement(element)); + var name = GetXmlObjectName(element, out upperNameCrc); + var sfxEvent = new SfxEvent(name, upperNameCrc, XmlLocationInfo.FromElement(element)); Parse(sfxEvent, element, default); ValidateValues(sfxEvent, element); sfxEvent.CoerceValues(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IXmlContainerContentParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IXmlContainerContentParser.cs index 043cd9e..2a51982 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IXmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IXmlContainerContentParser.cs @@ -16,5 +16,5 @@ void ParseEntriesFromFileListXml( IGameRepository gameRepository, string lookupPath, ValueListDictionary entries, - Action? onFileParseAction = null); + Action? onFileParseAction = null) where T : notnull; } \ 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 index 2d8cd2a..61686cb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -26,18 +26,17 @@ public void ParseEntriesFromFileListXml( IGameRepository gameRepository, string lookupPath, ValueListDictionary entries, - Action? onFileParseAction = null) + Action? onFileParseAction = null) where T : notnull { - var containerParser = new XmlFileListParser(Services, reporter); Logger.LogDebug($"Parsing container data '{xmlFile}'"); using var containerStream = gameRepository.TryOpenFile(xmlFile); if (containerStream == null) { - reporter.Report(containerParser.ToString(), XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + reporter.Report(ToString(), XmlParseErrorEventArgs.FromMissingFile(xmlFile)); Logger.LogWarning($"Could not find XML file '{xmlFile}'"); - var args = new XmlContainerParserErrorEventArgs(xmlFile, true) + var args = new XmlContainerParserErrorEventArgs(xmlFile, null, true) { // No reason to continue Continue = false @@ -50,13 +49,14 @@ public void ParseEntriesFromFileListXml( try { - container = containerParser.ParseFile(containerStream); + 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) { - var args = new XmlContainerParserErrorEventArgs(e, xmlFile, true) + var args = new XmlContainerParserErrorEventArgs(xmlFile, e, true) { // No reason to continue Continue = false @@ -101,7 +101,7 @@ public void ParseEntriesFromFileListXml( { reporter.Report(parser.ToString(), new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); - var args = new XmlContainerParserErrorEventArgs(e, file); + var args = new XmlContainerParserErrorEventArgs(file, e); XmlParseError?.Invoke(this, args); if (!args.Continue) @@ -109,4 +109,9 @@ public void ParseEntriesFromFileListXml( } } } + + public override string ToString() + { + return GetType().FullName; + } } \ 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/XmlContainerParserErrorEventArgs.cs index 3661c21..87ccce1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerParserErrorEventArgs.cs @@ -3,38 +3,23 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; -internal class XmlContainerParserErrorEventArgs +internal class XmlContainerParserErrorEventArgs(string file, XmlException? exception = null, bool isXmlFileList = false) { - private bool _continue = true; - public bool Continue { - get => _continue; + get; // Once this is set to false, there is no way back. - set => _continue &= value; - } + set => field &= value; + } = true; - public bool IsContainer { get; } + public bool ErrorInXmlFileList { get; } = isXmlFileList; - public string File { get; } + public string File { get; } = file; [MemberNotNullWhen(true, nameof(Exception))] - public bool IsError => Exception is not null; - - public bool IsFileNotFound => !IsError; + public bool HasException => Exception is not null; - public XmlException? Exception { get; } + public bool IsFileNotFound => !HasException; - public XmlContainerParserErrorEventArgs(XmlException exception, string file, bool container = false) - { - Exception = exception; - File = file; - IsContainer = container; - } - - public XmlContainerParserErrorEventArgs(string file, bool container = false) - { - File = file; - IsContainer = container; - } + public XmlException? Exception { get; } = exception; } \ 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 6bd5dbb..345a6c5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs @@ -44,7 +44,7 @@ public abstract class XmlObjectParser( protected ICrc32HashingService HashingService { get; } = serviceProvider.GetRequiredService(); - public abstract TObject Parse(XElement element, out Crc32 nameCrc); + public abstract TObject Parse(XElement element, out Crc32 upperNameCrc); protected void Parse(TObject xmlObject, XElement element, in TParseState state) { @@ -61,11 +61,11 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) protected abstract bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState); - protected string GetXmlObjectName(XElement element, out Crc32 nameCrc32) + protected string GetXmlObjectName(XElement element, out Crc32 upperNameCrc32) { GetNameAttributeValue(element, out var name); - nameCrc32 = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.DefaultPGEncoding); - if (nameCrc32 == default) + upperNameCrc32 = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.DefaultPGEncoding); + if (upperNameCrc32 == default) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, $"Name for XmlObject cannot be empty.")); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index 4bed776..e37045e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -17,11 +17,18 @@ protected override XmlFileListContainer Parse(XElement element, string fileName) var files = new List(); foreach (var child in element.Elements()) { - if (child.Name == "File") + var tagName = GetTagName(child); + if (tagName == "File") { var file = PetroglyphXmlStringParser.Instance.Parse(child); files.Add(file); } + else + { + ErrorReporter?.Report(ToString(), + new XmlParseErrorEventArgs(child, XmlParseErrorKind.UnknownNode, + $"Tag '<{tagName}>' is not supported. Only '' is supported.")); + } } return new XmlFileListContainer(files); } From 111c6c799596e10b72f2f6bde11ad6e4e3cd8a2f Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 16 Feb 2025 12:20:25 +0100 Subject: [PATCH 11/34] polishing --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 1 + src/ModVerify.CliApp/Program.cs | 39 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 538885e..1e4c384 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -22,6 +22,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index b397068..7e7f744 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -37,6 +37,8 @@ internal class Program private static async Task Main(string[] args) { + PrintHeader(); + var result = 0; var parseResult = Parser.Default.ParseArguments(args); @@ -159,26 +161,27 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem } #endif loggingBuilder.SetMinimumLevel(logLevel); - + SetupFileLogging(loggingBuilder, fileSystem); + loggingBuilder.AddFilter((category, level) => - { - if (level < logLevel) - return false; - if (string.IsNullOrEmpty(category)) - return false; - if (category.StartsWith(EngineParserNamespace) || category.StartsWith(ParserNamespace)) - return false; - return true; - }).AddConsole(); + { + if (level < logLevel) + return false; + if (string.IsNullOrEmpty(category)) + return false; + if (category.StartsWith(EngineParserNamespace) || category.StartsWith(ParserNamespace)) + return false; + return true; + }) + .AddSimpleConsole(); } - + private static void SetupFileLogging(ILoggingBuilder loggingBuilder, IFileSystem fileSystem) { var logPath = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), "ModVerify_log.txt"); - var logger = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Verbose() @@ -195,4 +198,16 @@ private static bool IsXmlParserLogging(LogEvent logEvent) { return Matching.FromSource(ParserNamespace)(logEvent) || Matching.FromSource(EngineParserNamespace)(logEvent); } + + private static void PrintHeader() + { + Console.WriteLine("***********************************"); + Console.WriteLine("***********************************"); + Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Mod Verify")); + Console.WriteLine("***********************************"); + Console.WriteLine("***********************************"); + Console.WriteLine(" by Anakin"); + Console.WriteLine(); + Console.WriteLine(); + } } \ No newline at end of file From 8a390cbf28245003f86c76c4e65a64e69ebb70d6 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Wed, 19 Feb 2025 21:10:47 +0100 Subject: [PATCH 12/34] initialize command bar --- .../Properties/launchSettings.json | 5 + ....cs => ConcurrentGameGameErrorReporter.cs} | 14 +- .../Reporting/IDatabaseErrorCollection.cs | 2 +- src/ModVerify/VerificationProvider.cs | 6 +- .../InitializationErrorReporter.cs | 2 +- .../DatabaseError/XmlParseErrorReporter.cs | 2 +- src/ModVerify/VerifyGamePipeline.cs | 4 +- .../Audio/Sfx/SfxEventGameManager.cs | 8 +- .../CommandBar/CommandBarComponentGroup.cs | 21 + .../CommandBar/CommandBarComponentId.cs | 972 +++++++++++++++++ .../CommandBar/CommandBarComponentType.cs | 22 +- .../CommandBar/CommandBarGameManager.cs | 155 ++- .../CommandBar/Components/ButtonMode.cs | 14 + .../Components/CommandBarBarComponent.cs | 8 + .../Components/CommandBarBaseComponent.cs | 74 ++ .../CommandBarButtonComponentClass.cs | 10 + .../Components/CommandBarComponentTextData.cs | 23 + .../Components/CommandBarIconComponent.cs | 8 + .../Components/CommandBarModelComponent.cs | 8 + .../Components/CommandBarSelectComponent.cs | 8 + .../Components/CommandBarShellComponent.cs | 10 + .../CommandBarTextButtonComponent.cs | 32 + .../Components/CommandBarTextComponent.cs | 28 + .../CommandBar/ICommandBarGameManager.cs | 10 + .../SupportedCommandBarComponentData.cs | 976 ++++++++++++++++++ .../CommandBar/Xml/CommandBarComponentData.cs | 7 + .../ErrorReporting/DatabaseErrorReporter.cs | 12 - .../ErrorReporting/IDatabaseErrorReporter.cs | 8 - .../Database/GameDatabaseService.cs | 4 +- .../Database/GameInitializationOptions.cs | 4 +- .../Database/GameInitializer.cs | 4 +- .../Database/GameManagerBase.cs | 8 +- .../ErrorReporting/EngineAssert.cs | 38 + .../ErrorReporting/GameErrorReporter.cs | 16 + .../GameErrorReporterWrapper.cs} | 28 +- .../ErrorReporting/IGameErrorReporter.cs | 10 + .../ErrorReporting/InitializationError.cs | 2 +- .../{Database => }/ErrorReporting/XmlError.cs | 5 +- .../GameConstants/GameConstants.cs | 4 +- .../GameObjects/GameObjectTypeGameManager.cs | 8 +- .../GuiDialog/GuiDialogGameManager.cs | 4 +- .../GuiDialogGameManager_Initialization.cs | 2 +- .../IO/Repositories/FocGameRepository.cs | 4 +- .../IO/Repositories/GameRepository.cs | 7 +- .../IO/Repositories/GameRepositoryFactory.cs | 4 +- .../IO/Repositories/IGameRepositoryFactory.cs | 4 +- .../PetroglyphEngineServiceContribution.cs | 4 - .../Rendering/RgbaColor.cs | 29 +- .../Parsers/Data/CommandBarComponentParser.cs | 20 +- .../Xml/Parsers/Data/GameObjectParser.cs | 8 +- .../Xml/Parsers/Data/SfxEventParser.cs | 8 +- .../File/CommandBarComponentFileParser.cs | 4 +- .../Xml/Parsers/File/GuiDialogParser.cs | 1 - .../Xml/Parsers/IXmlContainerContentParser.cs | 20 - .../Xml/Parsers/XmlContainerContentParser.cs | 31 +- .../Xml/Parsers/XmlObjectParser.cs | 11 +- .../ErrorHandling/IXmlParserErrorReporter.cs | 6 +- .../PrimitiveXmlErrorReporter.cs | 3 +- .../ErrorHandling/XmlErrorEventHandler.cs | 6 +- .../ErrorHandling/XmlErrorReporter.cs | 5 +- .../ErrorHandling/XmlParseErrorKind.cs | 10 +- .../Base/IPetroglyphXmlElementParser.cs | 2 +- .../IPetroglyphXmlFileContainerParser.cs | 2 +- .../Parsers/Base/IPetroglyphXmlFileParser.cs | 2 +- .../Parsers/Base/IPetroglyphXmlParser.cs | 6 + .../Parsers/Base/PetroglyphXmlParserBase.cs | 18 +- .../CommaSeparatedStringKeyValueListParser.cs | 6 +- .../PetroglyphPrimitiveXmlParser.cs | 24 +- .../Primitives/PetroglyphXmlBooleanParser.cs | 22 +- .../Primitives/PetroglyphXmlByteParser.cs | 12 +- .../Primitives/PetroglyphXmlFloatParser.cs | 35 +- .../Primitives/PetroglyphXmlIntegerParser.cs | 11 +- .../PetroglyphXmlLooseStringListParser.cs | 21 +- .../PetroglyphXmlMax100ByteParser.cs | 8 +- .../PetroglyphXmlRgbaColorParser.cs | 34 + .../Primitives/PetroglyphXmlStringParser.cs | 8 +- .../PetroglyphXmlUnsignedIntegerParser.cs | 10 +- .../Primitives/PetroglyphXmlVector2FParser.cs | 15 +- .../Parsers/XmlFileListParser.cs | 9 +- 79 files changed, 2755 insertions(+), 261 deletions(-) rename src/ModVerify/Reporting/{ConcurrentGameDatabaseErrorReporter.cs => ConcurrentGameGameErrorReporter.cs} (64%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentGroup.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/ButtonMode.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBarComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarButtonComponentClass.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarComponentTextData.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarIconComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarModelComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarSelectComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextButtonComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextComponent.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database/ErrorReporting/DatabaseErrorReporterWrapper.cs => ErrorReporting/GameErrorReporterWrapper.cs} (55%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database => }/ErrorReporting/InitializationError.cs (70%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database => }/ErrorReporting/XmlError.cs (70%) delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/IXmlContainerContentParser.cs rename src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/{ => Base}/IPetroglyphXmlFileContainerParser.cs (68%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParser.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index 7f5c34d..e17a7c9 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -8,6 +8,11 @@ "FromModPath": { "commandName": "Project", "commandLineArgs": "-o verifyResults --baseline focBaseline.json --path C:/test --type Foc" + }, + + "Manual": { + "commandName": "Project", + "commandLineArgs": "-o verifyResults --baseline c:/users/lrs/desktop/focBaseline.json --game \"C:/Users/lrs/Privat/Star Wars Empire at War/corruption\" --fallbackGame \"C:/Users/lrs/Privat/Star Wars Empire at War/GameData\" --type Foc" } } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorReporter.cs b/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs similarity index 64% rename from src/ModVerify/Reporting/ConcurrentGameDatabaseErrorReporter.cs rename to src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs index f85ebd2..c185518 100644 --- a/src/ModVerify/Reporting/ConcurrentGameDatabaseErrorReporter.cs +++ b/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs @@ -1,19 +1,22 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; namespace AET.ModVerify.Reporting; -internal class ConcurrentGameDatabaseErrorReporter : DatabaseErrorReporter, IDatabaseErrorCollection +internal class ConcurrentGameGameErrorReporter : GameErrorReporter, IDatabaseErrorCollection { private readonly ConcurrentBag _xmlErrors = new(); - private readonly ConcurrentBag _initializationErrors = new(); + private readonly ConcurrentBag _asserts = new(); public IEnumerable XmlErrors => _xmlErrors.ToList(); + public IEnumerable InitializationErrors => _initializationErrors.ToList(); + public IEnumerable Asserts => _asserts.ToList(); + public override void Report(XmlError error) { _xmlErrors.Add(error); @@ -23,4 +26,9 @@ public override void Report(InitializationError error) { _initializationErrors.Add(error); } + + public override void Assert(EngineAssert assert) + { + _asserts.Add(assert); + } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs index 90c8203..1b11b8f 100644 --- a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs +++ b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; namespace AET.ModVerify.Reporting; diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify/VerificationProvider.cs index ca0f42d..66bb8b1 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify/VerificationProvider.cs @@ -10,9 +10,9 @@ internal class VerificationProvider(IServiceProvider serviceProvider) : IVerific { public IEnumerable GetAllDefaultVerifiers(IGameDatabase database, GameVerifySettings settings) { - yield return new ReferencedModelsVerifier(database, settings, serviceProvider); - yield return new DuplicateNameFinder(database, settings, serviceProvider); - yield return new AudioFilesVerifier(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 ReferencedTexturesVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs index e2e2e10..d003fca 100644 --- a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs +++ b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs @@ -1,6 +1,6 @@ using System; using AET.ModVerify.Reporting; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; namespace AET.ModVerify.Verifiers; diff --git a/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs b/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs index 9c0bdc9..5f3fc5c 100644 --- a/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs +++ b/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs @@ -4,7 +4,7 @@ using AET.ModVerify.Reporting; using AET.ModVerify.Utilities; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Files.XML.ErrorHandling; diff --git a/src/ModVerify/VerifyGamePipeline.cs b/src/ModVerify/VerifyGamePipeline.cs index 86307a7..4ae7ccf 100644 --- a/src/ModVerify/VerifyGamePipeline.cs +++ b/src/ModVerify/VerifyGamePipeline.cs @@ -52,12 +52,12 @@ protected sealed override async Task RunCoreAsync(CancellationToken token) { var databaseService = ServiceProvider.GetRequiredService(); - var initializationErrorListener = new ConcurrentGameDatabaseErrorReporter(); + var initializationErrorListener = new ConcurrentGameGameErrorReporter(); var initOptions = new GameInitializationOptions { Locations = _gameLocations, TargetEngineType = _targetType, - ErrorListener = initializationErrorListener + GameErrorReporter = initializationErrorListener }; var database = await databaseService.InitializeGameAsync(initOptions, token); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs index ac2e105..5805746 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -3,17 +3,16 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine.Audio.Sfx; -internal class SfxEventGameManager(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class SfxEventGameManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), ISfxEventGameManager { public IEnumerable InstalledLanguages { get; private set; } = []; @@ -26,13 +25,12 @@ protected override async Task InitializeCoreAsync(CancellationToken token) Logger?.LogInformation("Parsing SFXEvents..."); - var contentParser = ServiceProvider.GetRequiredService(); + var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); contentParser.XmlParseError += OnParseError; try { await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\SFXEventFiles.XML", - ErrorReporter, GameRepository, "DATA\\XML", NamedEntries, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentGroup.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentGroup.cs new file mode 100644 index 0000000..1799264 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentGroup.cs @@ -0,0 +1,21 @@ +using PG.StarWarsGame.Engine.CommandBar.Components; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PG.StarWarsGame.Engine.CommandBar; + +public sealed class CommandBarComponentGroup +{ + public string Name { get; } + + public IReadOnlyList Components { get; } + + internal CommandBarComponentGroup(string name, IEnumerable components) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + if (components == null) + throw new ArgumentNullException(nameof(components)); + Components = components.ToList(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs new file mode 100644 index 0000000..ba42797 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs @@ -0,0 +1,972 @@ +namespace PG.StarWarsGame.Engine.CommandBar; + +public enum CommandBarComponentId +{ + MainShell = 0x0, + ProductionOptions = 0x1, + GalacticCamera = 0x2, + PlanetName = 0x3, + PlanetValue = 0x4, + PlanetAffiliation = 0x5, + PlayerCredits = 0x6, + ReinforcementCap = 0x7, + PlanetNameTactical = 0x8, + PlanetInfo = 0x9, + Production0 = 0xa, + Production1 = 0xb, + Production2 = 0xc, + Production3 = 0xd, + Production4 = 0xe, + Production5 = 0xf, + Production6 = 0x10, + Production7 = 0x11, + Production8 = 0x12, + Production9 = 0x13, + Production10 = 0x14, + Production11 = 0x15, + Production12 = 0x16, + Production13 = 0x17, + Production14 = 0x18, + Production15 = 0x19, + Production16 = 0x1a, + Production17 = 0x1b, + Production18 = 0x1c, + Production19 = 0x1d, + Production20 = 0x1e, + Production21 = 0x1f, + Production22 = 0x20, + Production23 = 0x21, + Production24 = 0x22, + Production25 = 0x23, + Production26 = 0x24, + Production27 = 0x25, + Production28 = 0x26, + Production29 = 0x27, + Production30 = 0x28, + Production31 = 0x29, + Production32 = 0x2a, + Production33 = 0x2b, + Production34 = 0x2c, + Production35 = 0x2d, + Production36 = 0x2e, + Production37 = 0x2f, + Production38 = 0x30, + Production39 = 0x31, + Production40 = 0x32, + Production41 = 0x33, + Production42 = 0x34, + Production43 = 0x35, + Production44 = 0x36, + Production45 = 0x37, + Production46 = 0x38, + Production47 = 0x39, + DroidHelp = 0x3a, + DroidHelpTactical = 0x3b, + CurrentDay = 0x3c, + DayCredits = 0x3d, + PopulationCap = 0x3e, + Filter0 = 0x3f, + Filter1 = 0x40, + Filter2 = 0x41, + Filter3 = 0x42, + StoryArcButton = 0x43, + PlanetSummaryButton = 0x44, + SpaceTab = 0x45, + LandTab = 0x46, + Dial = 0x47, + ScrollRight = 0x48, + ScrollLeft = 0x49, + ZoomView = 0x4a, + PrevPlanet = 0x4b, + NextPlanet = 0x4c, + RadarGalactic = 0x4d, + TechLevel = 0x4e, + BalancePip = 0x4f, + BuildQueue00 = 0x50, + BuildQueue01 = 0x51, + BuildQueue02 = 0x52, + BuildQueue03 = 0x53, + BuildQueue04 = 0x54, + BuildQueue05 = 0x55, + BuildQueue06 = 0x56, + BuildQueue07 = 0x57, + BuildQueue08 = 0x58, + BuildQueue09 = 0x59, + OrganizationShell = 0x5a, + OrganizationCollision = 0x5b, + SmugglerBox = 0x5c, + SpaceStationUpgrade01 = 0x5d, + SpaceStationUpgrade02 = 0x5e, + HeroAbilitySlot = 0x5f, + PlanetOrganize0 = 0x60, + PlanetOrganize1 = 0x61, + PlanetOrganize2 = 0x62, + PlanetOrganize3 = 0x63, + PlanetOrganize4 = 0x64, + PlanetOrganize5 = 0x65, + PlanetOrganize6 = 0x66, + PlanetOrganize7 = 0x67, + PlanetOrganize8 = 0x68, + PlanetOrganize9 = 0x69, + SpecialStructureLand0 = 0x6a, + SpecialStructureLand1 = 0x6b, + SpecialStructureLand2 = 0x6c, + SpecialStructureLand3 = 0x6d, + SpecialStructureLand4 = 0x6e, + SpecialStructureLand5 = 0x6f, + SpecialStructureLand6 = 0x70, + SpecialStructureLand7 = 0x71, + SpecialStructureLand8 = 0x72, + SpecialStructureLandSell = 0x73, + BigFleet0 = 0x74, + BigFleet1 = 0x75, + BigFleet2 = 0x76, + BigFleet3 = 0x77, + Fleet0Slot0 = 0x78, + Fleet0Slot1 = 0x79, + Fleet0Slot2 = 0x7a, + Fleet0Slot3 = 0x7b, + Fleet0Slot4 = 0x7c, + Fleet0Slot5 = 0x7d, + Fleet0Slot6 = 0x7e, + Fleet0Slot7 = 0x7f, + Fleet0Slot8 = 0x80, + Fleet0Slot9 = 0x81, + Fleet0Slot10 = 0x82, + Fleet0Slot11 = 0x83, + Fleet0Slot12 = 0x84, + Fleet0Slot13 = 0x85, + Fleet0Slot14 = 0x86, + Fleet0Slot15 = 0x87, + Fleet0Slot16 = 0x88, + Fleet0Slot17 = 0x89, + Fleet0Slot18 = 0x8a, + Fleet0Slot19 = 0x8b, + Fleet0Slot20 = 0x8c, + Fleet0Slot21 = 0x8d, + Fleet0Slot22 = 0x8e, + Fleet0Slot23 = 0x8f, + Fleet0Slot24 = 0x90, + Fleet0Slot25 = 0x91, + Fleet0Slot26 = 0x92, + Fleet0Slot27 = 0x93, + Fleet0Slot28 = 0x94, + Fleet0Slot29 = 0x95, + Fleet0Slot30 = 0x96, + Fleet0Slot31 = 0x97, + Fleet0Slot32 = 0x98, + Fleet0Slot33 = 0x99, + Fleet0Slot34 = 0x9a, + Fleet1Slot0 = 0x9b, + Fleet1Slot1 = 0x9c, + Fleet1Slot2 = 0x9d, + Fleet1Slot3 = 0x9e, + Fleet1Slot4 = 0x9f, + Fleet1Slot5 = 0xa0, + Fleet1Slot6 = 0xa1, + Fleet1Slot7 = 0xa2, + Fleet1Slot8 = 0xa3, + Fleet1Slot9 = 0xa4, + Fleet1Slot10 = 0xa5, + Fleet1Slot11 = 0xa6, + Fleet1Slot12 = 0xa7, + Fleet1Slot13 = 0xa8, + Fleet1Slot14 = 0xa9, + Fleet1Slot15 = 0xaa, + Fleet1Slot16 = 0xab, + Fleet1Slot17 = 0xac, + Fleet1Slot18 = 0xad, + Fleet1Slot19 = 0xae, + Fleet1Slot20 = 0xaf, + Fleet1Slot21 = 0xb0, + Fleet1Slot22 = 0xb1, + Fleet1Slot23 = 0xb2, + Fleet1Slot24 = 0xb3, + Fleet1Slot25 = 0xb4, + Fleet1Slot26 = 0xb5, + Fleet1Slot27 = 0xb6, + Fleet1Slot28 = 0xb7, + Fleet1Slot29 = 0xb8, + Fleet1Slot30 = 0xb9, + Fleet1Slot31 = 0xba, + Fleet1Slot32 = 0xbb, + Fleet1Slot33 = 0xbc, + Fleet1Slot34 = 0xbd, + Fleet2Slot0 = 0xbe, + Fleet2Slot1 = 0xbf, + Fleet2Slot2 = 0xc0, + Fleet2Slot3 = 0xc1, + Fleet2Slot4 = 0xc2, + Fleet2Slot5 = 0xc3, + Fleet2Slot6 = 0xc4, + Fleet2Slot7 = 0xc5, + Fleet2Slot8 = 0xc6, + Fleet2Slot9 = 0xc7, + Fleet2Slot10 = 0xc8, + Fleet2Slot11 = 0xc9, + Fleet2Slot12 = 0xca, + Fleet2Slot13 = 0xcb, + Fleet2Slot14 = 0xcc, + Fleet2Slot15 = 0xcd, + Fleet2Slot16 = 0xce, + Fleet2Slot17 = 0xcf, + Fleet2Slot18 = 0xd0, + Fleet2Slot19 = 0xd1, + Fleet2Slot20 = 0xd2, + Fleet2Slot21 = 0xd3, + Fleet2Slot22 = 0xd4, + Fleet2Slot23 = 0xd5, + Fleet2Slot24 = 0xd6, + Fleet2Slot25 = 0xd7, + Fleet2Slot26 = 0xd8, + Fleet2Slot27 = 0xd9, + Fleet2Slot28 = 0xda, + Fleet2Slot29 = 0xdb, + Fleet2Slot30 = 0xdc, + Fleet2Slot31 = 0xdd, + Fleet2Slot32 = 0xde, + Fleet2Slot33 = 0xdf, + Fleet2Slot34 = 0xe0, + TacticalMain = 0xe1, + TacticalOptions = 0xe2, + TacticalCamera = 0xe3, + TacticalBeacon = 0xe4, + TacticalWeapon0 = 0xe5, + TacticalWeapon1 = 0xe6, + TacticalReinforce = 0xe7, + TacticalRetreat = 0xe8, + TacticalStoryArc = 0xe9, + TacticalTechLevel = 0xea, + TacticalAttack = 0xeb, + TacticalAttackMove = 0xec, + TacticalMove = 0xed, + TacticalWaypoint = 0xee, + TacticalStop = 0xef, + TacticalGuard = 0xf0, + SpaceTacticalRadar = 0xf1, + SpaceTacticalCredits = 0xf2, + SpaceTacticalGrabBar = 0xf3, + SpaceTacticalHealthBar = 0xf4, + SpaceTacticalBracketSmall = 0xf5, + SpaceTacticalBracketMedium = 0xf6, + SpaceTacticalBracketLarge = 0xf7, + SpaceTacticalHeroIcon = 0xf8, + SpaceTacticalHeroHealth = 0xf9, + SpaceTacticalHealth = 0xfa, + SpaceTacticalHealthMedium = 0xfb, + SpaceTacticalHealthLarge = 0xfc, + SpaceTacticalShields = 0xfd, + SpaceTacticalShieldsMedium = 0xfe, + SpaceTacticalShieldsLarge = 0xff, + SpaceTacticalPower = 0x100, + SpaceTacticalControlGroup = 0x101, + SpaceTacticalAbilityIcon = 0x102, + SpaceTacticalGarrisonIcon = 0x103, + LandTacticalWeatherIcon = 0x104, + GarrisonSlotIcon = 0x105, + DsFireShell = 0x106, + DsFire = 0x107, + DsCountdownTimer = 0x108, + TacticalSelect00 = 0x109, + TacticalSelect01 = 0x10a, + TacticalSelect02 = 0x10b, + TacticalSelect03 = 0x10c, + TacticalSelect04 = 0x10d, + TacticalSelect05 = 0x10e, + TacticalSelect06 = 0x10f, + TacticalSelect07 = 0x110, + TacticalSelect08 = 0x111, + TacticalSelect09 = 0x112, + TacticalSelect10 = 0x113, + TacticalSelect11 = 0x114, + TacticalSelect12 = 0x115, + TacticalSelect13 = 0x116, + TacticalSelect14 = 0x117, + TacticalSelect15 = 0x118, + TacticalSelect16 = 0x119, + TacticalSelect17 = 0x11a, + TacticalSelect18 = 0x11b, + TacticalSelect19 = 0x11c, + TacticalSelect20 = 0x11d, + TacticalSelect21 = 0x11e, + TacticalSelect22 = 0x11f, + TacticalSelect23 = 0x120, + TacticalSelect24 = 0x121, + TacticalSelect25 = 0x122, + TacticalSelect26 = 0x123, + TacticalSelect27 = 0x124, + TacticalSelect28 = 0x125, + TacticalSelect29 = 0x126, + TacticalSelect30 = 0x127, + TacticalSelect31 = 0x128, + TacticalSelect32 = 0x129, + TacticalSelect33 = 0x12a, + TacticalSelect34 = 0x12b, + TacticalSelect35 = 0x12c, + TacticalSelect36 = 0x12d, + TacticalSelect37 = 0x12e, + TacticalSelect38 = 0x12f, + TacticalSelect39 = 0x130, + TacticalSelect40 = 0x131, + TacticalSelect41 = 0x132, + TacticalSelect42 = 0x133, + TacticalSelect43 = 0x134, + TacticalSelect44 = 0x135, + TacticalSelect45 = 0x136, + TacticalSelect46 = 0x137, + TacticalSelect47 = 0x138, + TacticalSelectHealth00 = 0x139, + TacticalSelectHealth01 = 0x13a, + TacticalSelectHealth02 = 0x13b, + TacticalSelectHealth03 = 0x13c, + TacticalSelectHealth04 = 0x13d, + TacticalSelectHealth05 = 0x13e, + TacticalSelectHealth06 = 0x13f, + TacticalSelectHealth07 = 0x140, + TacticalSelectHealth08 = 0x141, + TacticalSelectHealth09 = 0x142, + TacticalSelectHealth10 = 0x143, + TacticalSelectHealth11 = 0x144, + TacticalSelectHealth12 = 0x145, + TacticalSelectHealth13 = 0x146, + TacticalSelectHealth14 = 0x147, + TacticalSelectHealth15 = 0x148, + TacticalSelectHealth16 = 0x149, + TacticalSelectHealth17 = 0x14a, + TacticalSelectHealth18 = 0x14b, + TacticalSelectHealth19 = 0x14c, + TacticalSelectHealth20 = 0x14d, + TacticalSelectHealth21 = 0x14e, + TacticalSelectHealth22 = 0x14f, + TacticalSelectHealth23 = 0x150, + TacticalSelectHealth24 = 0x151, + TacticalSelectHealth25 = 0x152, + TacticalSelectHealth26 = 0x153, + TacticalSelectHealth27 = 0x154, + TacticalSelectHealth28 = 0x155, + TacticalSelectHealth29 = 0x156, + TacticalSelectHealth30 = 0x157, + TacticalSelectHealth31 = 0x158, + TacticalSelectHealth32 = 0x159, + TacticalSelectHealth33 = 0x15a, + TacticalSelectHealth34 = 0x15b, + TacticalSelectHealth35 = 0x15c, + TacticalSelectHealth36 = 0x15d, + TacticalSelectHealth37 = 0x15e, + TacticalSelectHealth38 = 0x15f, + TacticalSelectHealth39 = 0x160, + TacticalSelectHealth40 = 0x161, + TacticalSelectHealth41 = 0x162, + TacticalSelectHealth42 = 0x163, + TacticalSelectHealth43 = 0x164, + TacticalSelectHealth44 = 0x165, + TacticalSelectHealth45 = 0x166, + TacticalSelectHealth46 = 0x167, + TacticalSelectHealth47 = 0x168, + TacticalSelectShield00 = 0x169, + TacticalSelectShield01 = 0x16a, + TacticalSelectShield02 = 0x16b, + TacticalSelectShield03 = 0x16c, + TacticalSelectShield04 = 0x16d, + TacticalSelectShield05 = 0x16e, + TacticalSelectShield06 = 0x16f, + TacticalSelectShield07 = 0x170, + TacticalSelectShield08 = 0x171, + TacticalSelectShield09 = 0x172, + TacticalSelectShield10 = 0x173, + TacticalSelectShield11 = 0x174, + TacticalSelectShield12 = 0x175, + TacticalSelectShield13 = 0x176, + TacticalSelectShield14 = 0x177, + TacticalSelectShield15 = 0x178, + TacticalSelectShield16 = 0x179, + TacticalSelectShield17 = 0x17a, + TacticalSelectShield18 = 0x17b, + TacticalSelectShield19 = 0x17c, + TacticalSelectShield20 = 0x17d, + TacticalSelectShield21 = 0x17e, + TacticalSelectShield22 = 0x17f, + TacticalSelectShield23 = 0x180, + TacticalSelectShield24 = 0x181, + TacticalSelectShield25 = 0x182, + TacticalSelectShield26 = 0x183, + TacticalSelectShield27 = 0x184, + TacticalSelectShield28 = 0x185, + TacticalSelectShield29 = 0x186, + TacticalSelectShield30 = 0x187, + TacticalSelectShield31 = 0x188, + TacticalSelectShield32 = 0x189, + TacticalSelectShield33 = 0x18a, + TacticalSelectShield34 = 0x18b, + TacticalSelectShield35 = 0x18c, + TacticalSelectShield36 = 0x18d, + TacticalSelectShield37 = 0x18e, + TacticalSelectShield38 = 0x18f, + TacticalSelectShield39 = 0x190, + TacticalSelectShield40 = 0x191, + TacticalSelectShield41 = 0x192, + TacticalSelectShield42 = 0x193, + TacticalSelectShield43 = 0x194, + TacticalSelectShield44 = 0x195, + TacticalSelectShield45 = 0x196, + TacticalSelectShield46 = 0x197, + TacticalSelectShield47 = 0x198, + TacticalBorder00 = 0x199, + TacticalBorder01 = 0x19a, + TacticalBorder02 = 0x19b, + TacticalBorder03 = 0x19c, + TacticalBorder04 = 0x19d, + TacticalBorder05 = 0x19e, + TacticalBorder06 = 0x19f, + TacticalBorder07 = 0x1a0, + TacticalBorder08 = 0x1a1, + TacticalBorder09 = 0x1a2, + TacticalBorder10 = 0x1a3, + TacticalBorder11 = 0x1a4, + TacticalSelectButton00 = 0x1a5, + TacticalSelectButton01 = 0x1a6, + TacticalSelectButton02 = 0x1a7, + TacticalSelectButton03 = 0x1a8, + TacticalSelectButton04 = 0x1a9, + TacticalSelectButton05 = 0x1aa, + TacticalSelectButton06 = 0x1ab, + TacticalSelectButton07 = 0x1ac, + TacticalSelectButton08 = 0x1ad, + TacticalSelectButton09 = 0x1ae, + TacticalSelectButton10 = 0x1af, + TacticalSelectButton11 = 0x1b0, + TacticalSelectButton12 = 0x1b1, + TacticalSelectButton13 = 0x1b2, + TacticalSelectButton14 = 0x1b3, + TacticalSelectButton15 = 0x1b4, + TacticalSelectButton16 = 0x1b5, + TacticalSelectButton17 = 0x1b6, + TacticalSelectButton18 = 0x1b7, + TacticalSelectButton19 = 0x1b8, + TacticalSelectButton20 = 0x1b9, + TacticalSelectButton21 = 0x1ba, + TacticalSelectButton22 = 0x1bb, + TacticalSelectButton23 = 0x1bc, + TacticalSelectButton24 = 0x1bd, + TacticalSelectButton25 = 0x1be, + TacticalSelectButton26 = 0x1bf, + TacticalSelectButton27 = 0x1c0, + TacticalSelectButton28 = 0x1c1, + TacticalSelectButton29 = 0x1c2, + TacticalSelectButton30 = 0x1c3, + TacticalSelectButton31 = 0x1c4, + TacticalSelectButton32 = 0x1c5, + TacticalSelectButton33 = 0x1c6, + TacticalSelectButton34 = 0x1c7, + TacticalSelectButton35 = 0x1c8, + TacticalSelectButton36 = 0x1c9, + TacticalSelectButton37 = 0x1ca, + TacticalSelectButton38 = 0x1cb, + TacticalSelectButton39 = 0x1cc, + TacticalSelectButton40 = 0x1cd, + TacticalSelectButton41 = 0x1ce, + TacticalSelectButton42 = 0x1cf, + TacticalSelectButton43 = 0x1d0, + TacticalSelectButton44 = 0x1d1, + TacticalSelectButton45 = 0x1d2, + TacticalSelectButton46 = 0x1d3, + TacticalSelectButton47 = 0x1d4, + TacticalBuildQueue00 = 0x1d5, + TacticalBuildQueue01 = 0x1d6, + TacticalBuildQueue02 = 0x1d7, + TacticalBuildQueue03 = 0x1d8, + TacticalBuildQueue04 = 0x1d9, + TacticalBuildQueue05 = 0x1da, + TacticalBuildQueue06 = 0x1db, + TacticalBuildQueue07 = 0x1dc, + TacticalBuildQueue08 = 0x1dd, + TacticalBuildQueue09 = 0x1de, + TooltipBack = 0x1df, + TooltipName = 0x1e0, + TooltipPrice = 0x1e1, + TooltipIcon = 0x1e2, + TooltipIconLand = 0x1e3, + TooltipLeftJustified = 0x1e4, + EncyclopediaBack = 0x1e5, + EncyclopediaHeaderText = 0x1e6, + EncyclopediaText = 0x1e7, + EncyclopediaRightText = 0x1e8, + EncyclopediaCenterText = 0x1e9, + EncyclopediaIcon = 0x1ea, + EncyclopediaCostText = 0x1eb, + ZoomedBack = 0x1ec, + ZoomedHeaderText = 0x1ed, + ZoomedText = 0x1ee, + ZoomedRightText = 0x1ef, + ZoomedCenterText = 0x1f0, + ZoomedCostText = 0x1f1, + GPlanetFleet = 0x1f2, + GPlanetName = 0x1f3, + GPlanetValue = 0x1f4, + GPoliticalControl = 0x1f5, + GSpaceLevel = 0x1f6, + GSpaceIcon = 0x1f7, + GSpaceLevelPips = 0x1f8, + GGroundLevel = 0x1f9, + GGroundIcon = 0x1fa, + GGroundLevelPips = 0x1fb, + GConflict = 0x1fc, + GHero = 0x1fd, + GEnemyHero = 0x1fe, + GBuild = 0x1ff, + GSmuggler = 0x200, + GBountyHunter = 0x201, + GPlanetLandForces = 0x202, + GGalacticRadarBlip = 0x203, + GGalacticRadarView = 0x204, + GSmuggled = 0x205, + GSpecialAbility = 0x206, + GHeroIcon = 0x207, + GPlanetRing = 0x208, + GWeather = 0x209, + GPlanetAbility = 0x20a, + GCorruptionText = 0x20b, + GCorruptionIcon = 0x20c, + TutorialText = 0x20d, + TutorialTextBack = 0x20e, + RadarBlip = 0x20f, + TacticalBuildButtonShell = 0x210, + TacticalBuildButton0 = 0x211, + TacticalBuildButton1 = 0x212, + TacticalBuildButton2 = 0x213, + TacticalBuildButton3 = 0x214, + TacticalBuildButton4 = 0x215, + TacticalBuildButton5 = 0x216, + TacticalSellButton = 0x217, + ReinforcementShell = 0x218, + ReinforcementCancel = 0x219, + ReinforcementSlot00 = 0x21a, + ReinforcementSlot01 = 0x21b, + ReinforcementSlot02 = 0x21c, + ReinforcementSlot03 = 0x21d, + ReinforcementSlot04 = 0x21e, + ReinforcementSlot05 = 0x21f, + ReinforcementSlot06 = 0x220, + ReinforcementSlot07 = 0x221, + ReinforcementSlot08 = 0x222, + ReinforcementSlot09 = 0x223, + ReinforcementSlot10 = 0x224, + ReinforcementSlot11 = 0x225, + ReinforcementSlot12 = 0x226, + ReinforcementSlot13 = 0x227, + ReinforcementSlot14 = 0x228, + ReinforcementSlot15 = 0x229, + ReinforcementSlot16 = 0x22a, + ReinforcementSlot17 = 0x22b, + ReinforcementSlot18 = 0x22c, + ReinforcementSlot19 = 0x22d, + ReinforcementCap2 = 0x22e, + ReinforcementCap2Text = 0x22f, + ReinforcementCounter = 0x230, + GarrisonRespawnCounter = 0x231, + SkirmishUpgrade = 0x232, + PendingBattleShell = 0x233, + PendingBattleText = 0x234, + PendingBattleButton = 0x235, + PendingBattleAutoresolve = 0x236, + PendingBattleGraphLeft = 0x237, + PendingBattleGraphRight = 0x238, + TatcicalAutoresolveShell = 0x239, + TacticalAutoresolveButton = 0x23a, + TacticalAutoresolveGraphLeft = 0x23b, + TacticalAutoresolveGraphRight = 0x23c, + ObjectiveBack = 0x23d, + ObjectiveHeaderText = 0x23e, + ObjectiveText = 0x23f, + ObjectiveIcon = 0x240, + GuiDialogTooltip = 0x241, + TargetUnitTypeShell = 0x242, + TargetUnitTypeTitle = 0x243, + TargetUnitTypeCancel = 0x244, + TargetUnitTypeDescription = 0x245, + TargetUnitTypeSlot00 = 0x246, + TargetUnitTypeSlot01 = 0x247, + TargetUnitTypeSlot02 = 0x248, + TargetUnitTypeSlot03 = 0x249, + TargetUnitTypeSlot04 = 0x24a, + TargetUnitTypeSlot05 = 0x24b, + TargetUnitTypeSlot06 = 0x24c, + TargetUnitTypeSlot07 = 0x24d, + TargetUnitTypeSlot08 = 0x24e, + TargetUnitTypeSlot09 = 0x24f, + TargetUnitTypeSlot10 = 0x250, + TargetUnitTypeSlot11 = 0x251, + TargetUnitTypeName00 = 0x252, + TargetUnitTypeName01 = 0x253, + TargetUnitTypeName02 = 0x254, + TargetUnitTypeName03 = 0x255, + TargetUnitTypeName04 = 0x256, + TargetUnitTypeName05 = 0x257, + TargetUnitTypeName06 = 0x258, + TargetUnitTypeName07 = 0x259, + TargetUnitTypeName08 = 0x25a, + TargetUnitTypeName09 = 0x25b, + TargetUnitTypeName10 = 0x25c, + TargetUnitTypeName11 = 0x25d, + TargetUnitTypePrice00 = 0x25e, + TargetUnitTypePrice01 = 0x25f, + TargetUnitTypePrice02 = 0x260, + TargetUnitTypePrice03 = 0x261, + TargetUnitTypePrice04 = 0x262, + TargetUnitTypePrice05 = 0x263, + TargetUnitTypePrice06 = 0x264, + TargetUnitTypePrice07 = 0x265, + TargetUnitTypePrice08 = 0x266, + TargetUnitTypePrice09 = 0x267, + TargetUnitTypePrice10 = 0x268, + TargetUnitTypePrice11 = 0x269, + VcrButtonPlayPause = 0x26a, + VcrButtonFastForward = 0x26b, + VcrButtonFastForwardTactical = 0x26c, + VcrButtonPlayPauseTactical = 0x26d, + AdvisorHintPopupGalactic = 0x26e, + AdvisorHintPopupTactical = 0x26f, + AdvisorHintBack = 0x270, + TextOrganizeFleet00 = 0x271, + TextOrganizeFleet01 = 0x272, + TextOrganizeFleet02 = 0x273, + IconOrganizeFleet00 = 0x274, + IconOrganizeFleet01 = 0x275, + IconOrganizeFleet02 = 0x276, + TextOrganizeLandFleet = 0x277, + CsAbilityButton = 0x278, + CsAbilityText = 0x279, + MovieBoneGalactic = 0x27a, + MovieBoneTactical = 0x27b, + GenericCollision = 0x27c, + GoodHeroShell = 0x27d, + GoodHeroSlot00 = 0x27e, + GoodHeroSlot01 = 0x27f, + GoodHeroSlot02 = 0x280, + GoodHeroSlot03 = 0x281, + GoodHeroSlot04 = 0x282, + GoodHeroSlot05 = 0x283, + GoodHeroSlot06 = 0x284, + GoodHeroSlot07 = 0x285, + GoodHeroSlot08 = 0x286, + GoodHeroSlot09 = 0x287, + GoodHeroSlot10 = 0x288, + GoodHeroHealth00 = 0x289, + GoodHeroHealth01 = 0x28a, + GoodHeroHealth02 = 0x28b, + GoodHeroHealth03 = 0x28c, + GoodHeroHealth04 = 0x28d, + GoodHeroHealth05 = 0x28e, + GoodHeroHealth06 = 0x28f, + GoodHeroHealth07 = 0x290, + GoodHeroHealth08 = 0x291, + GoodHeroHealth09 = 0x292, + GoodHeroHealth10 = 0x293, + EvilHeroShell = 0x294, + EvilHeroSlot00 = 0x295, + EvilHeroSlot01 = 0x296, + EvilHeroSlot02 = 0x297, + EvilHeroSlot03 = 0x298, + EvilHeroSlot04 = 0x299, + EvilHeroSlot05 = 0x29a, + EvilHeroSlot06 = 0x29b, + EvilHeroSlot07 = 0x29c, + EvilHeroSlot08 = 0x29d, + EvilHeroSlot09 = 0x29e, + EvilHeroSlot10 = 0x29f, + EvilHeroHealth00 = 0x2a0, + EvilHeroHealth01 = 0x2a1, + EvilHeroHealth02 = 0x2a2, + EvilHeroHealth03 = 0x2a3, + EvilHeroHealth04 = 0x2a4, + EvilHeroHealth05 = 0x2a5, + EvilHeroHealth06 = 0x2a6, + EvilHeroHealth07 = 0x2a7, + EvilHeroHealth08 = 0x2a8, + EvilHeroHealth09 = 0x2a9, + EvilHeroHealth10 = 0x2aa, + PauseShell = 0x2ab, + PauseText = 0x2ac, + PauseButton = 0x2ad, + StoryCampaignPendingBattleShell = 0x2ae, + StoryCampaignPendingBattleText = 0x2af, + StoryCampaignPendingBattleButton = 0x2b0, + MapActivate = 0x2b1, + MapShell = 0x2b2, + MapOverlayShell = 0x2b3, + MapGroundForcesText = 0x2b4, + MapDragDropText = 0x2b5, + MapUnitCapText = 0x2b6, + MapUnitCapNumber = 0x2b7, + MapBuildingCapText = 0x2b8, + MapBuildingCapNumber = 0x2b9, + MapFactionIcon = 0x2ba, + MapPlanetNameText = 0x2bb, + MapPlanetIncomeNumber = 0x2bc, + MapDayCounterText = 0x2bd, + MapDayCounter = 0x2be, + MapBackButton = 0x2bf, + MapWeatherIcon = 0x2c0, + MapPlanetBonusIcon = 0x2c1, + MapRadarMap = 0x2c2, + MapZoomOut = 0x2c3, + MapEncyclopediaPopup = 0x2c4, + AdvancedMapFilter0 = 0x2c5, + AdvancedMapFilter1 = 0x2c6, + AdvancedMapFilter2 = 0x2c7, + AdvancedMapFilter3 = 0x2c8, + AdvancedMapOptions = 0x2c9, + MapMainMap = 0x2ca, + MapSpaceUnit00 = 0x2cb, + MapSpaceUnit01 = 0x2cc, + MapSpaceUnit02 = 0x2cd, + MapSpaceUnit03 = 0x2ce, + MapSpaceUnit04 = 0x2cf, + MapSpaceUnit05 = 0x2d0, + MapSpaceUnit06 = 0x2d1, + MapSpaceUnit07 = 0x2d2, + MapSpaceUnit08 = 0x2d3, + MapSpaceUnit09 = 0x2d4, + MapSpaceUnit10 = 0x2d5, + MapSpaceUnit11 = 0x2d6, + MapSpaceUnit12 = 0x2d7, + MapSpaceUnit13 = 0x2d8, + MapSpaceUnit14 = 0x2d9, + MapSpaceUnit15 = 0x2da, + MapSpaceUnit16 = 0x2db, + MapSpaceUnit17 = 0x2dc, + MapSpaceUnit18 = 0x2dd, + MapSpaceUnit19 = 0x2de, + MapBuildPad00 = 0x2df, + MapBuildPad01 = 0x2e0, + MapBuildPad02 = 0x2e1, + MapBuildPad03 = 0x2e2, + MapBuildPad04 = 0x2e3, + MapBuildPad05 = 0x2e4, + MapBuildPad06 = 0x2e5, + MapBuildPad07 = 0x2e6, + MapBuildPad08 = 0x2e7, + MapBuildPad09 = 0x2e8, + MapBuildPad10 = 0x2e9, + MapBuildPad11 = 0x2ea, + MapBuildPad12 = 0x2eb, + MapBuildPad13 = 0x2ec, + MapBuildPad14 = 0x2ed, + MapBuildPad15 = 0x2ee, + MapBuildPad16 = 0x2ef, + MapBuildPad17 = 0x2f0, + MapBuildPad18 = 0x2f1, + MapBuildPad19 = 0x2f2, + MapBuildPad20 = 0x2f3, + MapBuildPad21 = 0x2f4, + MapBuildPad22 = 0x2f5, + MapBuildPad23 = 0x2f6, + MapBuildPad24 = 0x2f7, + MapBuildPad25 = 0x2f8, + MapBuildPad26 = 0x2f9, + MapBuildPad27 = 0x2fa, + MapBuildPad28 = 0x2fb, + MapBuildPad29 = 0x2fc, + MapBuildPad30 = 0x2fd, + MapBuildPad31 = 0x2fe, + MapBuildPad32 = 0x2ff, + MapReinforcePad00 = 0x300, + MapReinforcePad01 = 0x301, + MapReinforcePad02 = 0x302, + MapReinforcePad03 = 0x303, + MapReinforcePad04 = 0x304, + MapReinforcePad05 = 0x305, + MapReinforcePad06 = 0x306, + MapBunkerAndBuildPad00 = 0x307, + MapBunkerAndBuildPad01 = 0x308, + MapBunkerAndBuildPad02 = 0x309, + MapBunkerAndBuildPad03 = 0x30a, + MapBunkerAndBuildPad04 = 0x30b, + MapBunkerAndBuildPad05 = 0x30c, + MapBunkerAndBuildPad06 = 0x30d, + MapBunkerAndBuildPad07 = 0x30e, + MapBunkerAndBuildPad08 = 0x30f, + MapBunkerAndBuildPad09 = 0x310, + MapBunkerAndBuildPad10 = 0x311, + MapBunkerAndBuildPad11 = 0x312, + MapBunkerAndBuildPad12 = 0x313, + MapBunkerAndBuildPad13 = 0x314, + MapBunkerAndBuildPad14 = 0x315, + MapBunkerAndBuildPad15 = 0x316, + MapBunkerAndBuildPad16 = 0x317, + MapBunkerAndBuildPad17 = 0x318, + MapBunkerAndBuildPad18 = 0x319, + MapBunkerAndBuildPad19 = 0x31a, + MapBunkerAndBuildPad20 = 0x31b, + MapBunkerAndBuildPad21 = 0x31c, + MapBunkerAndBuildPad22 = 0x31d, + MapBunkerAndBuildPad23 = 0x31e, + MapBunkerAndBuildPad24 = 0x31f, + MapBunkerAndBuildPad25 = 0x320, + MapBunkerAndBuildPad26 = 0x321, + MapBunkerAndBuildPad27 = 0x322, + MapBunkerAndBuildPad28 = 0x323, + MapBunkerAndBuildPad29 = 0x324, + MapBunkerAndBuildPad30 = 0x325, + MapBunkerAndBuildPad31 = 0x326, + MapBunkerAndBuildPad32 = 0x327, + MapBunkerAndBuildPad33 = 0x328, + MapBunkerAndBuildPad34 = 0x329, + MapBunkerAndBuildPad35 = 0x32a, + MapBunkerAndBuildPad36 = 0x32b, + MapBunkerAndBuildPad37 = 0x32c, + MapBunkerAndBuildPad38 = 0x32d, + MapBunkerAndBuildPad39 = 0x32e, + MapUnitPad00 = 0x32f, + MapUnitPad01 = 0x330, + MapUnitPad02 = 0x331, + MapUnitPad03 = 0x332, + MapUnitPad04 = 0x333, + MapUnitPad05 = 0x334, + MapUnitPad06 = 0x335, + MapUnitPad07 = 0x336, + MapUnitPad08 = 0x337, + MapUnitPad09 = 0x338, + BribeDisplay = 0x339, + BlackMarketShell = 0x33a, + BlackMarketTitle = 0x33b, + BlackMarketPlanet = 0x33c, + BlackMarketTechLevelHeader = 0x33d, + BlackMarketSlotHeader = 0x33e, + BlackMarketCancel = 0x33f, + BlackMarketEncyclopedia = 0x340, + BlackMarketTechLevel00 = 0x341, + BlackMarketTechLevel01 = 0x342, + BlackMarketTechLevel02 = 0x343, + BlackMarketTechLevel03 = 0x344, + BlackMarketTechLevel04 = 0x345, + BlackMarketSlot00 = 0x346, + BlackMarketSlot01 = 0x347, + BlackMarketSlot02 = 0x348, + BlackMarketSlot03 = 0x349, + BlackMarketSlot04 = 0x34a, + BlackMarketSlotDescription00 = 0x34b, + BlackMarketSlotDescription01 = 0x34c, + BlackMarketSlotDescription02 = 0x34d, + BlackMarketSlotDescription03 = 0x34e, + BlackMarketSlotDescription04 = 0x34f, + BlackMarketSlotPrice00 = 0x350, + BlackMarketSlotPrice01 = 0x351, + BlackMarketSlotPrice02 = 0x352, + BlackMarketSlotPrice03 = 0x353, + BlackMarketSlotPrice04 = 0x354, + SabotageShell = 0x355, + SabotageTitle = 0x356, + SabotageTitle2 = 0x357, + SabotageTechLevel = 0x358, + SabotageCancel = 0x359, + SabotageDescription = 0x35a, + SabotageSlot00 = 0x35b, + SabotageSlot01 = 0x35c, + SabotageSlot02 = 0x35d, + SabotageSlot03 = 0x35e, + SabotageSlot04 = 0x35f, + SabotageSlot05 = 0x360, + SabotageSlot06 = 0x361, + SabotageSlot07 = 0x362, + SabotageSlot08 = 0x363, + SabotageSlot09 = 0x364, + SabotagePrice00 = 0x365, + SabotagePrice01 = 0x366, + SabotagePrice02 = 0x367, + SabotagePrice03 = 0x368, + SabotagePrice04 = 0x369, + SabotagePrice05 = 0x36a, + SabotagePrice06 = 0x36b, + SabotagePrice07 = 0x36c, + SabotagePrice08 = 0x36d, + SabotagePrice09 = 0x36e, + SabotageName00 = 0x36f, + SabotageName01 = 0x370, + SabotageName02 = 0x371, + SabotageName03 = 0x372, + SabotageName04 = 0x373, + SabotageName05 = 0x374, + SabotageName06 = 0x375, + SabotageName07 = 0x376, + SabotageName08 = 0x377, + SabotageName09 = 0x378, + PlanetaryBombardment = 0x379, + PlanetaryBombardmentRecharge = 0x37a, + SuperLaser = 0x37b, + SuperLaserRecharge = 0x37c, + GenericFlytext = 0x37d, + BribedIcon = 0x37e, + SurfaceModIcon = 0x37f, + RemoteBombIcon = 0x380, + CorruptionShell = 0x381, + CorruptionTitle = 0x382, + CorruptionPlanetName = 0x383, + CorruptionEncyclopedia = 0x384, + CorruptionClose = 0x385, + CorruptionPlanetModel = 0x386, + CorruptionInfo00 = 0x387, + CorruptionInfo01 = 0x388, + CorruptionChoiceIcon0 = 0x389, + CorruptionChoiceIcon1 = 0x38a, + CorruptionChoiceIcon2 = 0x38b, + CorruptionChoiceCost0 = 0x38c, + CorruptionChoiceCost1 = 0x38d, + CorruptionChoiceCost2 = 0x38e, + CorruptionChoiceTitle0 = 0x38f, + CorruptionChoiceTitle1 = 0x390, + CorruptionChoiceTitle2 = 0x391, + CorruptionChoiceBenefitText00 = 0x392, + CorruptionChoiceBenefitText01 = 0x393, + CorruptionChoiceBenefitText02 = 0x394, + CorruptionChoiceBenefitText03 = 0x395, + CorruptionChoiceBenefitText10 = 0x396, + CorruptionChoiceBenefitText11 = 0x397, + CorruptionChoiceBenefitText12 = 0x398, + CorruptionChoiceBenefitText13 = 0x399, + CorruptionChoiceBenefitText20 = 0x39a, + CorruptionChoiceBenefitText21 = 0x39b, + CorruptionChoiceBenefitText22 = 0x39c, + CorruptionChoiceBenefitText23 = 0x39d, + CorruptionChoiceRequirement0 = 0x39e, + CorruptionChoiceRequirement1 = 0x39f, + CorruptionChoiceRequirement2 = 0x3a0, + CorruptionChoiceRequirementIcon0000 = 0x3a1, + CorruptionChoiceRequirementIcon0001 = 0x3a2, + CorruptionChoiceRequirementIcon0002 = 0x3a3, + CorruptionChoiceRequirementIcon0003 = 0x3a4, + CorruptionChoiceRequirementIcon0100 = 0x3a5, + CorruptionChoiceRequirementIcon0101 = 0x3a6, + CorruptionChoiceRequirementIcon0102 = 0x3a7, + CorruptionChoiceRequirementIcon0103 = 0x3a8, + CorruptionChoiceRequirementIcon1000 = 0x3a9, + CorruptionChoiceRequirementIcon1001 = 0x3aa, + CorruptionChoiceRequirementIcon1002 = 0x3ab, + CorruptionChoiceRequirementIcon1003 = 0x3ac, + CorruptionChoiceRequirementIcon1100 = 0x3ad, + CorruptionChoiceRequirementIcon1101 = 0x3ae, + CorruptionChoiceRequirementIcon1102 = 0x3af, + CorruptionChoiceRequirementIcon1103 = 0x3b0, + CorruptionChoiceRequirementIcon2000 = 0x3b1, + CorruptionChoiceRequirementIcon2001 = 0x3b2, + CorruptionChoiceRequirementIcon2002 = 0x3b3, + CorruptionChoiceRequirementIcon2003 = 0x3b4, + CorruptionChoiceRequirementIcon2100 = 0x3b5, + CorruptionChoiceRequirementIcon2101 = 0x3b6, + CorruptionChoiceRequirementIcon2102 = 0x3b7, + CorruptionChoiceRequirementIcon2103 = 0x3b8, + HackSuperWeaponShell = 0x3b9, + HackSuperWeaponTitle = 0x3ba, + HackSuperWeaponText00 = 0x3bb, + HackSuperWeaponText01 = 0x3bc, + HackSuperWeaponText02 = 0x3bd, + HackSuperWeaponText03 = 0x3be, + HackSuperWeaponText04 = 0x3bf, + HackSuperWeaponText05 = 0x3c0, + HackSuperWeaponText06 = 0x3c1, + HackSuperWeaponCost = 0x3c2, + HackSuperWeaponAccept = 0x3c3, + HackSuperWeaponCancel = 0x3c4, + Count = 0x3c5, + None = 0x3c6 +}; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs index 793eb44..9a4c6ee 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentType.cs @@ -2,14 +2,16 @@ public enum CommandBarComponentType { - Shell, - Icon, - Button, - Text, - TextButton, - Model, - Bar, - Select, - Count, - None, + // Used for XML lookup + Shell = 0, + Icon = 1, + Button = 2, + Text = 3, + TextButton = 4, + Model = 5, + Bar = 6, + // Used internally by the engine only + Select = 7, + Count = 8, + None = 9, } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index b7dbf4d..55ed6da 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -1,33 +1,60 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.Commons.Collections; using PG.Commons.Hashing; +using PG.StarWarsGame.Engine.CommandBar.Components; using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Xml.Parsers; +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; namespace PG.StarWarsGame.Engine.CommandBar; -public interface ICommandBarGameManager : IGameManager -{ -} - internal class CommandBarGameManager( GameRepository repository, - DatabaseErrorReporterWrapper errorReporter, + GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) - : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager + : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager { + public const string MegaTextureBaseName = "MT_COMMANDBAR"; + + private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); + private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); + private readonly Dictionary _groups = new(); + + private bool _megaTextureExists; + + public ICollection Components => Entries; + + public IReadOnlyDictionary Groups => _groups; + + public IMtdFile? MegaTextureFile + { + get + { + ThrowIfNotInitialized(); + return field; + } + private set + { + ThrowIfAlreadyInitialized(); + field = value; + } + } + protected override async Task InitializeCoreAsync(CancellationToken token) { Logger?.LogInformation("Creating command bar components..."); - var contentParser = ServiceProvider.GetRequiredService(); + var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); contentParser.XmlParseError += OnParseError; var parsedCommandBarComponents = new ValueListDictionary(); @@ -36,7 +63,6 @@ protected override async Task InitializeCoreAsync(CancellationToken token) { await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\CommandBarComponentFiles.XML", - ErrorReporter, GameRepository, ".\\DATA\\XML", parsedCommandBarComponents, @@ -47,6 +73,109 @@ await Task.Run(() => contentParser.ParseEntriesFromFileListXml( { contentParser.XmlParseError -= OnParseError; } + + // Create Scene + // Create Camera + // Resize(true) + + + foreach (var parsedCommandBarComponent in parsedCommandBarComponents.Values) + { + var component = CommandBarBaseComponent.Create(parsedCommandBarComponent, ErrorReporter); + if (component is not null) + { + // If I understand this correctly, the name is not uppercased, which means name is case-sensitive! + // TODO: Debug to confirm! + var crc = _hashingService.GetCrc32(component.Name, PGConstants.DefaultPGEncoding); + NamedEntries.Add(crc, component); + } + } + + SetComponentGroup(Components); + SetMegaTexture(); + SetDefaultFont(); + + LinkComponentsToShell(); + LinkComponentsWithActions(); + } + + private void LinkComponentsWithActions() + { + var ids = (CommandBarComponentId[])Enum.GetValues(typeof(CommandBarComponentId)); + foreach (var id in ids) + { + if (!SupportedCommandBarComponentData.SupportedComponents.TryGetValue(id, out var name)) + continue; + var crc = _hashingService.GetCrc32(name, PGConstants.DefaultPGEncoding); + if (NamedEntries.TryGetFirstValue(crc, out var component)) + component.Id = id; + } + } + + private void LinkComponentsToShell() + { + // TODO: DEBUG!!! + + if (!Groups.TryGetValue("Shells", out var shellGroup)) + return; + + foreach (var shellComponent in shellGroup.Components) + { + if (shellComponent.Type == CommandBarComponentType.None) + continue; + + foreach (var other in shellGroup.Components) + { + + } + } + } + + 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; + + /* + 140ab82bd if (!BaseComponentClass::DefaultFont) + 140ab82bd { + 140ab82c6 int32_t point_size = j_GameConstantsClass::Get_Command_Bar_Default_Font_Size(&TheGameConstants); + 140ab830c BaseComponentClass::DefaultFont = j_FontManagerClass::Create_Font(&FontManager, j_GameConstantsClass::Get_Command_Bar_Default_Font_Name(&TheGameConstants), point_size, 1, 0, 0, 1f); + 140ab830c + 140ab831b if (!BaseComponentClass::DefaultFont) + 140ab8331 j_Assert_Handler("DefaultFont != NULL", "C:\BuildSystem\AB_SW_STEAM_FOC_BUILDSERV04_BUILD\StarWars_Steam\FOC\Code\RTS\CommandBar\CommandBarComponent.cpp", 0x1325); + 140ab82bd } + */ + } + + 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 + // TODO: Confirm! + var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{MegaTextureBaseName}.mtd"); + using var megaTexture = GameRepository.TryOpenFile(mtdPath); + MegaTextureFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); + _megaTextureExists = GameRepository.TextureRepository.FileExists($"{MegaTextureBaseName}.tga"); + } + + private void SetComponentGroup(IEnumerable components) + { + // TODO: Check whether groups are really case-sensitive! + 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 OnParseError(object sender, XmlContainerParserErrorEventArgs e) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/ButtonMode.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/ButtonMode.cs new file mode 100644 index 0000000..d18326d --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/ButtonMode.cs @@ -0,0 +1,14 @@ +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public enum ButtonMode +{ + Hidden = 0x0, + Normal = 0x1, + Mouseover = 0x2, + Selected = 0x3, + Disabled = 0x4, + Drag = 0x5, + Building = 0x6, + Ghost = 0x7, + None = 0x8 +}; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBarComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBarComponent.cs new file mode 100644 index 0000000..b6685cc --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBarComponent.cs @@ -0,0 +1,8 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarBarComponent(CommandBarComponentData xmlData) : CommandBarBaseComponent(xmlData) +{ + public override CommandBarComponentType Type => CommandBarComponentType.Bar; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs new file mode 100644 index 0000000..2b5cabb --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs @@ -0,0 +1,74 @@ +using System; +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.Rendering; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) +{ + internal readonly CommandBarComponentData XmlData = xmlData ?? throw new ArgumentNullException(nameof(xmlData)); + + public string Name => XmlData.Name; + public RgbaColor Color { get; } = xmlData.Color.HasValue ? new RgbaColor(xmlData.Color.Value) : new RgbaColor(255, 255, 255, 255); + public int Bone { get; } = unchecked((int)0xFFFFFFFF); + public int BaseLayer { get; } = xmlData.BaseLayer; + public bool Hidden { get; internal set; } = xmlData.Hidden; + public bool Disabled { get; } = xmlData.Disabled; + public abstract CommandBarComponentType Type { get; } + public CommandBarComponentId Id { get; internal set; } + public CommandBarComponentGroup Group { get; internal set; } + + public static CommandBarBaseComponent? Create(CommandBarComponentData xmlData, IGameErrorReporter errorReporter) + { + var type = GetTypeFromString(xmlData.Type.AsSpan()); + switch (type) + { + case CommandBarComponentType.Shell: + return new CommandBarShellComponent(xmlData); + case CommandBarComponentType.Icon: + return new CommandBarIconComponent(xmlData); + case CommandBarComponentType.Button: + return new CommandBarButtonComponentClass(xmlData); + case CommandBarComponentType.Text: + return new CommandBarTextComponent(xmlData); + case CommandBarComponentType.TextButton: + return new CommandBarTextButtonComponent(xmlData); + case CommandBarComponentType.Model: + return new CommandBarModelComponent(xmlData); + case CommandBarComponentType.Bar: + return new CommandBarBarComponent(xmlData); + case CommandBarComponentType.Select: + // I don't know how this code could ever be reached, because GetTypeFromString, + // if I understand it correctly, does not allow SELECT (and COUNT). The code to create a + // SELECT Component exists nether the less. + return new CommandBarSelectComponent(xmlData); + } + + errorReporter.Assert(EngineAssert.CreateCapture( + $"Invalid type value '{xmlData.Type}' for CommandbarComponent '{xmlData.Name}')")); + + return null; + } + + private static CommandBarComponentType GetTypeFromString(ReadOnlySpan xmlValue) + { + if (xmlValue.Equals("SHELL".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.Shell; + if (xmlValue.Equals("ICON".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.Icon; + if (xmlValue.Equals("BUTTON".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.Button; + if (xmlValue.Equals("TEXT".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.Text; + if (xmlValue.Equals("TEXTBUTTON".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.TextButton; + if (xmlValue.Equals("MODEL".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.Model; + if (xmlValue.Equals("BAR".AsSpan(), StringComparison.OrdinalIgnoreCase)) + return CommandBarComponentType.Bar; + + + return CommandBarComponentType.None; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarButtonComponentClass.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarButtonComponentClass.cs new file mode 100644 index 0000000..9876c31 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarButtonComponentClass.cs @@ -0,0 +1,10 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarButtonComponentClass(CommandBarComponentData xmlData) : CommandBarIconComponent(xmlData) +{ + public override CommandBarComponentType Type => CommandBarComponentType.Button; + + public ButtonMode Mode => ButtonMode.Normal; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarComponentTextData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarComponentTextData.cs new file mode 100644 index 0000000..d57e170 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarComponentTextData.cs @@ -0,0 +1,23 @@ +using PG.StarWarsGame.Engine.Rendering; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public sealed class CommandBarComponentTextData +{ + public CommandBarBaseComponent Parent { get; } + + public PrimRenderMode Mode { get; init; } + + public float Scale { get; init; } + + public int TempAlpha { get; init; } + + public RgbaColor Color { get; init; } + + public bool AutoPixelAlign { get; init; } = true; + + internal CommandBarComponentTextData(CommandBarBaseComponent parent) + { + Parent = parent; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarIconComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarIconComponent.cs new file mode 100644 index 0000000..792f58e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarIconComponent.cs @@ -0,0 +1,8 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarIconComponent(CommandBarComponentData xmlData) : CommandBarBaseComponent(xmlData) +{ + public override CommandBarComponentType Type => CommandBarComponentType.Icon; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarModelComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarModelComponent.cs new file mode 100644 index 0000000..a094747 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarModelComponent.cs @@ -0,0 +1,8 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarModelComponent(CommandBarComponentData xmlData) : CommandBarBaseComponent(xmlData) +{ + public override CommandBarComponentType Type => CommandBarComponentType.Model; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarSelectComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarSelectComponent.cs new file mode 100644 index 0000000..000995c --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarSelectComponent.cs @@ -0,0 +1,8 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarSelectComponent(CommandBarComponentData xmlData) : CommandBarBaseComponent(xmlData) +{ + public override CommandBarComponentType Type => CommandBarComponentType.Select; +} \ 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 new file mode 100644 index 0000000..f02ddbe --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs @@ -0,0 +1,10 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarShellComponent(CommandBarComponentData xmlData) : CommandBarBaseComponent(xmlData) +{ + public override CommandBarComponentType Type => CommandBarComponentType.Shell; + + public string? ModelName { get; } = xmlData.ModelName; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextButtonComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextButtonComponent.cs new file mode 100644 index 0000000..9213f03 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextButtonComponent.cs @@ -0,0 +1,32 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.Rendering; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarTextButtonComponent : CommandBarButtonComponentClass +{ + public CommandBarComponentTextData TextData { get; } + public CommandBarComponentTextData TextData2 { get; } + public CommandBarComponentTextData FlashData { get; } + public bool ShowDraggedText { get; } = true; + public override CommandBarComponentType Type => CommandBarComponentType.TextButton; + + public CommandBarTextButtonComponent(CommandBarComponentData xmlData) : base(xmlData) + { + TextData = new CommandBarComponentTextData(this) + { + Mode = PrimRenderMode.PrimAlpha, + Scale = xmlData.Scale, + TempAlpha = -1, + Color = xmlData.TextColor.HasValue ? new RgbaColor(xmlData.TextColor.Value) : new RgbaColor(255, 255, 255, 255) + }; + TextData2 = new CommandBarComponentTextData(this) + { + Mode = PrimRenderMode.PrimAlpha, + Scale = xmlData.Scale, + TempAlpha = -1, + Color = xmlData.TextColor2.HasValue ? new RgbaColor(xmlData.TextColor2.Value) : new RgbaColor(255, 255, 255, 255) + }; + FlashData = new CommandBarComponentTextData(this); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextComponent.cs new file mode 100644 index 0000000..d7471a5 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarTextComponent.cs @@ -0,0 +1,28 @@ +using PG.StarWarsGame.Engine.CommandBar.Xml; +using PG.StarWarsGame.Engine.Rendering; + +namespace PG.StarWarsGame.Engine.CommandBar.Components; + +public class CommandBarTextComponent : CommandBarBaseComponent +{ + public override CommandBarComponentType Type => CommandBarComponentType.Text; + + public CommandBarComponentTextData TextData { get; } + + public CommandBarComponentTextData FlashData { get; } + + public bool AutoPixelAlign => true; + + + public CommandBarTextComponent(CommandBarComponentData xmlData) : base(xmlData) + { + TextData = new CommandBarComponentTextData(this) + { + Scale = XmlData.Scale, + Color = Color, + TempAlpha = -1, + Mode = PrimRenderMode.PrimAlpha, + }; + FlashData = new CommandBarComponentTextData(this); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs new file mode 100644 index 0000000..2d788c9 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using PG.StarWarsGame.Engine.CommandBar.Components; +using PG.StarWarsGame.Engine.Database; + +namespace PG.StarWarsGame.Engine.CommandBar; + +public interface ICommandBarGameManager : IGameManager +{ + ICollection Components { get; } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs new file mode 100644 index 0000000..1e7e2ee --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs @@ -0,0 +1,976 @@ +using System.Collections.Generic; + +namespace PG.StarWarsGame.Engine.CommandBar; + +internal static class SupportedCommandBarComponentData +{ + public static IReadOnlyDictionary SupportedComponents = + new Dictionary + { + { CommandBarComponentId.MainShell, "i_main_commandbar" }, + { CommandBarComponentId.ProductionOptions, "b_option_g" }, + { CommandBarComponentId.GalacticCamera, "b_camera_g" }, + { CommandBarComponentId.PlanetName, "Text_Planet" }, + { CommandBarComponentId.PlanetValue, "Text_Planetary_Income" }, + { CommandBarComponentId.PlanetAffiliation, "Text_Allegiance_A" }, + { CommandBarComponentId.PlayerCredits, "Text_Credits" }, + { CommandBarComponentId.ReinforcementCap, "Text_Planetary_Pop" }, + { CommandBarComponentId.PlanetNameTactical, "Text_Planet_tactical" }, + { CommandBarComponentId.PlanetInfo, "planet_info" }, + { CommandBarComponentId.Production0, "b_Create_00" }, + { CommandBarComponentId.Production1, "b_Create_01" }, + { CommandBarComponentId.Production2, "b_Create_02" }, + { CommandBarComponentId.Production3, "b_Create_03" }, + { CommandBarComponentId.Production4, "b_Create_04" }, + { CommandBarComponentId.Production5, "b_Create_05" }, + { CommandBarComponentId.Production6, "b_Create_06" }, + { CommandBarComponentId.Production7, "b_Create_07" }, + { CommandBarComponentId.Production8, "b_Create_08" }, + { CommandBarComponentId.Production9, "b_Create_09" }, + { CommandBarComponentId.Production10, "b_Create_10" }, + { CommandBarComponentId.Production11, "b_Create_11" }, + { CommandBarComponentId.Production12, "b_Create_12" }, + { CommandBarComponentId.Production13, "b_Create_13" }, + { CommandBarComponentId.Production14, "b_Create_14" }, + { CommandBarComponentId.Production15, "b_Create_15" }, + { CommandBarComponentId.Production16, "b_Create_16" }, + { CommandBarComponentId.Production17, "b_Create_17" }, + { CommandBarComponentId.Production18, "b_Create_18" }, + { CommandBarComponentId.Production19, "b_Create_19" }, + { CommandBarComponentId.Production20, "b_Create_20" }, + { CommandBarComponentId.Production21, "b_Create_21" }, + { CommandBarComponentId.Production22, "b_Create_22" }, + { CommandBarComponentId.Production23, "b_Create_23" }, + { CommandBarComponentId.Production24, "b_Create_24" }, + { CommandBarComponentId.Production25, "b_Create_25" }, + { CommandBarComponentId.Production26, "b_Create_26" }, + { CommandBarComponentId.Production27, "b_Create_27" }, + { CommandBarComponentId.Production28, "b_Create_28" }, + { CommandBarComponentId.Production29, "b_Create_29" }, + { CommandBarComponentId.Production30, "b_Create_30" }, + { CommandBarComponentId.Production31, "b_Create_31" }, + { CommandBarComponentId.Production32, "b_Create_32" }, + { CommandBarComponentId.Production33, "b_Create_33" }, + { CommandBarComponentId.Production34, "b_Create_34" }, + { CommandBarComponentId.Production35, "b_Create_35" }, + { CommandBarComponentId.Production36, "b_Create_36" }, + { CommandBarComponentId.Production37, "b_Create_37" }, + { CommandBarComponentId.Production38, "b_Create_38" }, + { CommandBarComponentId.Production39, "b_Create_39" }, + { CommandBarComponentId.Production40, "b_Create_40" }, + { CommandBarComponentId.Production41, "b_Create_41" }, + { CommandBarComponentId.Production42, "b_Create_42" }, + { CommandBarComponentId.Production43, "b_Create_43" }, + { CommandBarComponentId.Production44, "b_Create_44" }, + { CommandBarComponentId.Production45, "b_Create_45" }, + { CommandBarComponentId.Production46, "b_Create_46" }, + { CommandBarComponentId.Production47, "b_Create_47" }, + { CommandBarComponentId.DroidHelp, "b_droid_help" }, + { CommandBarComponentId.DroidHelpTactical, "b_droid_help_tactical" }, + { CommandBarComponentId.CurrentDay, "Text_Day_Counter" }, + { CommandBarComponentId.DayCredits, "Text_Day_Credits" }, + { CommandBarComponentId.PopulationCap, "Text_Pop_Cap" }, + { CommandBarComponentId.Filter0, "b_filters0" }, + { CommandBarComponentId.Filter1, "b_filters1" }, + { CommandBarComponentId.Filter2, "b_filters2" }, + { CommandBarComponentId.Filter3, "b_filters3" }, + { CommandBarComponentId.StoryArcButton, "b_story_arc_g" }, + { CommandBarComponentId.PlanetSummaryButton, "b_quick_ref" }, + { CommandBarComponentId.SpaceTab, "b_space_tab" }, + { CommandBarComponentId.LandTab, "b_planet_tab" }, + { CommandBarComponentId.Dial, "b_dial" }, + { CommandBarComponentId.ScrollRight, "b_scroll_right" }, + { CommandBarComponentId.ScrollLeft, "b_scroll_left" }, + { CommandBarComponentId.ZoomView, "b_zoom" }, + { CommandBarComponentId.PrevPlanet, "b_planet_left" }, + { CommandBarComponentId.NextPlanet, "b_planet_right" }, + { CommandBarComponentId.RadarGalactic, "radar_galactic" }, + { CommandBarComponentId.TechLevel, "Text_Tech_Level" }, + { CommandBarComponentId.BalancePip, "balance_pip" }, + { CommandBarComponentId.BuildQueue00, "queue00" }, + { CommandBarComponentId.BuildQueue01, "queue01" }, + { CommandBarComponentId.BuildQueue02, "queue02" }, + { CommandBarComponentId.BuildQueue03, "queue03" }, + { CommandBarComponentId.BuildQueue04, "queue04" }, + { CommandBarComponentId.BuildQueue05, "queue05" }, + { CommandBarComponentId.BuildQueue06, "queue06" }, + { CommandBarComponentId.BuildQueue07, "queue07" }, + { CommandBarComponentId.BuildQueue08, "queue08" }, + { CommandBarComponentId.BuildQueue09, "queue09" }, + { CommandBarComponentId.OrganizationShell, "i_main_organize" }, + { CommandBarComponentId.OrganizationCollision, "land_art_ALT0" }, + { CommandBarComponentId.SmugglerBox, "b_underground" }, + { CommandBarComponentId.SpaceStationUpgrade01, "space_Pad_1" }, + { CommandBarComponentId.SpaceStationUpgrade02, "space_Pad_02" }, + { CommandBarComponentId.HeroAbilitySlot, "Slot_00" }, + { CommandBarComponentId.PlanetOrganize0, "Pad_00" }, + { CommandBarComponentId.PlanetOrganize1, "Pad_01" }, + { CommandBarComponentId.PlanetOrganize2, "Pad_02" }, + { CommandBarComponentId.PlanetOrganize3, "Pad_03" }, + { CommandBarComponentId.PlanetOrganize4, "Pad_04" }, + { CommandBarComponentId.PlanetOrganize5, "Pad_05" }, + { CommandBarComponentId.PlanetOrganize6, "Pad_06" }, + { CommandBarComponentId.PlanetOrganize7, "Pad_07" }, + { CommandBarComponentId.PlanetOrganize8, "Pad_08" }, + { CommandBarComponentId.PlanetOrganize9, "Pad_09" }, + { CommandBarComponentId.SpecialStructureLand0, "Planet_structure_00" }, + { CommandBarComponentId.SpecialStructureLand1, "Planet_structure_01" }, + { CommandBarComponentId.SpecialStructureLand2, "Planet_structure_02" }, + { CommandBarComponentId.SpecialStructureLand3, "Planet_structure_03" }, + { CommandBarComponentId.SpecialStructureLand4, "Planet_structure_04" }, + { CommandBarComponentId.SpecialStructureLand5, "Planet_structure_05" }, + { CommandBarComponentId.SpecialStructureLand6, "Planet_structure_06" }, + { CommandBarComponentId.SpecialStructureLand7, "Planet_structure_07" }, + { CommandBarComponentId.SpecialStructureLand8, "Planet_structure_08" }, + { CommandBarComponentId.SpecialStructureLandSell, "g_ground_sell" }, + { CommandBarComponentId.BigFleet0, "q1_fleet" }, + { CommandBarComponentId.BigFleet1, "q2_fleet" }, + { CommandBarComponentId.BigFleet2, "q3_fleet" }, + { CommandBarComponentId.BigFleet3, "q4_fleet" }, + { CommandBarComponentId.Fleet0Slot0, "q1_0000" }, + { CommandBarComponentId.Fleet0Slot1, "q1_0001" }, + { CommandBarComponentId.Fleet0Slot2, "q1_0002" }, + { CommandBarComponentId.Fleet0Slot3, "q1_0003" }, + { CommandBarComponentId.Fleet0Slot4, "q1_0004" }, + { CommandBarComponentId.Fleet0Slot5, "q1_0005" }, + { CommandBarComponentId.Fleet0Slot6, "q1_0006" }, + { CommandBarComponentId.Fleet0Slot7, "q1_0100" }, + { CommandBarComponentId.Fleet0Slot8, "q1_0101" }, + { CommandBarComponentId.Fleet0Slot9, "q1_0102" }, + { CommandBarComponentId.Fleet0Slot10, "q1_0103" }, + { CommandBarComponentId.Fleet0Slot11, "q1_0104" }, + { CommandBarComponentId.Fleet0Slot12, "q1_0105" }, + { CommandBarComponentId.Fleet0Slot13, "q1_0106" }, + { CommandBarComponentId.Fleet0Slot14, "q1_0200" }, + { CommandBarComponentId.Fleet0Slot15, "q1_0201" }, + { CommandBarComponentId.Fleet0Slot16, "q1_0202" }, + { CommandBarComponentId.Fleet0Slot17, "q1_0203" }, + { CommandBarComponentId.Fleet0Slot18, "q1_0204" }, + { CommandBarComponentId.Fleet0Slot19, "q1_0205" }, + { CommandBarComponentId.Fleet0Slot20, "q1_0206" }, + { CommandBarComponentId.Fleet0Slot21, "q1_0300" }, + { CommandBarComponentId.Fleet0Slot22, "q1_0301" }, + { CommandBarComponentId.Fleet0Slot23, "q1_0302" }, + { CommandBarComponentId.Fleet0Slot24, "q1_0303" }, + { CommandBarComponentId.Fleet0Slot25, "q1_0304" }, + { CommandBarComponentId.Fleet0Slot26, "q1_0305" }, + { CommandBarComponentId.Fleet0Slot27, "q1_0306" }, + { CommandBarComponentId.Fleet0Slot28, "q1_0400" }, + { CommandBarComponentId.Fleet0Slot29, "q1_0401" }, + { CommandBarComponentId.Fleet0Slot30, "q1_0402" }, + { CommandBarComponentId.Fleet0Slot31, "q1_0403" }, + { CommandBarComponentId.Fleet0Slot32, "q1_0404" }, + { CommandBarComponentId.Fleet0Slot33, "q1_0405" }, + { CommandBarComponentId.Fleet0Slot34, "q1_0406" }, + { CommandBarComponentId.Fleet1Slot0, "q2_0000" }, + { CommandBarComponentId.Fleet1Slot1, "q2_0001" }, + { CommandBarComponentId.Fleet1Slot2, "q2_0002" }, + { CommandBarComponentId.Fleet1Slot3, "q2_0003" }, + { CommandBarComponentId.Fleet1Slot4, "q2_0004" }, + { CommandBarComponentId.Fleet1Slot5, "q2_0005" }, + { CommandBarComponentId.Fleet1Slot6, "q2_0006" }, + { CommandBarComponentId.Fleet1Slot7, "q2_0100" }, + { CommandBarComponentId.Fleet1Slot8, "q2_0101" }, + { CommandBarComponentId.Fleet1Slot9, "q2_0102" }, + { CommandBarComponentId.Fleet1Slot10, "q2_0103" }, + { CommandBarComponentId.Fleet1Slot11, "q2_0104" }, + { CommandBarComponentId.Fleet1Slot12, "q2_0105" }, + { CommandBarComponentId.Fleet1Slot13, "q2_0106" }, + { CommandBarComponentId.Fleet1Slot14, "q2_0200" }, + { CommandBarComponentId.Fleet1Slot15, "q2_0201" }, + { CommandBarComponentId.Fleet1Slot16, "q2_0202" }, + { CommandBarComponentId.Fleet1Slot17, "q2_0203" }, + { CommandBarComponentId.Fleet1Slot18, "q2_0204" }, + { CommandBarComponentId.Fleet1Slot19, "q2_0205" }, + { CommandBarComponentId.Fleet1Slot20, "q2_0206" }, + { CommandBarComponentId.Fleet1Slot21, "q2_0300" }, + { CommandBarComponentId.Fleet1Slot22, "q2_0301" }, + { CommandBarComponentId.Fleet1Slot23, "q2_0302" }, + { CommandBarComponentId.Fleet1Slot24, "q2_0303" }, + { CommandBarComponentId.Fleet1Slot25, "q2_0304" }, + { CommandBarComponentId.Fleet1Slot26, "q2_0305" }, + { CommandBarComponentId.Fleet1Slot27, "q2_0306" }, + { CommandBarComponentId.Fleet1Slot28, "q2_0400" }, + { CommandBarComponentId.Fleet1Slot29, "q2_0401" }, + { CommandBarComponentId.Fleet1Slot30, "q2_0402" }, + { CommandBarComponentId.Fleet1Slot31, "q2_0403" }, + { CommandBarComponentId.Fleet1Slot32, "q2_0404" }, + { CommandBarComponentId.Fleet1Slot33, "q2_0405" }, + { CommandBarComponentId.Fleet1Slot34, "q2_0406" }, + { CommandBarComponentId.Fleet2Slot0, "q3_0000" }, + { CommandBarComponentId.Fleet2Slot1, "q3_0001" }, + { CommandBarComponentId.Fleet2Slot2, "q3_0002" }, + { CommandBarComponentId.Fleet2Slot3, "q3_0003" }, + { CommandBarComponentId.Fleet2Slot4, "q3_0004" }, + { CommandBarComponentId.Fleet2Slot5, "q3_0005" }, + { CommandBarComponentId.Fleet2Slot6, "q3_0006" }, + { CommandBarComponentId.Fleet2Slot7, "q3_0100" }, + { CommandBarComponentId.Fleet2Slot8, "q3_0101" }, + { CommandBarComponentId.Fleet2Slot9, "q3_0102" }, + { CommandBarComponentId.Fleet2Slot10, "q3_0103" }, + { CommandBarComponentId.Fleet2Slot11, "q3_0104" }, + { CommandBarComponentId.Fleet2Slot12, "q3_0105" }, + { CommandBarComponentId.Fleet2Slot13, "q3_0106" }, + { CommandBarComponentId.Fleet2Slot14, "q3_0200" }, + { CommandBarComponentId.Fleet2Slot15, "q3_0201" }, + { CommandBarComponentId.Fleet2Slot16, "q3_0202" }, + { CommandBarComponentId.Fleet2Slot17, "q3_0203" }, + { CommandBarComponentId.Fleet2Slot18, "q3_0204" }, + { CommandBarComponentId.Fleet2Slot19, "q3_0205" }, + { CommandBarComponentId.Fleet2Slot20, "q3_0206" }, + { CommandBarComponentId.Fleet2Slot21, "q3_0300" }, + { CommandBarComponentId.Fleet2Slot22, "q3_0301" }, + { CommandBarComponentId.Fleet2Slot23, "q3_0302" }, + { CommandBarComponentId.Fleet2Slot24, "q3_0303" }, + { CommandBarComponentId.Fleet2Slot25, "q3_0304" }, + { CommandBarComponentId.Fleet2Slot26, "q3_0305" }, + { CommandBarComponentId.Fleet2Slot27, "q3_0306" }, + { CommandBarComponentId.Fleet2Slot28, "q3_0400" }, + { CommandBarComponentId.Fleet2Slot29, "q3_0401" }, + { CommandBarComponentId.Fleet2Slot30, "q3_0402" }, + { CommandBarComponentId.Fleet2Slot31, "q3_0403" }, + { CommandBarComponentId.Fleet2Slot32, "q3_0404" }, + { CommandBarComponentId.Fleet2Slot33, "q3_0405" }, + { CommandBarComponentId.Fleet2Slot34, "q3_0406" }, + { CommandBarComponentId.TacticalMain, "i_main_skirmish" }, + { CommandBarComponentId.TacticalOptions, "b_option_t" }, + { CommandBarComponentId.TacticalCamera, "b_camera_t" }, + { CommandBarComponentId.TacticalBeacon, "b_beacon_t" }, + { CommandBarComponentId.TacticalWeapon0, "b_special_weapon" }, + { CommandBarComponentId.TacticalWeapon1, "b_special_weapon2" }, + { CommandBarComponentId.TacticalReinforce, "b_reinforcement" }, + { CommandBarComponentId.TacticalRetreat, "b_retreat" }, + { CommandBarComponentId.TacticalStoryArc, "b_story_arc_t" }, + { CommandBarComponentId.TacticalTechLevel, "Text_Tactical_Tech" }, + { CommandBarComponentId.TacticalAttack, "c_button00" }, + { CommandBarComponentId.TacticalAttackMove, "c_button01" }, + { CommandBarComponentId.TacticalMove, "c_button02" }, + { CommandBarComponentId.TacticalWaypoint, "c_button03" }, + { CommandBarComponentId.TacticalStop, "c_button04" }, + { CommandBarComponentId.TacticalGuard, "c_button05" }, + { CommandBarComponentId.SpaceTacticalRadar, "radar" }, + { CommandBarComponentId.SpaceTacticalCredits, "Text_Credits_tactical" }, + { CommandBarComponentId.SpaceTacticalGrabBar, "st_grab_bar" }, + { CommandBarComponentId.SpaceTacticalHealthBar, "st_health_bar" }, + { CommandBarComponentId.SpaceTacticalBracketSmall, "st_bracket_small" }, + { CommandBarComponentId.SpaceTacticalBracketMedium, "st_bracket_medium" }, + { CommandBarComponentId.SpaceTacticalBracketLarge, "st_bracket_large" }, + { CommandBarComponentId.SpaceTacticalHeroIcon, "st_hero_icon" }, + { CommandBarComponentId.SpaceTacticalHeroHealth, "st_hero_health" }, + { CommandBarComponentId.SpaceTacticalHealth, "st_health" }, + { CommandBarComponentId.SpaceTacticalHealthMedium, "st_health_medium" }, + { CommandBarComponentId.SpaceTacticalHealthLarge, "st_health_large" }, + { CommandBarComponentId.SpaceTacticalShields, "st_shields" }, + { CommandBarComponentId.SpaceTacticalShieldsMedium, "st_shields_medium" }, + { CommandBarComponentId.SpaceTacticalShieldsLarge, "st_shields_large" }, + { CommandBarComponentId.SpaceTacticalPower, "st_power" }, + { CommandBarComponentId.SpaceTacticalControlGroup, "st_control_group" }, + { CommandBarComponentId.SpaceTacticalAbilityIcon, "st_ability_icon" }, + { CommandBarComponentId.SpaceTacticalGarrisonIcon, "st_garrison_icon" }, + { CommandBarComponentId.LandTacticalWeatherIcon, "lt_weather_icon" }, + { CommandBarComponentId.GarrisonSlotIcon, "garrison_slot_icon" }, + { CommandBarComponentId.DsFireShell, "ds_shell" }, + { CommandBarComponentId.DsFire, "deathstar_switch" }, + { CommandBarComponentId.DsCountdownTimer, "DS_Countdown_Timer" }, + { CommandBarComponentId.TacticalSelect00, "s_select_00" }, + { CommandBarComponentId.TacticalSelect01, "s_select_01" }, + { CommandBarComponentId.TacticalSelect02, "s_select_02" }, + { CommandBarComponentId.TacticalSelect03, "s_select_03" }, + { CommandBarComponentId.TacticalSelect04, "s_select_04" }, + { CommandBarComponentId.TacticalSelect05, "s_select_05" }, + { CommandBarComponentId.TacticalSelect06, "s_select_06" }, + { CommandBarComponentId.TacticalSelect07, "s_select_07" }, + { CommandBarComponentId.TacticalSelect08, "s_select_08" }, + { CommandBarComponentId.TacticalSelect09, "s_select_09" }, + { CommandBarComponentId.TacticalSelect10, "s_select_10" }, + { CommandBarComponentId.TacticalSelect11, "s_select_11" }, + { CommandBarComponentId.TacticalSelect12, "s_select_12" }, + { CommandBarComponentId.TacticalSelect13, "s_select_13" }, + { CommandBarComponentId.TacticalSelect14, "s_select_14" }, + { CommandBarComponentId.TacticalSelect15, "s_select_15" }, + { CommandBarComponentId.TacticalSelect16, "s_select_16" }, + { CommandBarComponentId.TacticalSelect17, "s_select_17" }, + { CommandBarComponentId.TacticalSelect18, "s_select_18" }, + { CommandBarComponentId.TacticalSelect19, "s_select_19" }, + { CommandBarComponentId.TacticalSelect20, "s_select_20" }, + { CommandBarComponentId.TacticalSelect21, "s_select_21" }, + { CommandBarComponentId.TacticalSelect22, "s_select_22" }, + { CommandBarComponentId.TacticalSelect23, "s_select_23" }, + { CommandBarComponentId.TacticalSelect24, "s_select_24" }, + { CommandBarComponentId.TacticalSelect25, "s_select_25" }, + { CommandBarComponentId.TacticalSelect26, "s_select_26" }, + { CommandBarComponentId.TacticalSelect27, "s_select_27" }, + { CommandBarComponentId.TacticalSelect28, "s_select_28" }, + { CommandBarComponentId.TacticalSelect29, "s_select_29" }, + { CommandBarComponentId.TacticalSelect30, "s_select_30" }, + { CommandBarComponentId.TacticalSelect31, "s_select_31" }, + { CommandBarComponentId.TacticalSelect32, "s_select_32" }, + { CommandBarComponentId.TacticalSelect33, "s_select_33" }, + { CommandBarComponentId.TacticalSelect34, "s_select_34" }, + { CommandBarComponentId.TacticalSelect35, "s_select_35" }, + { CommandBarComponentId.TacticalSelect36, "s_select_36" }, + { CommandBarComponentId.TacticalSelect37, "s_select_37" }, + { CommandBarComponentId.TacticalSelect38, "s_select_38" }, + { CommandBarComponentId.TacticalSelect39, "s_select_39" }, + { CommandBarComponentId.TacticalSelect40, "s_select_40" }, + { CommandBarComponentId.TacticalSelect41, "s_select_41" }, + { CommandBarComponentId.TacticalSelect42, "s_select_42" }, + { CommandBarComponentId.TacticalSelect43, "s_select_43" }, + { CommandBarComponentId.TacticalSelect44, "s_select_44" }, + { CommandBarComponentId.TacticalSelect45, "s_select_45" }, + { CommandBarComponentId.TacticalSelect46, "s_select_46" }, + { CommandBarComponentId.TacticalSelect47, "s_select_47" }, + { CommandBarComponentId.TacticalSelectHealth00, "s_health_00" }, + { CommandBarComponentId.TacticalSelectHealth01, "s_health_01" }, + { CommandBarComponentId.TacticalSelectHealth02, "s_health_02" }, + { CommandBarComponentId.TacticalSelectHealth03, "s_health_03" }, + { CommandBarComponentId.TacticalSelectHealth04, "s_health_04" }, + { CommandBarComponentId.TacticalSelectHealth05, "s_health_05" }, + { CommandBarComponentId.TacticalSelectHealth06, "s_health_06" }, + { CommandBarComponentId.TacticalSelectHealth07, "s_health_07" }, + { CommandBarComponentId.TacticalSelectHealth08, "s_health_08" }, + { CommandBarComponentId.TacticalSelectHealth09, "s_health_09" }, + { CommandBarComponentId.TacticalSelectHealth10, "s_health_10" }, + { CommandBarComponentId.TacticalSelectHealth11, "s_health_11" }, + { CommandBarComponentId.TacticalSelectHealth12, "s_health_12" }, + { CommandBarComponentId.TacticalSelectHealth13, "s_health_13" }, + { CommandBarComponentId.TacticalSelectHealth14, "s_health_14" }, + { CommandBarComponentId.TacticalSelectHealth15, "s_health_15" }, + { CommandBarComponentId.TacticalSelectHealth16, "s_health_16" }, + { CommandBarComponentId.TacticalSelectHealth17, "s_health_17" }, + { CommandBarComponentId.TacticalSelectHealth18, "s_health_18" }, + { CommandBarComponentId.TacticalSelectHealth19, "s_health_19" }, + { CommandBarComponentId.TacticalSelectHealth20, "s_health_20" }, + { CommandBarComponentId.TacticalSelectHealth21, "s_health_21" }, + { CommandBarComponentId.TacticalSelectHealth22, "s_health_22" }, + { CommandBarComponentId.TacticalSelectHealth23, "s_health_23" }, + { CommandBarComponentId.TacticalSelectHealth24, "s_health_24" }, + { CommandBarComponentId.TacticalSelectHealth25, "s_health_25" }, + { CommandBarComponentId.TacticalSelectHealth26, "s_health_26" }, + { CommandBarComponentId.TacticalSelectHealth27, "s_health_27" }, + { CommandBarComponentId.TacticalSelectHealth28, "s_health_28" }, + { CommandBarComponentId.TacticalSelectHealth29, "s_health_29" }, + { CommandBarComponentId.TacticalSelectHealth30, "s_health_30" }, + { CommandBarComponentId.TacticalSelectHealth31, "s_health_31" }, + { CommandBarComponentId.TacticalSelectHealth32, "s_health_32" }, + { CommandBarComponentId.TacticalSelectHealth33, "s_health_33" }, + { CommandBarComponentId.TacticalSelectHealth34, "s_health_34" }, + { CommandBarComponentId.TacticalSelectHealth35, "s_health_35" }, + { CommandBarComponentId.TacticalSelectHealth36, "s_health_36" }, + { CommandBarComponentId.TacticalSelectHealth37, "s_health_37" }, + { CommandBarComponentId.TacticalSelectHealth38, "s_health_38" }, + { CommandBarComponentId.TacticalSelectHealth39, "s_health_39" }, + { CommandBarComponentId.TacticalSelectHealth40, "s_health_40" }, + { CommandBarComponentId.TacticalSelectHealth41, "s_health_41" }, + { CommandBarComponentId.TacticalSelectHealth42, "s_health_42" }, + { CommandBarComponentId.TacticalSelectHealth43, "s_health_43" }, + { CommandBarComponentId.TacticalSelectHealth44, "s_health_44" }, + { CommandBarComponentId.TacticalSelectHealth45, "s_health_45" }, + { CommandBarComponentId.TacticalSelectHealth46, "s_health_46" }, + { CommandBarComponentId.TacticalSelectHealth47, "s_health_47" }, + { CommandBarComponentId.TacticalSelectShield00, "s_shield_00" }, + { CommandBarComponentId.TacticalSelectShield01, "s_shield_01" }, + { CommandBarComponentId.TacticalSelectShield02, "s_shield_02" }, + { CommandBarComponentId.TacticalSelectShield03, "s_shield_03" }, + { CommandBarComponentId.TacticalSelectShield04, "s_shield_04" }, + { CommandBarComponentId.TacticalSelectShield05, "s_shield_05" }, + { CommandBarComponentId.TacticalSelectShield06, "s_shield_06" }, + { CommandBarComponentId.TacticalSelectShield07, "s_shield_07" }, + { CommandBarComponentId.TacticalSelectShield08, "s_shield_08" }, + { CommandBarComponentId.TacticalSelectShield09, "s_shield_09" }, + { CommandBarComponentId.TacticalSelectShield10, "s_shield_10" }, + { CommandBarComponentId.TacticalSelectShield11, "s_shield_11" }, + { CommandBarComponentId.TacticalSelectShield12, "s_shield_12" }, + { CommandBarComponentId.TacticalSelectShield13, "s_shield_13" }, + { CommandBarComponentId.TacticalSelectShield14, "s_shield_14" }, + { CommandBarComponentId.TacticalSelectShield15, "s_shield_15" }, + { CommandBarComponentId.TacticalSelectShield16, "s_shield_16" }, + { CommandBarComponentId.TacticalSelectShield17, "s_shield_17" }, + { CommandBarComponentId.TacticalSelectShield18, "s_shield_18" }, + { CommandBarComponentId.TacticalSelectShield19, "s_shield_19" }, + { CommandBarComponentId.TacticalSelectShield20, "s_shield_20" }, + { CommandBarComponentId.TacticalSelectShield21, "s_shield_21" }, + { CommandBarComponentId.TacticalSelectShield22, "s_shield_22" }, + { CommandBarComponentId.TacticalSelectShield23, "s_shield_23" }, + { CommandBarComponentId.TacticalSelectShield24, "s_shield_24" }, + { CommandBarComponentId.TacticalSelectShield25, "s_shield_25" }, + { CommandBarComponentId.TacticalSelectShield26, "s_shield_26" }, + { CommandBarComponentId.TacticalSelectShield27, "s_shield_27" }, + { CommandBarComponentId.TacticalSelectShield28, "s_shield_28" }, + { CommandBarComponentId.TacticalSelectShield29, "s_shield_29" }, + { CommandBarComponentId.TacticalSelectShield30, "s_shield_30" }, + { CommandBarComponentId.TacticalSelectShield31, "s_shield_31" }, + { CommandBarComponentId.TacticalSelectShield32, "s_shield_32" }, + { CommandBarComponentId.TacticalSelectShield33, "s_shield_33" }, + { CommandBarComponentId.TacticalSelectShield34, "s_shield_34" }, + { CommandBarComponentId.TacticalSelectShield35, "s_shield_35" }, + { CommandBarComponentId.TacticalSelectShield36, "s_shield_36" }, + { CommandBarComponentId.TacticalSelectShield37, "s_shield_37" }, + { CommandBarComponentId.TacticalSelectShield38, "s_shield_38" }, + { CommandBarComponentId.TacticalSelectShield39, "s_shield_39" }, + { CommandBarComponentId.TacticalSelectShield40, "s_shield_40" }, + { CommandBarComponentId.TacticalSelectShield41, "s_shield_41" }, + { CommandBarComponentId.TacticalSelectShield42, "s_shield_42" }, + { CommandBarComponentId.TacticalSelectShield43, "s_shield_43" }, + { CommandBarComponentId.TacticalSelectShield44, "s_shield_44" }, + { CommandBarComponentId.TacticalSelectShield45, "s_shield_45" }, + { CommandBarComponentId.TacticalSelectShield46, "s_shield_46" }, + { CommandBarComponentId.TacticalSelectShield47, "s_shield_47" }, + { CommandBarComponentId.TacticalBorder00, "special_border_00" }, + { CommandBarComponentId.TacticalBorder01, "special_border_01" }, + { CommandBarComponentId.TacticalBorder02, "special_border_02" }, + { CommandBarComponentId.TacticalBorder03, "special_border_03" }, + { CommandBarComponentId.TacticalBorder04, "special_border_04" }, + { CommandBarComponentId.TacticalBorder05, "special_border_05" }, + { CommandBarComponentId.TacticalBorder06, "special_border_06" }, + { CommandBarComponentId.TacticalBorder07, "special_border_07" }, + { CommandBarComponentId.TacticalBorder08, "special_border_08" }, + { CommandBarComponentId.TacticalBorder09, "special_border_09" }, + { CommandBarComponentId.TacticalBorder10, "special_border_10" }, + { CommandBarComponentId.TacticalBorder11, "special_border_11" }, + { CommandBarComponentId.TacticalSelectButton00, "special_button_00" }, + { CommandBarComponentId.TacticalSelectButton01, "special_button_01" }, + { CommandBarComponentId.TacticalSelectButton02, "special_button_02" }, + { CommandBarComponentId.TacticalSelectButton03, "special_button_03" }, + { CommandBarComponentId.TacticalSelectButton04, "special_button_04" }, + { CommandBarComponentId.TacticalSelectButton05, "special_button_05" }, + { CommandBarComponentId.TacticalSelectButton06, "special_button_06" }, + { CommandBarComponentId.TacticalSelectButton07, "special_button_07" }, + { CommandBarComponentId.TacticalSelectButton08, "special_button_08" }, + { CommandBarComponentId.TacticalSelectButton09, "special_button_09" }, + { CommandBarComponentId.TacticalSelectButton10, "special_button_10" }, + { CommandBarComponentId.TacticalSelectButton11, "special_button_11" }, + { CommandBarComponentId.TacticalSelectButton12, "special_button_12" }, + { CommandBarComponentId.TacticalSelectButton13, "special_button_13" }, + { CommandBarComponentId.TacticalSelectButton14, "special_button_14" }, + { CommandBarComponentId.TacticalSelectButton15, "special_button_15" }, + { CommandBarComponentId.TacticalSelectButton16, "special_button_16" }, + { CommandBarComponentId.TacticalSelectButton17, "special_button_17" }, + { CommandBarComponentId.TacticalSelectButton18, "special_button_18" }, + { CommandBarComponentId.TacticalSelectButton19, "special_button_19" }, + { CommandBarComponentId.TacticalSelectButton20, "special_button_20" }, + { CommandBarComponentId.TacticalSelectButton21, "special_button_21" }, + { CommandBarComponentId.TacticalSelectButton22, "special_button_22" }, + { CommandBarComponentId.TacticalSelectButton23, "special_button_23" }, + { CommandBarComponentId.TacticalSelectButton24, "special_button_24" }, + { CommandBarComponentId.TacticalSelectButton25, "special_button_25" }, + { CommandBarComponentId.TacticalSelectButton26, "special_button_26" }, + { CommandBarComponentId.TacticalSelectButton27, "special_button_27" }, + { CommandBarComponentId.TacticalSelectButton28, "special_button_28" }, + { CommandBarComponentId.TacticalSelectButton29, "special_button_29" }, + { CommandBarComponentId.TacticalSelectButton30, "special_button_30" }, + { CommandBarComponentId.TacticalSelectButton31, "special_button_31" }, + { CommandBarComponentId.TacticalSelectButton32, "special_button_32" }, + { CommandBarComponentId.TacticalSelectButton33, "special_button_33" }, + { CommandBarComponentId.TacticalSelectButton34, "special_button_34" }, + { CommandBarComponentId.TacticalSelectButton35, "special_button_35" }, + { CommandBarComponentId.TacticalSelectButton36, "special_button_36" }, + { CommandBarComponentId.TacticalSelectButton37, "special_button_37" }, + { CommandBarComponentId.TacticalSelectButton38, "special_button_38" }, + { CommandBarComponentId.TacticalSelectButton39, "special_button_39" }, + { CommandBarComponentId.TacticalSelectButton40, "special_button_40" }, + { CommandBarComponentId.TacticalSelectButton41, "special_button_41" }, + { CommandBarComponentId.TacticalSelectButton42, "special_button_42" }, + { CommandBarComponentId.TacticalSelectButton43, "special_button_43" }, + { CommandBarComponentId.TacticalSelectButton44, "special_button_44" }, + { CommandBarComponentId.TacticalSelectButton45, "special_button_45" }, + { CommandBarComponentId.TacticalSelectButton46, "special_button_46" }, + { CommandBarComponentId.TacticalSelectButton47, "special_button_47" }, + { CommandBarComponentId.TacticalBuildQueue00, "tqueue00" }, + { CommandBarComponentId.TacticalBuildQueue01, "tqueue01" }, + { CommandBarComponentId.TacticalBuildQueue02, "tqueue02" }, + { CommandBarComponentId.TacticalBuildQueue03, "tqueue03" }, + { CommandBarComponentId.TacticalBuildQueue04, "tqueue04" }, + { CommandBarComponentId.TacticalBuildQueue05, "tqueue05" }, + { CommandBarComponentId.TacticalBuildQueue06, "tqueue06" }, + { CommandBarComponentId.TacticalBuildQueue07, "tqueue07" }, + { CommandBarComponentId.TacticalBuildQueue08, "tqueue08" }, + { CommandBarComponentId.TacticalBuildQueue09, "tqueue09" }, + { CommandBarComponentId.TooltipBack, "tooltip_back" }, + { CommandBarComponentId.TooltipName, "tooltip_name" }, + { CommandBarComponentId.TooltipPrice, "tooltip_price" }, + { CommandBarComponentId.TooltipIcon, "tooltip_icon" }, + { CommandBarComponentId.TooltipIconLand, "tooltip_icon_land" }, + { CommandBarComponentId.TooltipLeftJustified, "tooltip_left_text" }, + { CommandBarComponentId.EncyclopediaBack, "encyclopedia_back" }, + { CommandBarComponentId.EncyclopediaHeaderText, "encyclopedia_header_text" }, + { CommandBarComponentId.EncyclopediaText, "encyclopedia_text" }, + { CommandBarComponentId.EncyclopediaRightText, "encyclopedia_right_text" }, + { CommandBarComponentId.EncyclopediaCenterText, "encyclopedia_center_text" }, + { CommandBarComponentId.EncyclopediaIcon, "encyclopedia_icon" }, + { CommandBarComponentId.EncyclopediaCostText, "encyclopedia_cost_text" }, + { CommandBarComponentId.ZoomedBack, "zoomed_back" }, + { CommandBarComponentId.ZoomedHeaderText, "zoomed_header_text" }, + { CommandBarComponentId.ZoomedText, "zoomed_text" }, + { CommandBarComponentId.ZoomedRightText, "zoomed_right_text" }, + { CommandBarComponentId.ZoomedCenterText, "zoomed_center_text" }, + { CommandBarComponentId.ZoomedCostText, "zoomed_cost_text" }, + { CommandBarComponentId.GPlanetFleet, "g_planet_fleet" }, + { CommandBarComponentId.GPlanetName, "g_planet_name" }, + { CommandBarComponentId.GPlanetValue, "g_planet_value" }, + { CommandBarComponentId.GPoliticalControl, "g_political_control" }, + { CommandBarComponentId.GSpaceLevel, "g_space_level" }, + { CommandBarComponentId.GSpaceIcon, "g_space_icon" }, + { CommandBarComponentId.GSpaceLevelPips, "g_space_level_pips" }, + { CommandBarComponentId.GGroundLevel, "g_ground_level" }, + { CommandBarComponentId.GGroundIcon, "g_ground_icon" }, + { CommandBarComponentId.GGroundLevelPips, "g_ground_level_pips" }, + { CommandBarComponentId.GConflict, "g_conflict" }, + { CommandBarComponentId.GHero, "g_hero" }, + { CommandBarComponentId.GEnemyHero, "g_enemy_hero" }, + { CommandBarComponentId.GBuild, "g_build" }, + { CommandBarComponentId.GSmuggler, "g_smuggler" }, + { CommandBarComponentId.GBountyHunter, "g_bounty_hunter" }, + { CommandBarComponentId.GPlanetLandForces, "g_planet_land_forces" }, + { CommandBarComponentId.GGalacticRadarBlip, "g_radar_blip" }, + { CommandBarComponentId.GGalacticRadarView, "g_radar_view" }, + { CommandBarComponentId.GSmuggled, "g_smuggled" }, + { CommandBarComponentId.GSpecialAbility, "g_special_ability" }, + { CommandBarComponentId.GHeroIcon, "g_hero_icon" }, + { CommandBarComponentId.GPlanetRing, "g_planet_ring" }, + { CommandBarComponentId.GWeather, "g_weather" }, + { CommandBarComponentId.GPlanetAbility, "g_planet_ability" }, + { CommandBarComponentId.GCorruptionText, "g_corruption_text" }, + { CommandBarComponentId.GCorruptionIcon, "g_corruption_icon" }, + { CommandBarComponentId.TutorialText, "tutorial_text" }, + { CommandBarComponentId.TutorialTextBack, "tutorial_text_back" }, + { CommandBarComponentId.RadarBlip, "radar_blip" }, + { CommandBarComponentId.TacticalBuildButtonShell, "i_build_buttons" }, + { CommandBarComponentId.TacticalBuildButton0, "Build_00" }, + { CommandBarComponentId.TacticalBuildButton1, "Build_01" }, + { CommandBarComponentId.TacticalBuildButton2, "Build_02" }, + { CommandBarComponentId.TacticalBuildButton3, "Build_03" }, + { CommandBarComponentId.TacticalBuildButton4, "Build_04" }, + { CommandBarComponentId.TacticalBuildButton5, "Build_05" }, + { CommandBarComponentId.TacticalSellButton, "tactical_sell" }, + { CommandBarComponentId.ReinforcementShell, "i_main_reinforce" }, + { CommandBarComponentId.ReinforcementCancel, "r_close" }, + { CommandBarComponentId.ReinforcementSlot00, "r_0000" }, + { CommandBarComponentId.ReinforcementSlot01, "r_0001" }, + { CommandBarComponentId.ReinforcementSlot02, "r_0002" }, + { CommandBarComponentId.ReinforcementSlot03, "r_0003" }, + { CommandBarComponentId.ReinforcementSlot04, "r_0100" }, + { CommandBarComponentId.ReinforcementSlot05, "r_0101" }, + { CommandBarComponentId.ReinforcementSlot06, "r_0102" }, + { CommandBarComponentId.ReinforcementSlot07, "r_0103" }, + { CommandBarComponentId.ReinforcementSlot08, "r_0200" }, + { CommandBarComponentId.ReinforcementSlot09, "r_0201" }, + { CommandBarComponentId.ReinforcementSlot10, "r_0202" }, + { CommandBarComponentId.ReinforcementSlot11, "r_0203" }, + { CommandBarComponentId.ReinforcementSlot12, "r_0300" }, + { CommandBarComponentId.ReinforcementSlot13, "r_0301" }, + { CommandBarComponentId.ReinforcementSlot14, "r_0302" }, + { CommandBarComponentId.ReinforcementSlot15, "r_0303" }, + { CommandBarComponentId.ReinforcementSlot16, "r_0400" }, + { CommandBarComponentId.ReinforcementSlot17, "r_0401" }, + { CommandBarComponentId.ReinforcementSlot18, "r_0402" }, + { CommandBarComponentId.ReinforcementSlot19, "r_0403" }, + { CommandBarComponentId.ReinforcementCap2, "r_pop_icon" }, + { CommandBarComponentId.ReinforcementCap2Text, "r_pop_text" }, + { CommandBarComponentId.ReinforcementCounter, "reinforcement_counter" }, + { CommandBarComponentId.GarrisonRespawnCounter, "garrison_respawn_counter" }, + { CommandBarComponentId.SkirmishUpgrade, "skirmish_upgrade" }, + { CommandBarComponentId.PendingBattleShell, "pending_battle_shell" }, + { CommandBarComponentId.PendingBattleText, "text_attack_choice" }, + { CommandBarComponentId.PendingBattleButton, "choice_button_left" }, + { CommandBarComponentId.PendingBattleAutoresolve, "choice_button_right" }, + { CommandBarComponentId.PendingBattleGraphLeft, "graph_left" }, + { CommandBarComponentId.PendingBattleGraphRight, "graph_right" }, + { CommandBarComponentId.TatcicalAutoresolveShell, "autoresolve_shell" }, + { CommandBarComponentId.TacticalAutoresolveButton, "resolve_button" }, + { CommandBarComponentId.TacticalAutoresolveGraphLeft, "graph_left_resolve" }, + { CommandBarComponentId.TacticalAutoresolveGraphRight, "graph_right_resolve" }, + { CommandBarComponentId.ObjectiveBack, "objective_back" }, + { CommandBarComponentId.ObjectiveHeaderText, "objective_header_text" }, + { CommandBarComponentId.ObjectiveText, "objective_text" }, + { CommandBarComponentId.ObjectiveIcon, "objective_icon" }, + { CommandBarComponentId.GuiDialogTooltip, "gui_dialog_tooltip" }, + { CommandBarComponentId.TargetUnitTypeShell, "target_type_back" }, + { CommandBarComponentId.TargetUnitTypeTitle, "h_title" }, + { CommandBarComponentId.TargetUnitTypeCancel, "h_close" }, + { CommandBarComponentId.TargetUnitTypeDescription, "text_hero" }, + { CommandBarComponentId.TargetUnitTypeSlot00, "h_0000" }, + { CommandBarComponentId.TargetUnitTypeSlot01, "h_0001" }, + { CommandBarComponentId.TargetUnitTypeSlot02, "h_0100" }, + { CommandBarComponentId.TargetUnitTypeSlot03, "h_0101" }, + { CommandBarComponentId.TargetUnitTypeSlot04, "h_0200" }, + { CommandBarComponentId.TargetUnitTypeSlot05, "h_0201" }, + { CommandBarComponentId.TargetUnitTypeSlot06, "h_0300" }, + { CommandBarComponentId.TargetUnitTypeSlot07, "h_0301" }, + { CommandBarComponentId.TargetUnitTypeSlot08, "h_0400" }, + { CommandBarComponentId.TargetUnitTypeSlot09, "h_0401" }, + { CommandBarComponentId.TargetUnitTypeSlot10, "h_0500" }, + { CommandBarComponentId.TargetUnitTypeSlot11, "h_0501" }, + { CommandBarComponentId.TargetUnitTypeName00, "h_name_00" }, + { CommandBarComponentId.TargetUnitTypeName01, "h_name_01" }, + { CommandBarComponentId.TargetUnitTypeName02, "h_name_02" }, + { CommandBarComponentId.TargetUnitTypeName03, "h_name_03" }, + { CommandBarComponentId.TargetUnitTypeName04, "h_name_04" }, + { CommandBarComponentId.TargetUnitTypeName05, "h_name_05" }, + { CommandBarComponentId.TargetUnitTypeName06, "h_name_06" }, + { CommandBarComponentId.TargetUnitTypeName07, "h_name_07" }, + { CommandBarComponentId.TargetUnitTypeName08, "h_name_08" }, + { CommandBarComponentId.TargetUnitTypeName09, "h_name_09" }, + { CommandBarComponentId.TargetUnitTypeName10, "h_name_10" }, + { CommandBarComponentId.TargetUnitTypeName11, "h_name_11" }, + { CommandBarComponentId.TargetUnitTypePrice00, "h_price_00" }, + { CommandBarComponentId.TargetUnitTypePrice01, "h_price_01" }, + { CommandBarComponentId.TargetUnitTypePrice02, "h_price_02" }, + { CommandBarComponentId.TargetUnitTypePrice03, "h_price_03" }, + { CommandBarComponentId.TargetUnitTypePrice04, "h_price_04" }, + { CommandBarComponentId.TargetUnitTypePrice05, "h_price_05" }, + { CommandBarComponentId.TargetUnitTypePrice06, "h_price_06" }, + { CommandBarComponentId.TargetUnitTypePrice07, "h_price_07" }, + { CommandBarComponentId.TargetUnitTypePrice08, "h_price_08" }, + { CommandBarComponentId.TargetUnitTypePrice09, "h_price_09" }, + { CommandBarComponentId.TargetUnitTypePrice10, "h_price_10" }, + { CommandBarComponentId.TargetUnitTypePrice11, "h_price_11" }, + { CommandBarComponentId.VcrButtonPlayPause, "b_play_pause" }, + { CommandBarComponentId.VcrButtonFastForward, "b_fast_forward" }, + { CommandBarComponentId.VcrButtonFastForwardTactical, "b_fast_forward_t" }, + { CommandBarComponentId.VcrButtonPlayPauseTactical, "b_play_pause_t" }, + { CommandBarComponentId.AdvisorHintPopupGalactic, "text_galactic_help" }, + { CommandBarComponentId.AdvisorHintPopupTactical, "text_tactical_help" }, + { CommandBarComponentId.AdvisorHintBack, "help_back" }, + { CommandBarComponentId.TextOrganizeFleet00, "text_fleet1" }, + { CommandBarComponentId.TextOrganizeFleet01, "text_fleet2" }, + { CommandBarComponentId.TextOrganizeFleet02, "text_fleet3" }, + { CommandBarComponentId.IconOrganizeFleet00, "icon_fleet01" }, + { CommandBarComponentId.IconOrganizeFleet01, "icon_fleet02" }, + { CommandBarComponentId.IconOrganizeFleet02, "icon_fleet03" }, + { CommandBarComponentId.TextOrganizeLandFleet, "Text_Land_Fleet" }, + { CommandBarComponentId.CsAbilityButton, "cs_ability_button" }, + { CommandBarComponentId.CsAbilityText, "cs_ability_text" }, + { CommandBarComponentId.MovieBoneGalactic, "Movie_galactic" }, + { CommandBarComponentId.MovieBoneTactical, "Movie_tactical" }, + { CommandBarComponentId.GenericCollision, "generic_collision" }, + { CommandBarComponentId.GoodHeroShell, "good_hero_frame" }, + { CommandBarComponentId.GoodHeroSlot00, "hero_slot_good00" }, + { CommandBarComponentId.GoodHeroSlot01, "hero_slot_good01" }, + { CommandBarComponentId.GoodHeroSlot02, "hero_slot_good02" }, + { CommandBarComponentId.GoodHeroSlot03, "hero_slot_good03" }, + { CommandBarComponentId.GoodHeroSlot04, "hero_slot_good04" }, + { CommandBarComponentId.GoodHeroSlot05, "hero_slot_good05" }, + { CommandBarComponentId.GoodHeroSlot06, "hero_slot_good06" }, + { CommandBarComponentId.GoodHeroSlot07, "hero_slot_good07" }, + { CommandBarComponentId.GoodHeroSlot08, "hero_slot_good08" }, + { CommandBarComponentId.GoodHeroSlot09, "hero_slot_good09" }, + { CommandBarComponentId.GoodHeroSlot10, "hero_slot_good10" }, + { CommandBarComponentId.GoodHeroHealth00, "hero_hb_good00" }, + { CommandBarComponentId.GoodHeroHealth01, "hero_hb_good01" }, + { CommandBarComponentId.GoodHeroHealth02, "hero_hb_good02" }, + { CommandBarComponentId.GoodHeroHealth03, "hero_hb_good03" }, + { CommandBarComponentId.GoodHeroHealth04, "hero_hb_good04" }, + { CommandBarComponentId.GoodHeroHealth05, "hero_hb_good05" }, + { CommandBarComponentId.GoodHeroHealth06, "hero_hb_good06" }, + { CommandBarComponentId.GoodHeroHealth07, "hero_hb_good07" }, + { CommandBarComponentId.GoodHeroHealth08, "hero_hb_good08" }, + { CommandBarComponentId.GoodHeroHealth09, "hero_hb_good09" }, + { CommandBarComponentId.GoodHeroHealth10, "hero_hb_good10" }, + { CommandBarComponentId.EvilHeroShell, "evil_hero_frame" }, + { CommandBarComponentId.EvilHeroSlot00, "hero_slot_evil00" }, + { CommandBarComponentId.EvilHeroSlot01, "hero_slot_evil01" }, + { CommandBarComponentId.EvilHeroSlot02, "hero_slot_evil02" }, + { CommandBarComponentId.EvilHeroSlot03, "hero_slot_evil03" }, + { CommandBarComponentId.EvilHeroSlot04, "hero_slot_evil04" }, + { CommandBarComponentId.EvilHeroSlot05, "hero_slot_evil05" }, + { CommandBarComponentId.EvilHeroSlot06, "hero_slot_evil06" }, + { CommandBarComponentId.EvilHeroSlot07, "hero_slot_evil07" }, + { CommandBarComponentId.EvilHeroSlot08, "hero_slot_evil08" }, + { CommandBarComponentId.EvilHeroSlot09, "hero_slot_evil09" }, + { CommandBarComponentId.EvilHeroSlot10, "hero_slot_evil10" }, + { CommandBarComponentId.EvilHeroHealth00, "hero_hb_evil00" }, + { CommandBarComponentId.EvilHeroHealth01, "hero_hb_evil01" }, + { CommandBarComponentId.EvilHeroHealth02, "hero_hb_evil02" }, + { CommandBarComponentId.EvilHeroHealth03, "hero_hb_evil03" }, + { CommandBarComponentId.EvilHeroHealth04, "hero_hb_evil04" }, + { CommandBarComponentId.EvilHeroHealth05, "hero_hb_evil05" }, + { CommandBarComponentId.EvilHeroHealth06, "hero_hb_evil06" }, + { CommandBarComponentId.EvilHeroHealth07, "hero_hb_evil07" }, + { CommandBarComponentId.EvilHeroHealth08, "hero_hb_evil08" }, + { CommandBarComponentId.EvilHeroHealth09, "hero_hb_evil09" }, + { CommandBarComponentId.EvilHeroHealth10, "hero_hb_evil10" }, + { CommandBarComponentId.PauseShell, "pause_shell" }, + { CommandBarComponentId.PauseText, "text_attack" }, + { CommandBarComponentId.PauseButton, "attack_button" }, + { CommandBarComponentId.StoryCampaignPendingBattleShell, "story_pending_battle_shell" }, + { CommandBarComponentId.StoryCampaignPendingBattleText, "text_attack_single" }, + { CommandBarComponentId.StoryCampaignPendingBattleButton, "attack_button_single" }, + { CommandBarComponentId.MapActivate, "advanced_planetary" }, + { CommandBarComponentId.MapShell, "map_shell" }, + { CommandBarComponentId.MapOverlayShell, "map_overlay_shell" }, + { CommandBarComponentId.MapGroundForcesText, "ground_forces_text" }, + { CommandBarComponentId.MapDragDropText, "drage_drop_text" }, + { CommandBarComponentId.MapUnitCapText, "unit_cap_text" }, + { CommandBarComponentId.MapUnitCapNumber, "unit_cap_number" }, + { CommandBarComponentId.MapBuildingCapText, "building_cap_text" }, + { CommandBarComponentId.MapBuildingCapNumber, "building_cap_number" }, + { CommandBarComponentId.MapFactionIcon, "Allegiance_A" }, + { CommandBarComponentId.MapPlanetNameText, "Planet_name_text" }, + { CommandBarComponentId.MapPlanetIncomeNumber, "Planetary_Income_number" }, + { CommandBarComponentId.MapDayCounterText, "Day_Counter_text" }, + { CommandBarComponentId.MapDayCounter, "day_clock" }, + { CommandBarComponentId.MapBackButton, "back_button" }, + { CommandBarComponentId.MapWeatherIcon, "weather" }, + { CommandBarComponentId.MapPlanetBonusIcon, "planet_bonus" }, + { CommandBarComponentId.MapRadarMap, "radar_galactic_adv_map" }, + { CommandBarComponentId.MapZoomOut, "b_zoom_adv_map" }, + { CommandBarComponentId.MapEncyclopediaPopup, "Text_adv_help" }, + { CommandBarComponentId.AdvancedMapFilter0, "c_filters0" }, + { CommandBarComponentId.AdvancedMapFilter1, "c_filters1" }, + { CommandBarComponentId.AdvancedMapFilter2, "c_filters2" }, + { CommandBarComponentId.AdvancedMapFilter3, "c_filters3" }, + { CommandBarComponentId.AdvancedMapOptions, "c_option_g" }, + { CommandBarComponentId.MapMainMap, "map_window" }, + { CommandBarComponentId.MapSpaceUnit00, "land_fleet_00" }, + { CommandBarComponentId.MapSpaceUnit01, "land_fleet_01" }, + { CommandBarComponentId.MapSpaceUnit02, "land_fleet_02" }, + { CommandBarComponentId.MapSpaceUnit03, "land_fleet_03" }, + { CommandBarComponentId.MapSpaceUnit04, "land_fleet_04" }, + { CommandBarComponentId.MapSpaceUnit05, "land_fleet_05" }, + { CommandBarComponentId.MapSpaceUnit06, "land_fleet_06" }, + { CommandBarComponentId.MapSpaceUnit07, "land_fleet_07" }, + { CommandBarComponentId.MapSpaceUnit08, "land_fleet_08" }, + { CommandBarComponentId.MapSpaceUnit09, "land_fleet_09" }, + { CommandBarComponentId.MapSpaceUnit10, "land_fleet_10" }, + { CommandBarComponentId.MapSpaceUnit11, "land_fleet_11" }, + { CommandBarComponentId.MapSpaceUnit12, "land_fleet_12" }, + { CommandBarComponentId.MapSpaceUnit13, "land_fleet_13" }, + { CommandBarComponentId.MapSpaceUnit14, "land_fleet_14" }, + { CommandBarComponentId.MapSpaceUnit15, "land_fleet_15" }, + { CommandBarComponentId.MapSpaceUnit16, "land_fleet_16" }, + { CommandBarComponentId.MapSpaceUnit17, "land_fleet_17" }, + { CommandBarComponentId.MapSpaceUnit18, "land_fleet_18" }, + { CommandBarComponentId.MapSpaceUnit19, "land_fleet_19" }, + { CommandBarComponentId.MapBuildPad00, "build_pad00" }, + { CommandBarComponentId.MapBuildPad01, "build_pad01" }, + { CommandBarComponentId.MapBuildPad02, "build_pad02" }, + { CommandBarComponentId.MapBuildPad03, "build_pad03" }, + { CommandBarComponentId.MapBuildPad04, "build_pad04" }, + { CommandBarComponentId.MapBuildPad05, "build_pad05" }, + { CommandBarComponentId.MapBuildPad06, "build_pad06" }, + { CommandBarComponentId.MapBuildPad07, "build_pad07" }, + { CommandBarComponentId.MapBuildPad08, "build_pad08" }, + { CommandBarComponentId.MapBuildPad09, "build_pad09" }, + { CommandBarComponentId.MapBuildPad10, "build_pad10" }, + { CommandBarComponentId.MapBuildPad11, "build_pad11" }, + { CommandBarComponentId.MapBuildPad12, "build_pad12" }, + { CommandBarComponentId.MapBuildPad13, "build_pad13" }, + { CommandBarComponentId.MapBuildPad14, "build_pad14" }, + { CommandBarComponentId.MapBuildPad15, "build_pad15" }, + { CommandBarComponentId.MapBuildPad16, "build_pad16" }, + { CommandBarComponentId.MapBuildPad17, "build_pad17" }, + { CommandBarComponentId.MapBuildPad18, "build_pad18" }, + { CommandBarComponentId.MapBuildPad19, "build_pad19" }, + { CommandBarComponentId.MapBuildPad20, "build_pad20" }, + { CommandBarComponentId.MapBuildPad21, "build_pad21" }, + { CommandBarComponentId.MapBuildPad22, "build_pad22" }, + { CommandBarComponentId.MapBuildPad23, "build_pad23" }, + { CommandBarComponentId.MapBuildPad24, "build_pad24" }, + { CommandBarComponentId.MapBuildPad25, "build_pad25" }, + { CommandBarComponentId.MapBuildPad26, "build_pad26" }, + { CommandBarComponentId.MapBuildPad27, "build_pad27" }, + { CommandBarComponentId.MapBuildPad28, "build_pad28" }, + { CommandBarComponentId.MapBuildPad29, "build_pad29" }, + { CommandBarComponentId.MapBuildPad30, "build_pad30" }, + { CommandBarComponentId.MapBuildPad31, "build_pad31" }, + { CommandBarComponentId.MapBuildPad32, "build_pad32" }, + { CommandBarComponentId.MapReinforcePad00, "build_pad33" }, + { CommandBarComponentId.MapReinforcePad01, "build_pad34" }, + { CommandBarComponentId.MapReinforcePad02, "build_pad35" }, + { CommandBarComponentId.MapReinforcePad03, "build_pad36" }, + { CommandBarComponentId.MapReinforcePad04, "build_pad37" }, + { CommandBarComponentId.MapReinforcePad05, "build_pad38" }, + { CommandBarComponentId.MapReinforcePad06, "build_pad39" }, + { CommandBarComponentId.MapBunkerAndBuildPad00, "build_pad40" }, + { CommandBarComponentId.MapBunkerAndBuildPad01, "build_pad41" }, + { CommandBarComponentId.MapBunkerAndBuildPad02, "build_pad42" }, + { CommandBarComponentId.MapBunkerAndBuildPad03, "build_pad43" }, + { CommandBarComponentId.MapBunkerAndBuildPad04, "build_pad44" }, + { CommandBarComponentId.MapBunkerAndBuildPad05, "build_pad45" }, + { CommandBarComponentId.MapBunkerAndBuildPad06, "build_pad46" }, + { CommandBarComponentId.MapBunkerAndBuildPad07, "build_pad47" }, + { CommandBarComponentId.MapBunkerAndBuildPad08, "build_pad48" }, + { CommandBarComponentId.MapBunkerAndBuildPad09, "build_pad49" }, + { CommandBarComponentId.MapBunkerAndBuildPad10, "build_pad50" }, + { CommandBarComponentId.MapBunkerAndBuildPad11, "build_pad51" }, + { CommandBarComponentId.MapBunkerAndBuildPad12, "build_pad52" }, + { CommandBarComponentId.MapBunkerAndBuildPad13, "build_pad53" }, + { CommandBarComponentId.MapBunkerAndBuildPad14, "build_pad54" }, + { CommandBarComponentId.MapBunkerAndBuildPad15, "build_pad55" }, + { CommandBarComponentId.MapBunkerAndBuildPad16, "build_pad56" }, + { CommandBarComponentId.MapBunkerAndBuildPad17, "build_pad57" }, + { CommandBarComponentId.MapBunkerAndBuildPad18, "build_pad58" }, + { CommandBarComponentId.MapBunkerAndBuildPad19, "build_pad59" }, + { CommandBarComponentId.MapBunkerAndBuildPad20, "build_pad60" }, + { CommandBarComponentId.MapBunkerAndBuildPad21, "build_pad61" }, + { CommandBarComponentId.MapBunkerAndBuildPad22, "build_pad62" }, + { CommandBarComponentId.MapBunkerAndBuildPad23, "build_pad63" }, + { CommandBarComponentId.MapBunkerAndBuildPad24, "build_pad64" }, + { CommandBarComponentId.MapBunkerAndBuildPad25, "build_pad65" }, + { CommandBarComponentId.MapBunkerAndBuildPad26, "build_pad66" }, + { CommandBarComponentId.MapBunkerAndBuildPad27, "build_pad67" }, + { CommandBarComponentId.MapBunkerAndBuildPad28, "build_pad68" }, + { CommandBarComponentId.MapBunkerAndBuildPad29, "build_pad69" }, + { CommandBarComponentId.MapBunkerAndBuildPad30, "build_pad70" }, + { CommandBarComponentId.MapBunkerAndBuildPad31, "build_pad71" }, + { CommandBarComponentId.MapBunkerAndBuildPad32, "build_pad72" }, + { CommandBarComponentId.MapBunkerAndBuildPad33, "build_pad73" }, + { CommandBarComponentId.MapBunkerAndBuildPad34, "build_pad74" }, + { CommandBarComponentId.MapBunkerAndBuildPad35, "build_pad75" }, + { CommandBarComponentId.MapBunkerAndBuildPad36, "build_pad76" }, + { CommandBarComponentId.MapBunkerAndBuildPad37, "build_pad77" }, + { CommandBarComponentId.MapBunkerAndBuildPad38, "build_pad78" }, + { CommandBarComponentId.MapBunkerAndBuildPad39, "build_pad79" }, + { CommandBarComponentId.MapUnitPad00, "unit_pad00" }, + { CommandBarComponentId.MapUnitPad01, "unit_pad01" }, + { CommandBarComponentId.MapUnitPad02, "unit_pad02" }, + { CommandBarComponentId.MapUnitPad03, "unit_pad03" }, + { CommandBarComponentId.MapUnitPad04, "unit_pad04" }, + { CommandBarComponentId.MapUnitPad05, "unit_pad05" }, + { CommandBarComponentId.MapUnitPad06, "unit_pad06" }, + { CommandBarComponentId.MapUnitPad07, "unit_pad07" }, + { CommandBarComponentId.MapUnitPad08, "unit_pad08" }, + { CommandBarComponentId.MapUnitPad09, "unit_pad09" }, + { CommandBarComponentId.BribeDisplay, "bribe_display" }, + { CommandBarComponentId.BlackMarketShell, "market_frame" }, + { CommandBarComponentId.BlackMarketTitle, "market_title00" }, + { CommandBarComponentId.BlackMarketPlanet, "market_title01" }, + { CommandBarComponentId.BlackMarketTechLevelHeader, "market_title02" }, + { CommandBarComponentId.BlackMarketSlotHeader, "market_title03" }, + { CommandBarComponentId.BlackMarketCancel, "market_close" }, + { CommandBarComponentId.BlackMarketEncyclopedia, "i_blackmarket_encycl" }, + { CommandBarComponentId.BlackMarketTechLevel00, "market_tech_level00" }, + { CommandBarComponentId.BlackMarketTechLevel01, "market_tech_level01" }, + { CommandBarComponentId.BlackMarketTechLevel02, "market_tech_level02" }, + { CommandBarComponentId.BlackMarketTechLevel03, "market_tech_level03" }, + { CommandBarComponentId.BlackMarketTechLevel04, "market_tech_level04" }, + { CommandBarComponentId.BlackMarketSlot00, "market_00" }, + { CommandBarComponentId.BlackMarketSlot01, "market_01" }, + { CommandBarComponentId.BlackMarketSlot02, "market_02" }, + { CommandBarComponentId.BlackMarketSlot03, "market_03" }, + { CommandBarComponentId.BlackMarketSlot04, "market_04" }, + { CommandBarComponentId.BlackMarketSlotDescription00, "market_title04" }, + { CommandBarComponentId.BlackMarketSlotDescription01, "market_title06" }, + { CommandBarComponentId.BlackMarketSlotDescription02, "market_title08" }, + { CommandBarComponentId.BlackMarketSlotDescription03, "market_title10" }, + { CommandBarComponentId.BlackMarketSlotDescription04, "market_title12" }, + { CommandBarComponentId.BlackMarketSlotPrice00, "market_title05" }, + { CommandBarComponentId.BlackMarketSlotPrice01, "market_title07" }, + { CommandBarComponentId.BlackMarketSlotPrice02, "market_title09" }, + { CommandBarComponentId.BlackMarketSlotPrice03, "market_title11" }, + { CommandBarComponentId.BlackMarketSlotPrice04, "market_title13" }, + { CommandBarComponentId.SabotageShell, "bm_frame" }, + { CommandBarComponentId.SabotageTitle, "bm_title" }, + { CommandBarComponentId.SabotageTitle2, "bm_title01" }, + { CommandBarComponentId.SabotageTechLevel, "bm_tech_level" }, + { CommandBarComponentId.SabotageCancel, "bm_close" }, + { CommandBarComponentId.SabotageDescription, "bm_text_steal" }, + { CommandBarComponentId.SabotageSlot00, "bm_0000" }, + { CommandBarComponentId.SabotageSlot01, "bm_0001" }, + { CommandBarComponentId.SabotageSlot02, "bm_0100" }, + { CommandBarComponentId.SabotageSlot03, "bm_0101" }, + { CommandBarComponentId.SabotageSlot04, "bm_0200" }, + { CommandBarComponentId.SabotageSlot05, "bm_0201" }, + { CommandBarComponentId.SabotageSlot06, "bm_0300" }, + { CommandBarComponentId.SabotageSlot07, "bm_0301" }, + { CommandBarComponentId.SabotageSlot08, "bm_0400" }, + { CommandBarComponentId.SabotageSlot09, "bm_0401" }, + { CommandBarComponentId.SabotagePrice00, "bm_title_000" }, + { CommandBarComponentId.SabotagePrice01, "bm_title_001" }, + { CommandBarComponentId.SabotagePrice02, "bm_title_100" }, + { CommandBarComponentId.SabotagePrice03, "bm_title_101" }, + { CommandBarComponentId.SabotagePrice04, "bm_title_200" }, + { CommandBarComponentId.SabotagePrice05, "bm_title_201" }, + { CommandBarComponentId.SabotagePrice06, "bm_title_300" }, + { CommandBarComponentId.SabotagePrice07, "bm_title_301" }, + { CommandBarComponentId.SabotagePrice08, "bm_title_400" }, + { CommandBarComponentId.SabotagePrice09, "bm_title_401" }, + { CommandBarComponentId.SabotageName00, "bm_title_402" }, + { CommandBarComponentId.SabotageName01, "bm_title_403" }, + { CommandBarComponentId.SabotageName02, "bm_title_404" }, + { CommandBarComponentId.SabotageName03, "bm_title_405" }, + { CommandBarComponentId.SabotageName04, "bm_title_406" }, + { CommandBarComponentId.SabotageName05, "bm_title_407" }, + { CommandBarComponentId.SabotageName06, "bm_title_408" }, + { CommandBarComponentId.SabotageName07, "bm_title_409" }, + { CommandBarComponentId.SabotageName08, "bm_title_4010" }, + { CommandBarComponentId.SabotageName09, "bm_title_4011" }, + { CommandBarComponentId.PlanetaryBombardment, "pb_switch" }, + { CommandBarComponentId.PlanetaryBombardmentRecharge, "pb_switch_recharge" }, + { CommandBarComponentId.SuperLaser, "sl_switch" }, + { CommandBarComponentId.SuperLaserRecharge, "sl_switch_recharge" }, + { CommandBarComponentId.GenericFlytext, "generic_flytext" }, + { CommandBarComponentId.BribedIcon, "bribed_icon" }, + { CommandBarComponentId.SurfaceModIcon, "surface_mod_icon" }, + { CommandBarComponentId.RemoteBombIcon, "remote_bomb_icon" }, + { CommandBarComponentId.CorruptionShell, "corruption_shell" }, + { CommandBarComponentId.CorruptionTitle, "corrupt_planet_title00" }, + { CommandBarComponentId.CorruptionPlanetName, "corrupt_planet_title01" }, + { CommandBarComponentId.CorruptionEncyclopedia, "i_corrupt_encycl" }, + { CommandBarComponentId.CorruptionClose, "corrupt_close" }, + { CommandBarComponentId.CorruptionPlanetModel, "i_planet_image" }, + { CommandBarComponentId.CorruptionInfo00, "corrupt_planet_defiler_00" }, + { CommandBarComponentId.CorruptionInfo01, "corrupt_planet_defiler_01" }, + { CommandBarComponentId.CorruptionChoiceIcon0, "corrupt_button_icon_a" }, + { CommandBarComponentId.CorruptionChoiceIcon1, "corrupt_button_icon_b" }, + { CommandBarComponentId.CorruptionChoiceIcon2, "corrupt_button_icon_c" }, + { CommandBarComponentId.CorruptionChoiceCost0, "corrupt_cost_a" }, + { CommandBarComponentId.CorruptionChoiceCost1, "corrupt_cost_b" }, + { CommandBarComponentId.CorruptionChoiceCost2, "corrupt_cost_c" }, + { CommandBarComponentId.CorruptionChoiceTitle0, "corrupt_title_a" }, + { CommandBarComponentId.CorruptionChoiceTitle1, "corrupt_title_b" }, + { CommandBarComponentId.CorruptionChoiceTitle2, "corrupt_title_c" }, + { CommandBarComponentId.CorruptionChoiceBenefitText00, "corrupt_text00a" }, + { CommandBarComponentId.CorruptionChoiceBenefitText01, "corrupt_text01a" }, + { CommandBarComponentId.CorruptionChoiceBenefitText02, "corrupt_text02a" }, + { CommandBarComponentId.CorruptionChoiceBenefitText03, "corrupt_text03a" }, + { CommandBarComponentId.CorruptionChoiceBenefitText10, "corrupt_text00b" }, + { CommandBarComponentId.CorruptionChoiceBenefitText11, "corrupt_text01b" }, + { CommandBarComponentId.CorruptionChoiceBenefitText12, "corrupt_text02b" }, + { CommandBarComponentId.CorruptionChoiceBenefitText13, "corrupt_text03b" }, + { CommandBarComponentId.CorruptionChoiceBenefitText20, "corrupt_text00c" }, + { CommandBarComponentId.CorruptionChoiceBenefitText21, "corrupt_text01c" }, + { CommandBarComponentId.CorruptionChoiceBenefitText22, "corrupt_text02c" }, + { CommandBarComponentId.CorruptionChoiceBenefitText23, "corrupt_text03c" }, + { CommandBarComponentId.CorruptionChoiceRequirement0, "corrupt_text04a" }, + { CommandBarComponentId.CorruptionChoiceRequirement1, "corrupt_text04b" }, + { CommandBarComponentId.CorruptionChoiceRequirement2, "corrupt_text04c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0001, "corrupt_icon_00a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0002, "corrupt_icon_01a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0003, "corrupt_icon_02a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0000, "corrupt_icon_03a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0100, "corrupt_icon_04a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0101, "corrupt_icon_05a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0102, "corrupt_icon_06a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon0103, "corrupt_icon_07a" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1000, "corrupt_icon_00b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1001, "corrupt_icon_01b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1002, "corrupt_icon_02b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1003, "corrupt_icon_03b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1100, "corrupt_icon_04b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1101, "corrupt_icon_05b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1102, "corrupt_icon_06b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon1103, "corrupt_icon_07b" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2000, "corrupt_icon_00c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2001, "corrupt_icon_01c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2002, "corrupt_icon_02c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2003, "corrupt_icon_03c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2100, "corrupt_icon_04c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2101, "corrupt_icon_05c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2102, "corrupt_icon_06c" }, + { CommandBarComponentId.CorruptionChoiceRequirementIcon2103, "corrupt_icon_07c" }, + { CommandBarComponentId.HackSuperWeaponShell, "hack_shell" }, + { CommandBarComponentId.HackSuperWeaponTitle, "hack_title01" }, + { CommandBarComponentId.HackSuperWeaponText00, "hack_text_00" }, + { CommandBarComponentId.HackSuperWeaponText01, "hack_text_01" }, + { CommandBarComponentId.HackSuperWeaponText02, "hack_text_02" }, + { CommandBarComponentId.HackSuperWeaponText03, "hack_text_03" }, + { CommandBarComponentId.HackSuperWeaponText04, "hack_text_04" }, + { CommandBarComponentId.HackSuperWeaponText05, "hack_text_05" }, + { CommandBarComponentId.HackSuperWeaponText06, "hack_text_06" }, + { CommandBarComponentId.HackSuperWeaponCost, "hack_cost" }, + { CommandBarComponentId.HackSuperWeaponAccept, "hack_hack" }, + { CommandBarComponentId.HackSuperWeaponCancel, "hack_close" }, + }; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs index fb06793..057745e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Xml/CommandBarComponentData.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Numerics; using PG.Commons.Hashing; +using PG.Commons.Numerics; using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.XML; @@ -14,6 +15,7 @@ public sealed class CommandBarComponentData(string name, Crc32 crc, XmlLocationI public const float DefaultBlinkRate = 0.2f; public const int DefaultBaseLayer = 2; public static readonly Vector2 DefaultOffsetWidescreenValue = new(9.9999998e17f, 9.9999998e17f); + public static readonly Vector4Int WhiteColor = new(255, 255, 255, 255); public IReadOnlyList SelectedTextureNames { get; internal set; } = []; public IReadOnlyList BlankTextureNames { get; internal set; } = []; @@ -121,6 +123,11 @@ public sealed class CommandBarComponentData(string name, Crc32 crc, XmlLocationI public Vector2 OverlayOffset { get; internal set; } public Vector2 Overlay2Offset { get; internal set; } + public Vector4Int? Color { get; internal set; } = WhiteColor; + public Vector4Int? TextColor { get; internal set; } + public Vector4Int? TextColor2 { get; internal set; } + public Vector4Int? MaxBarColor { get; internal set; } = WhiteColor; + internal override void CoerceValues() { base.CoerceValues(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs deleted file mode 100644 index 9027396..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporter.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; - -public abstract class DatabaseErrorReporter : IDatabaseErrorReporter -{ - public virtual void Report(XmlError error) - { - } - - public virtual void Report(InitializationError error) - { - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs deleted file mode 100644 index c404ac0..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/IDatabaseErrorReporter.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; - -public interface IDatabaseErrorReporter -{ - void Report(XmlError error); - - void Report(InitializationError error); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs index 774ce8b..3028fec 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; namespace PG.StarWarsGame.Engine.Database; @@ -15,7 +15,7 @@ public Task InitializeGameAsync( { var repoFactory = serviceProvider.GetRequiredService(); - using var errorListenerWrapper = new DatabaseErrorReporterWrapper(gameInitializationOptions.ErrorListener, serviceProvider); + using var errorListenerWrapper = new GameErrorReporterWrapper(gameInitializationOptions.GameErrorReporter, serviceProvider); var repository = repoFactory.Create(gameInitializationOptions.TargetEngineType, gameInitializationOptions.Locations, errorListenerWrapper); var gameInitializer = new GameInitializer(repository, gameInitializationOptions.CancelOnError, serviceProvider); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs index 5c82c01..e00e0d3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs @@ -1,4 +1,4 @@ -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; namespace PG.StarWarsGame.Engine.Database; @@ -10,5 +10,5 @@ public class GameInitializationOptions public bool CancelOnError { get; init; } - public IDatabaseErrorReporter? ErrorListener { get; init; } + public IGameErrorReporter? GameErrorReporter { get; init; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index f8e9f30..0868a80 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.CommandBar; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO.Repositories; @@ -18,7 +18,7 @@ internal class GameInitializer(GameRepository repository, bool cancelOnError, IS private CancellationTokenSource? _cancellationTokenSource; - public async Task InitializeAsync(DatabaseErrorReporterWrapper errorReporter, CancellationToken token) + public async Task InitializeAsync(GameErrorReporterWrapper errorReporter, CancellationToken token) { _logger?.LogInformation("Initializing Game Database..."); errorReporter.InitializationError += OnInitializationError; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs index bec742c..33696b3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs @@ -8,12 +8,12 @@ using Microsoft.Extensions.Logging; using PG.Commons.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; namespace PG.StarWarsGame.Engine.Database; -internal abstract class GameManagerBase(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal abstract class GameManagerBase(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameManager { protected readonly ValueListDictionary NamedEntries = new(); @@ -38,11 +38,11 @@ internal abstract class GameManagerBase protected readonly IFileSystem FileSystem; protected readonly ILogger? Logger; - protected readonly DatabaseErrorReporterWrapper ErrorReporter; + protected readonly GameErrorReporterWrapper ErrorReporter; public bool IsInitialized => _initialized; - protected GameManagerBase(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + protected GameManagerBase(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) { GameRepository = repository ?? throw new ArgumentNullException(nameof(repository)); ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs new file mode 100644 index 0000000..8c1d082 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace PG.StarWarsGame.Engine.ErrorReporting; + +public sealed class EngineAssert(string source, string message) +{ + public string Message { get; } = message ?? throw new ArgumentNullException(nameof(message)); + + public string Source { get; } = source ?? throw new ArgumentNullException(nameof(source)); + + public static EngineAssert CreateCapture(string message) + { + var stackTrace = new StackTrace(); + var frame = stackTrace.GetFrame(1); + if (frame is null) + return new EngineAssert("UNKNOWN SOURCE", message); + var method = frame.GetMethod(); + var methodName = CreateMethodName(method); + return new EngineAssert(methodName, message); + } + + private static string CreateMethodName(MethodBase method) + { + var methodName = new StringBuilder(); + if (method.DeclaringType is not null) + methodName.Append(method.DeclaringType.FullName); + methodName.Append("::"); + methodName.Append(method.Name); + methodName.Append('('); + methodName.Append(string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name))); + methodName.Append(')'); + return methodName.ToString(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs new file mode 100644 index 0000000..67b8d49 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs @@ -0,0 +1,16 @@ +namespace PG.StarWarsGame.Engine.ErrorReporting; + +public abstract class GameErrorReporter : IGameErrorReporter +{ + public virtual void Report(XmlError error) + { + } + + public virtual void Report(InitializationError error) + { + } + + public virtual void Assert(EngineAssert assert) + { + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs similarity index 55% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs index fc42e69..1946691 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/DatabaseErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs @@ -2,40 +2,46 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; +namespace PG.StarWarsGame.Engine.ErrorReporting; -internal sealed class DatabaseErrorReporterWrapper : XmlErrorReporter, IDatabaseErrorReporter +internal sealed class GameErrorReporterWrapper : XmlErrorReporter, IGameErrorReporter { internal event EventHandler? InitializationError; - private readonly IDatabaseErrorReporter? _errorListener; + private readonly IGameErrorReporter? _errorReporter; private readonly ILogger? _logger; - public DatabaseErrorReporterWrapper(IDatabaseErrorReporter? errorListener, IServiceProvider serviceProvider) + public GameErrorReporterWrapper(IGameErrorReporter? errorReporter, IServiceProvider serviceProvider) { - if (errorListener is null) + if (errorReporter is null) return; - _errorListener = errorListener; + _errorReporter = errorReporter; _logger = serviceProvider.GetService()?.CreateLogger(GetType()); } public void Report(XmlError error) { - _errorListener?.Report(error); + _errorReporter?.Report(error); } public void Report(InitializationError error) { InitializationError?.Invoke(this, error); - if (_errorListener is null) + if (_errorReporter is null) return; - _errorListener.Report(error); + _errorReporter.Report(error); } - public override void Report(string parser, XmlParseErrorEventArgs error) + public void Assert(EngineAssert assert) { - if (_errorListener is null) + _errorReporter?.Assert(assert); + } + + public override void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) + { + if (_errorReporter is null) return; _logger?.LogWarning($"Xml parser '{parser}' reported error: {error}"); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs new file mode 100644 index 0000000..1a4484c --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs @@ -0,0 +1,10 @@ +namespace PG.StarWarsGame.Engine.ErrorReporting; + +public interface IGameErrorReporter +{ + void Report(XmlError error); + + void Report(InitializationError error); + + void Assert(EngineAssert assert); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/InitializationError.cs similarity index 70% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/InitializationError.cs index 55262ea..c7f8e27 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/InitializationError.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/InitializationError.cs @@ -1,4 +1,4 @@ -namespace PG.StarWarsGame.Engine.Database.ErrorReporting; +namespace PG.StarWarsGame.Engine.ErrorReporting; public sealed class InitializationError { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs similarity index 70% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs index 60b16bd..83e6e9f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/ErrorReporting/XmlError.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/XmlError.cs @@ -1,14 +1,15 @@ 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.Database.ErrorReporting; +namespace PG.StarWarsGame.Engine.ErrorReporting; public sealed class XmlError { public required XmlLocationInfo FileLocation { get; init; } - public required string Parser { get; init; } + public required IPetroglyphXmlParser Parser { get; init; } public XElement? Element { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs index a170f9a..4368f48 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs @@ -2,12 +2,12 @@ using System.Threading; using System.Threading.Tasks; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; namespace PG.StarWarsGame.Engine.GameConstants; -internal class GameConstants(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class GameConstants(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameConstants { protected override Task InitializeCoreAsync(CancellationToken token) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index dd716c4..6147838 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -1,27 +1,25 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine.GameObjects; -internal class GameObjectTypeGameManager(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class GameObjectTypeGameManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameObjectTypeGameManager { protected override async Task InitializeCoreAsync(CancellationToken token) { Logger?.LogInformation("Parsing GameObjects..."); - var contentParser = ServiceProvider.GetRequiredService(); + var contentParser = new XmlContainerContentParser(ServiceProvider, ErrorReporter); await Task.Run(() => contentParser.ParseEntriesFromFileListXml( "DATA\\XML\\GAMEOBJECTFILES.XML", - ErrorReporter, GameRepository, ".\\DATA\\XML", NamedEntries, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index 66522e0..55e598b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Files.MTD.Binary; @@ -14,7 +14,7 @@ namespace PG.StarWarsGame.Engine.GuiDialog; -internal partial class GuiDialogGameManager(GameRepository repository, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal partial class GuiDialogGameManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGuiDialogManager { private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index d023dca..a186e5a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using AnakinRaW.CommonUtilities.Collections; using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Engine.Xml.Parsers.File; using PG.StarWarsGame.Engine.Xml.Tags; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs index a02e6e1..0be4daf 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.Utilities; using PG.StarWarsGame.Files.MEG.Files; @@ -12,7 +12,7 @@ internal class FocGameRepository : GameRepository { public override GameEngineType EngineType => GameEngineType.Foc; - public FocGameRepository(GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + public FocGameRepository(GameLocations gameLocations, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(gameLocations, errorReporter, serviceProvider) { if (gameLocations == null) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index 6d24048..0c1dd05 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -7,9 +7,8 @@ using Microsoft.Extensions.Logging; using PG.Commons.Hashing; using PG.Commons.Services; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.Localization; -using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.MEG.Data.Archives; using PG.StarWarsGame.Files.MEG.Data.Entries; using PG.StarWarsGame.Files.MEG.Data.EntryLocations; @@ -28,7 +27,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository private readonly ICrc32HashingService _crc32HashingService; private readonly IVirtualMegArchiveBuilder _virtualMegBuilder; private readonly IGameLanguageManagerProvider _languageManagerProvider; - private readonly DatabaseErrorReporterWrapper _errorReporter; + private readonly GameErrorReporterWrapper _errorReporter; protected readonly string GameDirectory; @@ -47,7 +46,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository private readonly List _loadedMegFiles = new(); protected IVirtualMegArchive? MasterMegArchive { get; private set; } - protected GameRepository(GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(serviceProvider) + protected GameRepository(GameLocations gameLocations, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(serviceProvider) { if (gameLocations == null) throw new ArgumentNullException(nameof(gameLocations)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs index 802c51b..564178c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs @@ -1,11 +1,11 @@ using System; -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; namespace PG.StarWarsGame.Engine.IO.Repositories; internal sealed class GameRepositoryFactory(IServiceProvider serviceProvider) : IGameRepositoryFactory { - public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter) + public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, GameErrorReporterWrapper errorReporter) { if (engineType == GameEngineType.Eaw) throw new NotImplementedException("Empire at War is currently not supported."); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs index ae54c37..c2322ee 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs @@ -1,8 +1,8 @@ -using PG.StarWarsGame.Engine.Database.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; namespace PG.StarWarsGame.Engine.IO.Repositories; internal interface IGameRepositoryFactory { - GameRepository Create(GameEngineType engineType, GameLocations gameLocations, DatabaseErrorReporterWrapper errorReporter); + GameRepository Create(GameEngineType engineType, GameLocations gameLocations, GameErrorReporterWrapper errorReporter); } \ 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 e43a693..5c7509e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs @@ -3,7 +3,6 @@ using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml; -using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine; @@ -17,8 +16,5 @@ public static void ContributeServices(IServiceCollection serviceCollection) serviceCollection.AddSingleton(sp => new PetroglyphXmlFileParserFactory(sp)); serviceCollection.AddSingleton(sp => new GameDatabaseService(sp)); - - // Transients - serviceCollection.AddTransient(sp => new XmlContainerContentParser(sp)); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs index 183441b..52bfee6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs @@ -24,11 +24,15 @@ public RgbaColor() : this(1.0f, 1.0f, 1.0f, 1.0f) { } - public RgbaColor(Vector4Int rgbaVector) : this(rgbaVector.First, rgbaVector.Second, rgbaVector.Third, rgbaVector.Fourth) + public RgbaColor(Vector4Int rgbaVector) : this( + unchecked((byte)rgbaVector.First), + unchecked ((byte)rgbaVector.Second), + unchecked ((byte)rgbaVector.Third), + unchecked ((byte)rgbaVector.Fourth)) { } - public RgbaColor(int r, int g, int b, int a) : this(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f) + public RgbaColor(uint r, uint g, uint b, uint a) : this(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f) { } @@ -60,4 +64,23 @@ public override int GetHashCode() { return HashCode.Combine(_r, _g, _b, _a); } -} \ No newline at end of file +} + +public enum PrimRenderMode +{ + PrimOpaque = 0x0, + PrimAdditive = 0x1, + PrimAlpha = 0x2, + PrimModulate = 0x3, + PrimDepthspriteAdditive = 0x4, + PrimDepthspriteAlpha = 0x5, + PrimDepthspriteModulate = 0x6, + PrimDiffuseAlpha = 0x7, + PrimStencilDarken = 0x8, + PrimStencilDarkenBlur = 0x9, + PrimHeat = 0xa, + PrimParticleBumpAlpha = 0xb, + PrimDecalBumpAlpha = 0xc, + PrimAlphaScanlines = 0xd, + PrimCount = 0xe +}; \ 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/Data/CommandBarComponentParser.cs index 1db211c..c6d2e03 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -7,7 +7,7 @@ using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -17,10 +17,10 @@ public sealed class CommandBarComponentParser( IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { - public override CommandBarComponentData Parse(XElement element, out Crc32 upperNameCrc) + public override CommandBarComponentData Parse(XElement element, out Crc32 crc32) { - var name = GetXmlObjectName(element, out upperNameCrc); - var component = new CommandBarComponentData(name, upperNameCrc, XmlLocationInfo.FromElement(element)); + var name = GetXmlObjectName(element, out crc32, true); + var component = new CommandBarComponentData(name, crc32, XmlLocationInfo.FromElement(element)); Parse(component, element, default); component.CoerceValues(); return component; @@ -334,6 +334,18 @@ protected override bool ParseTag(XElement tag, CommandBarComponentData component 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; } 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 8035d7c..4f445a7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/GameObjectParser.cs @@ -5,7 +5,7 @@ using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -31,12 +31,12 @@ public sealed class GameObjectParser( IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { - public override GameObject Parse(XElement element, out Crc32 upperNameCrc) + public override GameObject Parse(XElement element, out Crc32 crc32) { - var name = GetXmlObjectName(element, out upperNameCrc); + var name = GetXmlObjectName(element, out crc32, true); var type = GetTagName(element); var objectType = EstimateType(type); - var gameObject = new GameObject(type, name, upperNameCrc, objectType, XmlLocationInfo.FromElement(element)); + var gameObject = new GameObject(type, name, crc32, objectType, XmlLocationInfo.FromElement(element)); Parse(gameObject, element, default); 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 7a94fe3..4e3a5cb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/SfxEventParser.cs @@ -7,7 +7,7 @@ using PG.StarWarsGame.Engine.Xml.Tags; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.Xml.Parsers.Data; @@ -17,10 +17,10 @@ public sealed class SfxEventParser( IXmlParserErrorReporter? errorReporter = null) : XmlObjectParser(parsedElements, serviceProvider, errorReporter) { - public override SfxEvent Parse(XElement element, out Crc32 upperNameCrc) + public override SfxEvent Parse(XElement element, out Crc32 crc32) { - var name = GetXmlObjectName(element, out upperNameCrc); - var sfxEvent = new SfxEvent(name, upperNameCrc, XmlLocationInfo.FromElement(element)); + 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(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs index 53cdb88..a32ff09 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/File/CommandBarComponentFileParser.cs @@ -24,8 +24,8 @@ protected override void Parse(XElement element, IValueListDictionary? XmlParseError; - - void ParseEntriesFromFileListXml( - string xmlFile, - IXmlParserErrorReporter reporter, - IGameRepository gameRepository, - string lookupPath, - ValueListDictionary entries, - Action? onFileParseAction = null) where T : notnull; -} \ 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 index 61686cb..ff1477c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -14,15 +14,24 @@ namespace PG.StarWarsGame.Engine.Xml.Parsers; -internal sealed class XmlContainerContentParser(IServiceProvider serviceProvider) : ServiceBase(serviceProvider), IXmlContainerContentParser +internal sealed class XmlContainerContentParser : ServiceBase, IPetroglyphXmlParser { public event EventHandler? XmlParseError; - private readonly IPetroglyphXmlFileParserFactory _fileParserFactory = serviceProvider.GetRequiredService(); + 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, - IXmlParserErrorReporter reporter, IGameRepository gameRepository, string lookupPath, ValueListDictionary entries, @@ -33,7 +42,7 @@ public void ParseEntriesFromFileListXml( using var containerStream = gameRepository.TryOpenFile(xmlFile); if (containerStream == null) { - reporter.Report(ToString(), XmlParseErrorEventArgs.FromMissingFile(xmlFile)); + _reporter?.Report(this, XmlParseErrorEventArgs.FromMissingFile(xmlFile)); Logger.LogWarning($"Could not find XML file '{xmlFile}'"); var args = new XmlContainerParserErrorEventArgs(xmlFile, null, true) @@ -49,7 +58,7 @@ public void ParseEntriesFromFileListXml( try { - var containerParser = new XmlFileListParser(Services, reporter); + var containerParser = new XmlFileListParser(Services, _reporter); container = containerParser.ParseFile(containerStream); if (container is null) throw new XmlException($"Unable to parse XML container file '{xmlFile}'."); @@ -66,10 +75,9 @@ public void ParseEntriesFromFileListXml( } - var xmlFiles = container.Files.Select(x => FileSystem.Path.Combine(lookupPath, x)).ToList(); - var parser = _fileParserFactory.CreateFileParser(reporter); + var parser = _fileParserFactory.CreateFileParser(_reporter); foreach (var file in xmlFiles) { @@ -80,7 +88,7 @@ public void ParseEntriesFromFileListXml( if (fileStream is null) { - reporter.Report(parser.ToString(), XmlParseErrorEventArgs.FromMissingFile(file)); + _reporter?.Report(parser, XmlParseErrorEventArgs.FromMissingFile(file)); Logger.LogWarning($"Could not find XML file '{file}'"); var args = new XmlContainerParserErrorEventArgs(file); @@ -99,7 +107,7 @@ public void ParseEntriesFromFileListXml( } catch (XmlException e) { - reporter.Report(parser.ToString(), new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); + _reporter?.Report(parser, new XmlParseErrorEventArgs(new XmlLocationInfo(file, 0), XmlParseErrorKind.Unknown, e.Message)); var args = new XmlContainerParserErrorEventArgs(file, e); XmlParseError?.Invoke(this, args); @@ -109,9 +117,4 @@ public void ParseEntriesFromFileListXml( } } } - - public override string ToString() - { - return GetType().FullName; - } } \ 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 345a6c5..fead02d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlObjectParser.cs @@ -44,7 +44,7 @@ public abstract class XmlObjectParser( protected ICrc32HashingService HashingService { get; } = serviceProvider.GetRequiredService(); - public abstract TObject Parse(XElement element, out Crc32 upperNameCrc); + public abstract TObject Parse(XElement element, out Crc32 crc32); protected void Parse(TObject xmlObject, XElement element, in TParseState state) { @@ -61,11 +61,14 @@ protected void Parse(TObject xmlObject, XElement element, in TParseState state) protected abstract bool ParseTag(XElement tag, TObject xmlObject, in TParseState parseState); - protected string GetXmlObjectName(XElement element, out Crc32 upperNameCrc32) + protected string GetXmlObjectName(XElement element, out Crc32 crc32, bool uppercaseName) { GetNameAttributeValue(element, out var name); - upperNameCrc32 = HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.DefaultPGEncoding); - if (upperNameCrc32 == default) + crc32 = uppercaseName + ? HashingService.GetCrc32Upper(name.AsSpan(), PGConstants.DefaultPGEncoding) + : HashingService.GetCrc32(name.AsSpan(), PGConstants.DefaultPGEncoding); + + if (crc32 == default) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, $"Name for XmlObject cannot be empty.")); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs index 86b3356..6bb14c1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/IXmlParserErrorReporter.cs @@ -1,6 +1,8 @@ -namespace PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; + +namespace PG.StarWarsGame.Files.XML.ErrorHandling; public interface IXmlParserErrorReporter { - void Report(string parser, XmlParseErrorEventArgs error); + void Report(IPetroglyphXmlParser 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 7b254be..39de0e6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/PrimitiveXmlErrorReporter.cs @@ -1,4 +1,5 @@ using System; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Files.XML.ErrorHandling; @@ -14,7 +15,7 @@ private PrimitiveXmlErrorReporter() { } - public void Report(string parser, XmlParseErrorEventArgs error) + public void Report(IPetroglyphXmlParser 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 7001a03..69962e3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorEventHandler.cs @@ -1,3 +1,5 @@ -namespace PG.StarWarsGame.Files.XML.ErrorHandling; +using PG.StarWarsGame.Files.XML.Parsers; -public delegate void XmlErrorEventHandler(string parser, XmlParseErrorEventArgs error); \ No newline at end of file +namespace PG.StarWarsGame.Files.XML.ErrorHandling; + +public delegate void XmlErrorEventHandler(IPetroglyphXmlParser 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 5e5f9a5..8234729 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlErrorReporter.cs @@ -1,4 +1,5 @@ using AnakinRaW.CommonUtilities; +using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Files.XML.ErrorHandling; @@ -11,7 +12,7 @@ public XmlErrorReporter() PrimitiveXmlErrorReporter.Instance.XmlParseError += OnPrimitiveError; } - public virtual void Report(string parser, XmlParseErrorEventArgs error) + public virtual void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) { XmlParseError?.Invoke(parser, error); } @@ -21,7 +22,7 @@ protected override void DisposeResources() PrimitiveXmlErrorReporter.Instance.XmlParseError -= OnPrimitiveError; } - private void OnPrimitiveError(string parser, XmlParseErrorEventArgs error) + private void OnPrimitiveError(IPetroglyphXmlParser parser, XmlParseErrorEventArgs error) { Report(parser, error); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs index 8465c2f..aa3f8d2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/ErrorHandling/XmlParseErrorKind.cs @@ -20,11 +20,11 @@ public enum XmlParseErrorKind /// InvalidValue = 3, /// - /// A tag's value is has an invalid syntax. + /// A tag's value has an invalid syntax. /// MalformedValue = 4, /// - /// The value is too long + /// The tag or value is too long. /// TooLongData = 5, /// @@ -46,5 +46,9 @@ public enum XmlParseErrorKind /// /// The XML element contains an unsupported tag. /// - UnknownNode = 10 + UnknownNode = 10, + /// + /// The XML tag name is null or empty. + /// + EmptyNodeName } \ 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 index e63aa76..14a8aa1 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 where T : notnull +public interface IPetroglyphXmlElementParser : IPetroglyphXmlParser where T : notnull { T Parse(XElement element); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileContainerParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs similarity index 68% rename from src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/IPetroglyphXmlFileContainerParser.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlFileContainerParser.cs index 62be16b..f42a9a9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/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 where T : notnull +public interface IPetroglyphXmlFileContainerParser : IPetroglyphXmlParser where T : notnull { void ParseFile(Stream xmlStream, IValueListDictionary 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 327d6d2..5f9c4b4 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 where T : notnull +public interface IPetroglyphXmlFileParser : IPetroglyphXmlParser 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/IPetroglyphXmlParser.cs new file mode 100644 index 0000000..fdf5fd5 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/IPetroglyphXmlParser.cs @@ -0,0 +1,6 @@ +namespace PG.StarWarsGame.Files.XML.Parsers; + +public interface IPetroglyphXmlParser +{ + 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 8d4c02d..7395eb6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Base/PetroglyphXmlParserBase.cs @@ -4,13 +4,21 @@ namespace PG.StarWarsGame.Files.XML.Parsers; -public abstract class PetroglyphXmlParserBase(IXmlParserErrorReporter? errorReporter) +public abstract class PetroglyphXmlParserBase : IPetroglyphXmlParser { - protected readonly IXmlParserErrorReporter? ErrorReporter = errorReporter; + protected readonly IXmlParserErrorReporter? ErrorReporter; - public sealed override string ToString() + public string Name { get; } + + public override string ToString() + { + return Name; + } + + protected PetroglyphXmlParserBase(IXmlParserErrorReporter? errorReporter) { - return GetType().FullName!; + Name = GetType().FullName!; + ErrorReporter = errorReporter; } protected string GetTagName(XElement element) @@ -48,6 +56,6 @@ protected bool GetAttributeValue(XElement element, string attribute, out string? protected virtual void OnParseError(XmlParseErrorEventArgs error) { - ErrorReporter?.Report(ToString(), error); + ErrorReporter?.Report(this, error); } } \ No newline at end of file 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 5597e5e..1d2a5b3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/CommaSeparatedStringKeyValueListParser.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Xml.Linq; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; // Used e.g, by // Format: Key, Value, Key, Value @@ -15,7 +15,9 @@ private CommaSeparatedStringKeyValueListParser() { } - public override IList<(string key, string value)> Parse(XElement element) + private protected override IList<(string key, string value)> DefaultValue => []; + + protected internal override IList<(string key, string value)> ParseCore(string 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 59e6edf..0fd6fc1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphPrimitiveXmlParser.cs @@ -1,10 +1,30 @@ -using PG.StarWarsGame.Files.XML.ErrorHandling; +using System.Xml.Linq; +using PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public abstract class PetroglyphPrimitiveXmlParser : PetroglyphXmlElementParser where T : notnull { + private protected abstract T DefaultValue { get; } + private protected PetroglyphPrimitiveXmlParser() : base(PrimitiveXmlErrorReporter.Instance) { } + + 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.")); + 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.Trim(); + return value.Length == 0 ? DefaultValue : ParseCore(value, element); + } + + protected internal abstract T ParseCore(string 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 4dc6dd8..8672d1b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlBooleanParser.cs @@ -1,7 +1,6 @@ -using System; -using System.Xml.Linq; +using System.Xml.Linq; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlBooleanParser : PetroglyphPrimitiveXmlParser { @@ -11,22 +10,15 @@ private PetroglyphXmlBooleanParser() { } - public override bool Parse(XElement element) - { - var valueSpan = element.Value.AsSpan(); - var trimmed = valueSpan.Trim(); - - if (trimmed.Length == 0) - return false; + private protected override bool DefaultValue => false; + protected internal override bool ParseCore(string 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... - if (trimmed.Length == 1 && trimmed[0] == '1') - return true; - - if (trimmed[0] is 'y' or 'Y' or 't' or 'T') + if (trimmedValue.Length == 1 && trimmedValue[0] == '1') return true; - return false; + return trimmedValue[0] is 'y' or 'Y' or 't' or 'T'; } } \ No newline at end of file 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 30fb335..aa7ba4b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlByteParser.cs @@ -1,7 +1,7 @@ using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlByteParser : PetroglyphPrimitiveXmlParser { @@ -11,15 +11,17 @@ private PetroglyphXmlByteParser() { } - public override byte Parse(XElement element) + private protected override byte DefaultValue => 0; + + protected internal override byte ParseCore(string trimmedValue, XElement element) { - var intValue = PetroglyphXmlIntegerParser.Instance.Parse(element); + var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); var asByte = (byte)intValue; if (intValue != asByte) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, - $"Expected a byte value (0 - 255) but got value '{intValue}'.")); + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + $"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 de2f33c..d3b4e85 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlFloatParser.cs @@ -3,32 +3,16 @@ using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlFloatParser : PetroglyphPrimitiveXmlParser { public static readonly PetroglyphXmlFloatParser Instance = new(); - private PetroglyphXmlFloatParser() - { - } + private protected override float DefaultValue => 0.0f; - public float Parse(string value, XElement element) + private PetroglyphXmlFloatParser() { - // The engine always loads FP numbers a long double and then converts that result to float - if (!double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var doubleValue)) - { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, - $"Expected double but got value '{value}'.")); - return 0.0f; - } - - return (float)doubleValue; - } - - public override float Parse(XElement element) - { - return Parse(element.Value, element); } public float ParseAtLeast(XElement element, float minValue) @@ -43,4 +27,17 @@ public float ParseAtLeast(XElement element, float minValue) return corrected; } + + protected internal override float ParseCore(string 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)) + { + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, + $"Expected double but got value '{trimmedValue}'.")); + return 0.0f; + } + + return (float)doubleValue; + } } \ No newline at end of file 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 caffc2d..fd0c0ec 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlIntegerParser.cs @@ -2,26 +2,27 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Utilities; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlIntegerParser : PetroglyphPrimitiveXmlParser { public static readonly PetroglyphXmlIntegerParser Instance = new(); + private protected override int DefaultValue => 0; + private PetroglyphXmlIntegerParser() { } - public override int Parse(XElement element) + protected internal override int ParseCore(string 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(element.Value, out var i)) + if (!int.TryParse(trimmedValue, out var i)) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.MalformedValue, - $"Expected integer but got '{element.Value}'.")); + $"Expected integer but got '{trimmedValue}'.")); 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 8710e35..d06f660 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlLooseStringListParser.cs @@ -3,7 +3,7 @@ using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlLooseStringListParser : PetroglyphPrimitiveXmlParser> { @@ -19,27 +19,22 @@ public sealed class PetroglyphXmlLooseStringListParser : PetroglyphPrimitiveXmlP public static readonly PetroglyphXmlLooseStringListParser Instance = new(); + private protected override IList DefaultValue => []; + private PetroglyphXmlLooseStringListParser() { } - public override IList Parse(XElement element) + protected internal override IList ParseCore(string trimmedValue, XElement element) { - var trimmedValued = element.Value.Trim(); - - if (trimmedValued.Length == 0) - return Array.Empty(); - - if (trimmedValued.Length > 0x2000) + if (trimmedValue.Length > 0x2000) { OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, - $"Input value is too long '{trimmedValued.Length}' at {XmlLocationInfo.FromElement(element)}")); - - return Array.Empty(); + $"Input value is too long '{trimmedValue.Length}' at {XmlLocationInfo.FromElement(element)}")); + return DefaultValue; } - var entries = trimmedValued.Split(Separators, StringSplitOptions.RemoveEmptyEntries); - + var entries = trimmedValue.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 3090f16..ede64a3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlMax100ByteParser.cs @@ -2,19 +2,21 @@ using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Utilities; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +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() { } - public override byte Parse(XElement element) + protected internal override byte ParseCore(string trimmedValue, XElement element) { - var intValue = PetroglyphXmlIntegerParser.Instance.Parse(element); + var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); if (intValue > 100) intValue = 100; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs new file mode 100644 index 0000000..1ecf10a --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlRgbaColorParser.cs @@ -0,0 +1,34 @@ +using System; +using System.Xml.Linq; +using PG.Commons.Numerics; + +namespace PG.StarWarsGame.Files.XML.Parsers; + +public sealed class PetroglyphXmlRgbaColorParser : PetroglyphPrimitiveXmlParser +{ + public static readonly PetroglyphXmlRgbaColorParser Instance = new(); + + private protected override Vector4Int DefaultValue => default; + + private PetroglyphXmlRgbaColorParser() + { + } + + protected internal override Vector4Int ParseCore(string trimmedValue, XElement element) + { + var values = PetroglyphXmlLooseStringListParser.Instance.ParseCore(trimmedValue, element); + + if (values.Count == 0) + return DefaultValue; + + Span intValues = stackalloc int[4] { 0, 0, 0, 0 }; + for (var i = 0; i < intValues.Length; i++) + { + if (values.Count <= i) + break; + intValues[i] = PetroglyphXmlIntegerParser.Instance.ParseCore(values[i], element); + } + + return new Vector4Int(intValues); + } +} \ 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 f31745f..dcca294 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlStringParser.cs @@ -1,6 +1,6 @@ using System.Xml.Linq; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlStringParser : PetroglyphPrimitiveXmlParser { @@ -10,8 +10,10 @@ private PetroglyphXmlStringParser() { } - public override string Parse(XElement element) + private protected override string DefaultValue => string.Empty; + + protected internal override string ParseCore(string trimmedValue, XElement element) { - return element.Value.Trim(); + return trimmedValue; } } \ 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 0a97b75..7061cca 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlUnsignedIntegerParser.cs @@ -1,24 +1,26 @@ using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlUnsignedIntegerParser : PetroglyphPrimitiveXmlParser { public static readonly PetroglyphXmlUnsignedIntegerParser Instance = new(); + private protected override uint DefaultValue => 0; + private PetroglyphXmlUnsignedIntegerParser() { } - public override uint Parse(XElement element) + protected internal override uint ParseCore(string trimmedValue, XElement element) { - var intValue = PetroglyphXmlIntegerParser.Instance.Parse(element); + var intValue = PetroglyphXmlIntegerParser.Instance.ParseCore(trimmedValue, element); var asUint = (uint)intValue; if (intValue != asUint) { - OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, $"Expected unsigned integer but got '{intValue}'.")); } 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 ec24512..94900b7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/Primitives/PetroglyphXmlVector2FParser.cs @@ -1,20 +1,23 @@ using System.Numerics; using System.Xml.Linq; -namespace PG.StarWarsGame.Files.XML.Parsers.Primitives; +namespace PG.StarWarsGame.Files.XML.Parsers; public sealed class PetroglyphXmlVector2FParser : PetroglyphPrimitiveXmlParser { public static readonly PetroglyphXmlVector2FParser Instance = new(); private static readonly PetroglyphXmlFloatParser FloatParser = PetroglyphXmlFloatParser.Instance; + private static readonly PetroglyphXmlLooseStringListParser LooseStringListParser = PetroglyphXmlLooseStringListParser.Instance; private PetroglyphXmlVector2FParser() { } - public override Vector2 Parse(XElement element) + private protected override Vector2 DefaultValue => default; + + protected internal override Vector2 ParseCore(string trimmedValue, XElement element) { var listOfValues = LooseStringListParser.Parse(element); @@ -23,13 +26,13 @@ public override Vector2 Parse(XElement element) if (listOfValues.Count == 1) { - var value = FloatParser.Parse(listOfValues[0], element); + var value = FloatParser.ParseCore(listOfValues[0], element); return new Vector2(value, 0.0f); } - var value1 = FloatParser.Parse(listOfValues[0], element); - var value2 = FloatParser.Parse(listOfValues[1], element); - + var value1 = FloatParser.ParseCore(listOfValues[0], element); + var value2 = FloatParser.ParseCore(listOfValues[1], element); + return new Vector2(value1, value2); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs index e37045e..380ea3f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/XmlFileListParser.cs @@ -3,7 +3,6 @@ using System.Xml.Linq; using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; -using PG.StarWarsGame.Files.XML.Parsers.Primitives; namespace PG.StarWarsGame.Files.XML.Parsers; @@ -21,12 +20,16 @@ protected override XmlFileListContainer Parse(XElement element, string fileName) if (tagName == "File") { var file = PetroglyphXmlStringParser.Instance.Parse(child); + if (file.Length == 0) + { + ErrorReporter?.Report(this, + new XmlParseErrorEventArgs(element, XmlParseErrorKind.InvalidValue, "Empty value in tag.")); + } files.Add(file); } else { - ErrorReporter?.Report(ToString(), - new XmlParseErrorEventArgs(child, XmlParseErrorKind.UnknownNode, + ErrorReporter?.Report(this, new XmlParseErrorEventArgs(child, XmlParseErrorKind.UnknownNode, $"Tag '<{tagName}>' is not supported. Only '' is supported.")); } } From 1307adc2b67589651a81cf399b5a191977f1c5a0 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Thu, 20 Feb 2025 23:04:57 +0100 Subject: [PATCH 13/34] remove manual settings --- src/ModVerify.CliApp/Properties/launchSettings.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index e17a7c9..7f5c34d 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -8,11 +8,6 @@ "FromModPath": { "commandName": "Project", "commandLineArgs": "-o verifyResults --baseline focBaseline.json --path C:/test --type Foc" - }, - - "Manual": { - "commandName": "Project", - "commandLineArgs": "-o verifyResults --baseline c:/users/lrs/desktop/focBaseline.json --game \"C:/Users/lrs/Privat/Star Wars Empire at War/corruption\" --fallbackGame \"C:/Users/lrs/Privat/Star Wars Empire at War/GameData\" --type Foc" } } } \ No newline at end of file From 41e1454926c104fba45036248b5be122fef6b688 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 22 Feb 2025 12:56:34 +0100 Subject: [PATCH 14/34] commit --- .../CommandBar/CommandBarGameManager.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 55ed6da..a9838b5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -78,14 +78,11 @@ await Task.Run(() => contentParser.ParseEntriesFromFileListXml( // Create Camera // Resize(true) - foreach (var parsedCommandBarComponent in parsedCommandBarComponents.Values) { var component = CommandBarBaseComponent.Create(parsedCommandBarComponent, ErrorReporter); if (component is not null) { - // If I understand this correctly, the name is not uppercased, which means name is case-sensitive! - // TODO: Debug to confirm! var crc = _hashingService.GetCrc32(component.Name, PGConstants.DefaultPGEncoding); NamedEntries.Add(crc, component); } @@ -155,7 +152,6 @@ private void SetMegaTexture() if (Components.FirstOrDefault(x => x is CommandBarShellComponent) is null) return; // Note: The tag is not used by the engine - // TODO: Confirm! var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{MegaTextureBaseName}.mtd"); using var megaTexture = GameRepository.TryOpenFile(mtdPath); MegaTextureFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); @@ -163,8 +159,7 @@ private void SetMegaTexture() } private void SetComponentGroup(IEnumerable components) - { - // TODO: Check whether groups are really case-sensitive! + { var groupData = components .Where(x => !string.IsNullOrEmpty(x.XmlData.Group)) .GroupBy(x => x.XmlData.Group!, StringComparer.Ordinal); From 00bc8406dafb4d54e50eea909c0bcd054d5f32fe Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 23 Feb 2025 16:19:40 +0100 Subject: [PATCH 15/34] start implementing PGRender --- .../InitializationErrorReporter.cs | 2 +- .../InitializationErrorReporterBase.cs | 2 +- .../DatabaseError/XmlParseErrorReporter.cs | 2 +- src/ModVerify/Verifiers/GameVerifierBase.cs | 2 +- .../CommandBar/CommandBarGameManager.cs | 46 ++++++++++++++++--- .../Components/CommandBarBaseComponent.cs | 12 +++-- .../Components/CommandBarShellComponent.cs | 13 +++++- .../Database/GameDatabase.cs | 2 +- .../Database/GameDatabaseService.cs | 2 +- .../Database/GameInitializer.cs | 5 +- .../Database/IGameDatabase.cs | 2 +- .../IO/{Repositories => }/FileFoundInfo.cs | 2 +- .../GameRepositoryFactory.cs | 3 +- .../IO/{Repositories => }/IGameRepository.cs | 2 +- .../IGameRepositoryFactory.cs | 3 +- .../IO/{Repositories => }/IRepository.cs | 2 +- .../{Repositories => }/MultiPassRepository.cs | 3 +- .../IO/Repositories/EffectsRepository.cs | 19 +------- .../IO/Repositories/ModelRepository.cs | 45 ++++++++++++++++++ .../IO/Repositories/TextureRepository.cs | 5 +- .../IO/Utilities/PathExtensions.cs | 20 ++++++++ .../PG.StarWarsGame.Engine.csproj | 1 + .../PG.StarWarsGame.Engine/PGConstants.cs | 1 + .../PetroglyphEngineServiceContribution.cs | 2 +- .../Rendering/ModelClass.cs | 26 +++++++++++ .../Rendering/PGRender.cs | 22 +++++++++ .../Rendering/PrimRenderMode.cs | 20 ++++++++ .../Rendering/RgbaColor.cs | 21 +-------- .../Xml/Parsers/XmlContainerContentParser.cs | 2 +- 29 files changed, 220 insertions(+), 69 deletions(-) rename src/PetroglyphTools/PG.StarWarsGame.Engine/IO/{Repositories => }/FileFoundInfo.cs (93%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/IO/{Repositories => }/GameRepositoryFactory.cs (86%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/IO/{Repositories => }/IGameRepository.cs (91%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/IO/{Repositories => }/IGameRepositoryFactory.cs (72%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/IO/{Repositories => }/IRepository.cs (90%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/IO/{Repositories => }/MultiPassRepository.cs (96%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PrimRenderMode.cs diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs index d003fca..102b8c4 100644 --- a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs +++ b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs @@ -1,7 +1,7 @@ using System; using AET.ModVerify.Reporting; using PG.StarWarsGame.Engine.ErrorReporting; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; namespace AET.ModVerify.Verifiers; diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs index df3e22e..529dd19 100644 --- a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs +++ b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using AET.ModVerify.Reporting; using AnakinRaW.CommonUtilities; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; namespace AET.ModVerify.Verifiers; diff --git a/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs b/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs index 5f3fc5c..a3c4047 100644 --- a/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs +++ b/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs @@ -5,7 +5,7 @@ using AET.ModVerify.Utilities; using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.ErrorReporting; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace AET.ModVerify.Verifiers; diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 768236c..21a9371 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; namespace AET.ModVerify.Verifiers; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index a9838b5..2712250 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -15,11 +15,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using PG.StarWarsGame.Engine.Rendering; namespace PG.StarWarsGame.Engine.CommandBar; internal class CommandBarGameManager( GameRepository repository, + PGRender pgRender, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager @@ -29,6 +31,7 @@ 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; @@ -111,23 +114,54 @@ private void LinkComponentsWithActions() private void LinkComponentsToShell() { - // TODO: DEBUG!!! - if (!Groups.TryGetValue("Shells", out var shellGroup)) return; - foreach (var shellComponent in shellGroup.Components) + foreach (var component in Components) { - if (shellComponent.Type == CommandBarComponentType.None) + if (component.Type == CommandBarComponentType.Shell) continue; - foreach (var other in shellGroup.Components) + foreach (var shellComponent in shellGroup.Components) { - + if (LinkToShell(component, shellComponent as CommandBarShellComponent)) + break; } } } + private bool LinkToShell(CommandBarBaseComponent component, CommandBarShellComponent? shell) + { + if (shell is null) + { + ErrorReporter.Assert(EngineAssert.CreateCapture($"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; + + using var model = _pgRender.LoadModelAndAnimations(modelPath!); + if (model is null) + { + ErrorReporter.Assert(EngineAssert.CreateCapture($"Cannot link component '{componentName}' to shell '{shell.Name}' because model '{modelPath}' could not be loaded.")); + 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 diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs index 2b5cabb..2bbc4c9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs @@ -11,13 +11,14 @@ public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) public string Name => XmlData.Name; public RgbaColor Color { get; } = xmlData.Color.HasValue ? new RgbaColor(xmlData.Color.Value) : new RgbaColor(255, 255, 255, 255); - public int Bone { get; } = unchecked((int)0xFFFFFFFF); + public int Bone { get; internal set; } = -1; public int BaseLayer { get; } = xmlData.BaseLayer; public bool Hidden { get; internal set; } = xmlData.Hidden; public bool Disabled { get; } = xmlData.Disabled; public abstract CommandBarComponentType Type { get; } public CommandBarComponentId Id { get; internal set; } - public CommandBarComponentGroup Group { get; internal set; } + public CommandBarComponentGroup? Group { get; internal set; } + public CommandBarShellComponent? ParentShell { get; internal set; } public static CommandBarBaseComponent? Create(CommandBarComponentData xmlData, IGameErrorReporter errorReporter) { @@ -51,6 +52,11 @@ public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) return null; } + public override string ToString() + { + return Name; + } + private static CommandBarComponentType GetTypeFromString(ReadOnlySpan xmlValue) { if (xmlValue.Equals("SHELL".AsSpan(), StringComparison.OrdinalIgnoreCase)) @@ -67,8 +73,6 @@ private static CommandBarComponentType GetTypeFromString(ReadOnlySpan xmlV return CommandBarComponentType.Model; if (xmlValue.Equals("BAR".AsSpan(), StringComparison.OrdinalIgnoreCase)) return CommandBarComponentType.Bar; - - return CommandBarComponentType.None; } } \ 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 f02ddbe..799040e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarShellComponent.cs @@ -2,9 +2,18 @@ namespace PG.StarWarsGame.Engine.CommandBar.Components; -public class CommandBarShellComponent(CommandBarComponentData xmlData) : CommandBarBaseComponent(xmlData) +public class CommandBarShellComponent : CommandBarBaseComponent { public override CommandBarComponentType Type => CommandBarComponentType.Shell; - public string? ModelName { get; } = xmlData.ModelName; + public string? ModelName { get; } + + public string? ModelPath { get; } + + public CommandBarShellComponent(CommandBarComponentData xmlData) : base(xmlData) + { + ModelName = xmlData.ModelName; + if (!string.IsNullOrEmpty(ModelName)) + ModelPath = $"DATA\\ART\\MODELS\\{ModelName}"; + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs index a72feb1..910c6fe 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs @@ -4,7 +4,7 @@ using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.GuiDialog; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; namespace PG.StarWarsGame.Engine.Database; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs index 3028fec..820db03 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.ErrorReporting; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; namespace PG.StarWarsGame.Engine.Database; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index 0868a80..29fbadb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -9,6 +9,7 @@ using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Rendering; namespace PG.StarWarsGame.Engine.Database; @@ -63,6 +64,8 @@ public async Task InitializeAsync(GameErrorReporterWrapper errorR // FactionFiles.xml // TargetingPrioritySetFiles.xml // MousePointerFiles.xml + + var pgRender = new PGRender(repository, errorReporter, serviceProvider); var gameConstants = new GameConstants.GameConstants(repository, errorReporter, serviceProvider); await gameConstants.InitializeAsync( _cancellationTokenSource.Token); @@ -73,7 +76,7 @@ public async Task InitializeAsync(GameErrorReporterWrapper errorR var sfxGameManager = new SfxEventGameManager(repository, errorReporter, serviceProvider); await sfxGameManager.InitializeAsync( _cancellationTokenSource.Token); - var commandBarManager = new CommandBarGameManager(repository, errorReporter, serviceProvider); + var commandBarManager = new CommandBarGameManager(repository, pgRender, errorReporter, serviceProvider); await commandBarManager.InitializeAsync( _cancellationTokenSource.Token); var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorReporter, serviceProvider); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs index 087b7f8..4484e3b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs @@ -3,7 +3,7 @@ using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.GuiDialog; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; namespace PG.StarWarsGame.Engine.Database; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs similarity index 93% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs index eb1af79..838971d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FileFoundInfo.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using PG.StarWarsGame.Files.MEG.Data.Entries; -namespace PG.StarWarsGame.Engine.IO.Repositories; +namespace PG.StarWarsGame.Engine.IO; internal readonly ref struct FileFoundInfo { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs similarity index 86% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs index 564178c..9ff2485 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs @@ -1,7 +1,8 @@ using System; using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; -namespace PG.StarWarsGame.Engine.IO.Repositories; +namespace PG.StarWarsGame.Engine.IO; internal sealed class GameRepositoryFactory(IServiceProvider serviceProvider) : IGameRepositoryFactory { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs similarity index 91% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepository.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs index ca6bfd5..ab9556f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs @@ -1,6 +1,6 @@ using PG.StarWarsGame.Engine.Localization; -namespace PG.StarWarsGame.Engine.IO.Repositories; +namespace PG.StarWarsGame.Engine.IO; public interface IGameRepository : IRepository { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs similarity index 72% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs index c2322ee..c185793 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IGameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs @@ -1,6 +1,7 @@ using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; -namespace PG.StarWarsGame.Engine.IO.Repositories; +namespace PG.StarWarsGame.Engine.IO; internal interface IGameRepositoryFactory { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs similarity index 90% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs index 8054340..408c89b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/IRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace PG.StarWarsGame.Engine.IO.Repositories; +namespace PG.StarWarsGame.Engine.IO; public interface IRepository { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs similarity index 96% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs index 7dd0b6e..4de72a1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/MultiPassRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs @@ -2,9 +2,10 @@ using System.IO; using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Utilities; -namespace PG.StarWarsGame.Engine.IO.Repositories; +namespace PG.StarWarsGame.Engine.IO; internal abstract class MultiPassRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : IRepository { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs index 22f4949..06305b7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs @@ -24,7 +24,7 @@ private protected override FileFoundInfo MultiPassAction( ref ValueStringBuilder destination, bool megFileOnly) { - var strippedName = StripFileName(filePath); + var strippedName = PGPathUtilities.StripFileName(filePath); if (strippedName.Length > PGConstants.MaxEffectFileName) return default; @@ -84,21 +84,4 @@ private FileFoundInfo FindEffect( return BaseRepository.FindFile(multiPassStringBuilder.AsSpan(), ref filePathStringBuilder); } - - private static ReadOnlySpan StripFileName(ReadOnlySpan src) - { - var destination = src; - - for (var i = src.Length - 1; i >= 0; --i) - { - - if (src[i] == '.') - destination = src.Slice(0, i); - - if (src[i] == '/' || src[i] == '\\') - break; - } - - return destination; - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs new file mode 100644 index 0000000..9f20117 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs @@ -0,0 +1,45 @@ +using System; +using System.Runtime.CompilerServices; +using PG.StarWarsGame.Engine.IO.Utilities; +using PG.StarWarsGame.Engine.Utilities; + +namespace PG.StarWarsGame.Engine.IO.Repositories; + +internal class ModelRepository(GameRepository baseRepository, IServiceProvider serviceProvider) + : MultiPassRepository(baseRepository, serviceProvider) +{ + private protected override FileFoundInfo MultiPassAction( + ReadOnlySpan filePath, + ref ValueStringBuilder reusableStringBuilder, + ref ValueStringBuilder destination, + bool megFileOnly) + { + if (!IsValidSize(filePath)) + return default; + + var fileInfo = BaseRepository.FindFile(filePath, ref destination, megFileOnly); + if (fileInfo.FileFound) + return fileInfo; + + return default; + + //destination.Length = 0; + + //var stripped = PGPathUtilities.StripFileName(filePath); + + //var path = FileSystem.Path.GetDirectoryName(filePath); + //FileSystem.Path.Join(path, stripped, ref reusableStringBuilder); + //reusableStringBuilder.Append(".ALO"); + + //var alternatePath = reusableStringBuilder.AsSpan(); + //return !IsValidSize(alternatePath) + // ? default + // : BaseRepository.FindFile(alternatePath, ref destination, megFileOnly); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsValidSize(ReadOnlySpan path) + { + return path.Length != 0 && path.Length < PGConstants.MaxModelFileName; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs index af714f2..2e7aa80 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs @@ -1,5 +1,5 @@ -using System; -using PG.StarWarsGame.Engine.Utilities; +using PG.StarWarsGame.Engine.Utilities; +using System; namespace PG.StarWarsGame.Engine.IO.Repositories; @@ -8,7 +8,6 @@ internal class TextureRepository(GameRepository baseRepository, IServiceProvider private static readonly string DdsExtension = ".dds"; private static readonly string TexturePath = "DATA\\ART\\TEXTURES\\"; - private protected override FileFoundInfo MultiPassAction( ReadOnlySpan filePath, ref ValueStringBuilder reusableStringBuilder, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs index 471b5a8..8a6f221 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs @@ -27,4 +27,24 @@ public static void Join(this IPath _, ReadOnlySpan path1, ReadOnlySpan StripFileName(ReadOnlySpan src) + { + var destination = src; + + for (var i = src.Length - 1; i >= 0; --i) + { + + if (src[i] == '.') + destination = src.Slice(0, i); + + if (src[i] == '/' || src[i] == '\\') + break; + } + + return destination; + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index d927b2b..0b38f76 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -35,6 +35,7 @@ + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs index c7e3de2..9370806 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs @@ -10,6 +10,7 @@ public static class PGConstants public const int MaxMegEntryPathLength = 259; public const int MaxEffectFileName = 259; public const int MaxTextureFileName = 259; + public const int MaxModelFileName = 259; public const int MaxSFXEventDatabaseFileName = 259; public const int MaxSFXEventName = 255; public const int MaxGameObjectDatabaseFileName = 127; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs index 5c7509e..62b8070 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs new file mode 100644 index 0000000..899c23f --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs @@ -0,0 +1,26 @@ +using System; +using AnakinRaW.CommonUtilities; +using PG.StarWarsGame.Files.ALO.Data; + +namespace PG.StarWarsGame.Engine.Rendering; + +internal sealed class ModelClass(AlamoModel model) : DisposableObject +{ + public AlamoModel Model { get; } = model ?? throw new ArgumentNullException(nameof(model)); + + public int IndexOfBone(string boneName) + { + var bones = Model.Bones; + for (var i = 0; i < bones.Count; i++) + { + if (bones[i].Equals(boneName, StringComparison.OrdinalIgnoreCase)) + return i; + } + return -1; + } + + protected override void DisposeResources() + { + Model.Dispose(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs new file mode 100644 index 0000000..92ed70e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -0,0 +1,22 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Files.ALO.Services; + +namespace PG.StarWarsGame.Engine.Rendering; + +internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +{ + private readonly IAloFileService _aloFileService = serviceProvider.GetRequiredService(); + + public ModelClass? LoadModelAndAnimations(string path) + { + if (string.IsNullOrEmpty(path)) + { + errorReporter.Assert(EngineAssert.CreateCapture("Model path is null or empty.")); + } + + return new ModelClass(null!); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PrimRenderMode.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PrimRenderMode.cs new file mode 100644 index 0000000..f58e5bb --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PrimRenderMode.cs @@ -0,0 +1,20 @@ +namespace PG.StarWarsGame.Engine.Rendering; + +public enum PrimRenderMode +{ + PrimOpaque = 0x0, + PrimAdditive = 0x1, + PrimAlpha = 0x2, + PrimModulate = 0x3, + PrimDepthspriteAdditive = 0x4, + PrimDepthspriteAlpha = 0x5, + PrimDepthspriteModulate = 0x6, + PrimDiffuseAlpha = 0x7, + PrimStencilDarken = 0x8, + PrimStencilDarkenBlur = 0x9, + PrimHeat = 0xa, + PrimParticleBumpAlpha = 0xb, + PrimDecalBumpAlpha = 0xc, + PrimAlphaScanlines = 0xd, + PrimCount = 0xe +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs index 52bfee6..be1aaa9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/RgbaColor.cs @@ -64,23 +64,4 @@ public override int GetHashCode() { return HashCode.Combine(_r, _g, _b, _a); } -} - -public enum PrimRenderMode -{ - PrimOpaque = 0x0, - PrimAdditive = 0x1, - PrimAlpha = 0x2, - PrimModulate = 0x3, - PrimDepthspriteAdditive = 0x4, - PrimDepthspriteAlpha = 0x5, - PrimDepthspriteModulate = 0x6, - PrimDiffuseAlpha = 0x7, - PrimStencilDarken = 0x8, - PrimStencilDarkenBlur = 0x9, - PrimHeat = 0xa, - PrimParticleBumpAlpha = 0xb, - PrimDecalBumpAlpha = 0xc, - PrimAlphaScanlines = 0xd, - PrimCount = 0xe -}; \ No newline at end of file +} \ 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 index ff1477c..5d5f98a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/XmlContainerContentParser.cs @@ -6,7 +6,7 @@ using PG.Commons.Collections; using PG.Commons.Hashing; using PG.Commons.Services; -using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Files.XML; using PG.StarWarsGame.Files.XML.Data; using PG.StarWarsGame.Files.XML.ErrorHandling; From 00796d920efd7aadf59851232c0db501ea9fe0d3 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 1 Mar 2025 18:32:17 +0100 Subject: [PATCH 16/34] implement fontmanager, implement assert reporting, better console output --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 6 +- src/ModVerify.CliApp/ModVerifyApp.cs | 11 ++- .../Options/ModVerifyOptions.cs | 4 + src/ModVerify.CliApp/Program.cs | 9 ++- src/ModVerify.CliApp/SettingsBuilder.cs | 1 + src/ModVerify/ModVerify.csproj | 4 +- src/ModVerify/ModVerify.csproj.DotSettings | 3 +- .../Reporting/IDatabaseErrorCollection.cs | 1 + .../Reporting/Reporters/ConsoleReporter.cs | 42 ++++++++-- .../Reporting/Reporters/FileBasedReporter.cs | 3 +- .../Reporting/Reporters/JSON/JsonReporter.cs | 3 +- .../GlobalVerificationReportSettings.cs | 2 + .../Reporting/VerificationBaseline.cs | 2 +- src/ModVerify/Reporting/VerificationError.cs | 4 +- .../Reporting/VerificationReportBroker.cs | 1 - src/ModVerify/VerificationProvider.cs | 3 +- .../InitializationErrorReporter.cs | 16 ---- .../Engine/GameAssertErrorReporter.cs | 51 ++++++++++++ .../GameEngineErrorCollector.cs} | 6 +- .../Engine/InitializationErrorReporter.cs | 21 +++++ .../InitializationErrorReporterBase.cs | 6 +- .../XmlParseErrorReporter.cs | 6 +- src/ModVerify/Verifiers/VerifierErrorCodes.cs | 6 ++ src/ModVerify/VerifyGamePipeline.cs | 80 +++++++++---------- .../CommandBar/CommandBarGameManager.cs | 60 +++++++++++--- .../Components/CommandBarBaseComponent.cs | 4 +- .../Database/GameDatabase.cs | 3 + .../Database/GameInitializer.cs | 11 ++- .../Database/IGameDatabase.cs | 3 + .../ErrorReporting/EngineAssert.cs | 67 ++++++++++++---- .../ErrorReporting/EngineAssertKind.cs | 8 ++ .../IO/IGameRepository.cs | 2 + .../IO/Repositories/GameRepository.cs | 3 +- .../IO/Repositories/ModelRepository.cs | 25 +++--- .../PG.StarWarsGame.Engine.csproj | 7 +- .../PG.StarWarsGame.Engine/PGConstants.cs | 3 + .../Rendering/Font/FontData.cs | 16 ++++ .../Rendering/Font/FontManager.cs | 72 +++++++++++++++++ .../Rendering/Font/IFontManager.cs | 13 +++ .../Rendering/Font/IOSFontManager.cs | 8 ++ .../Rendering/Font/NetFontManager.cs | 13 +++ .../Rendering/Font/WindowsFontManager.cs | 30 +++++++ .../Rendering/PGRender.cs | 14 ++-- .../Parsers/Data/CommandBarComponentParser.cs | 10 +++ .../PG.StarWarsGame.Files.ChunkFiles.csproj | 2 +- .../PG.StarWarsGame.Files.XML.csproj | 2 +- 46 files changed, 523 insertions(+), 144 deletions(-) delete mode 100644 src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs create mode 100644 src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs rename src/ModVerify/Verifiers/{DatabaseError/GameDatabaseInitializationErrorCollector.cs => Engine/GameEngineErrorCollector.cs} (75%) create mode 100644 src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs rename src/ModVerify/Verifiers/{DatabaseError => Engine}/InitializationErrorReporterBase.cs (86%) rename src/ModVerify/Verifiers/{DatabaseError => Engine}/XmlParseErrorReporter.cs (96%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontData.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IFontManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IOSFontManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/NetFontManager.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 1e4c384..1ba6fa0 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -20,15 +20,15 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApp.cs index 598aa09..5147f48 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApp.cs @@ -31,12 +31,21 @@ public async Task RunApplication() { _logger?.LogInformation($"Verifying '{installData.Name}'..."); await verifyPipeline.RunAsync().ConfigureAwait(false); - _logger?.LogInformation("Finished Verifying"); } catch (GameVerificationException e) { + _logger?.LogError($"There is at least one verification error with severity " + + $"'{settings.GameVerifySettings.GlobalReportSettings.MinimumReportSeverity}' or greater."); returnCode = e.HResult; } + finally + { + var message = $"Verification finished with code: {returnCode}"; + if (returnCode == 0) + _logger?.LogInformation(message); + else + _logger?.LogError(message); + } if (settings.CreateNewBaseline) await WriteBaseline(verifyPipeline.Errors, settings.NewBaselinePath).ConfigureAwait(false); diff --git a/src/ModVerify.CliApp/Options/ModVerifyOptions.cs b/src/ModVerify.CliApp/Options/ModVerifyOptions.cs index 65e3a9b..c058a57 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Options/ModVerifyOptions.cs @@ -66,4 +66,8 @@ internal class ModVerifyOptions HelpText = "Additional fallback paths, which may contain assets that shall be included when doing the verification. Do not add EaW here. " + "Multiple paths can be separated using the ';' (semicolon) character.")] public IList? AdditionalFallbackPath { get; set; } + + [Option("ignoreAsserts", Required = false, + HelpText = "When this flag is present, the application will not report engine assertions.")] + public bool IgnoreAsserts { get; set; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 7e7f744..ec15a53 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -2,9 +2,11 @@ using System.IO.Abstractions; using System.Threading.Tasks; using AET.ModVerify; +using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Reporters; using AET.ModVerify.Reporting.Reporters.JSON; using AET.ModVerify.Reporting.Reporters.Text; +using AET.ModVerify.Reporting.Settings; using AET.ModVerifyTool.Options; using AET.SteamAbstraction; using AnakinRaW.CommonUtilities.Hashing; @@ -129,7 +131,10 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl private static void SetupReporting(IServiceCollection serviceCollection, ModVerifyAppSettings settings) { - serviceCollection.RegisterConsoleReporter(); + serviceCollection.RegisterConsoleReporter(new VerificationReportSettings + { + MinimumReportSeverity = VerificationSeverity.Error + }); serviceCollection.RegisterJsonReporter(new JsonReporterSettings { @@ -206,7 +211,7 @@ private static void PrintHeader() Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Mod Verify")); Console.WriteLine("***********************************"); Console.WriteLine("***********************************"); - Console.WriteLine(" by Anakin"); + Console.WriteLine(" by AnakinRaW"); Console.WriteLine(); Console.WriteLine(); } diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index 27c7857..c4babfd 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -73,6 +73,7 @@ private GlobalVerificationReportSettings BuilderGlobalReportSettings(ModVerifyOp Baseline = baseline, Suppressions = suppressions, MinimumReportSeverity = VerificationSeverity.Information, + ReportAsserts = !options.IgnoreAsserts }; } diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index c72b0dd..c2852dd 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -22,8 +22,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/ModVerify/ModVerify.csproj.DotSettings b/src/ModVerify/ModVerify.csproj.DotSettings index 8af804c..b49eb73 100644 --- a/src/ModVerify/ModVerify.csproj.DotSettings +++ b/src/ModVerify/ModVerify.csproj.DotSettings @@ -1,2 +1,3 @@  - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs index 1b11b8f..a2453a0 100644 --- a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs +++ b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs @@ -7,4 +7,5 @@ internal interface IDatabaseErrorCollection { IEnumerable XmlErrors { get; } IEnumerable InitializationErrors { get; } + IEnumerable Asserts { get; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs index 2e1cb6a..145f2a8 100644 --- a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs +++ b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs @@ -11,20 +11,46 @@ internal class ConsoleReporter(VerificationReportSettings settings, IServiceProv public override Task ReportAsync(IReadOnlyCollection errors) { var filteredErrors = FilteredErrors(errors).OrderByDescending(x => x.Severity).ToList(); + PrintErrorStats(errors, filteredErrors); + Console.WriteLine(); + return Task.CompletedTask; + } + private void PrintErrorStats(IReadOnlyCollection errors, List filteredErrors) + { Console.WriteLine(); - Console.WriteLine("GAME VERIFICATION RESULT"); - Console.WriteLine($"Errors of severity {Settings.MinimumReportSeverity}: {filteredErrors.Count}"); Console.WriteLine(); + Console.WriteLine("***********************"); + Console.WriteLine(" Error Report "); + Console.WriteLine("***********************"); + Console.WriteLine(); + if (errors.Count == 0) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("No errors! Well done :)"); + Console.ResetColor(); + return; + } - if (filteredErrors.Count == 0) - Console.WriteLine("No errors!"); - - foreach (var error in filteredErrors) - Console.WriteLine($"[{error.Severity}] [{error.Id}] Message={error.Message}"); + Console.WriteLine($"TOTAL Verification Errors: {errors.Count}"); + var groupedBySeverity = errors.GroupBy(x => x.Severity); + foreach (var group in groupedBySeverity) + { + Console.WriteLine($" Severity {group.Key}: {group.Count()}"); + } Console.WriteLine(); - return Task.CompletedTask; + if (filteredErrors.Count == 0) + { + if (errors.Count != 0) + Console.WriteLine("Some errors are not displayed to the console. Please check the created output files."); + return; + } + + Console.WriteLine($"Below the list of error with minimum severity {Settings.MinimumReportSeverity}:"); + + foreach (var error in filteredErrors) + Console.WriteLine($"[{error.Severity}] [{error.Id}] Message={error.Message}"); } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs b/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs index b66f29a..1057bfa 100644 --- a/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs +++ b/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs @@ -6,7 +6,8 @@ namespace AET.ModVerify.Reporting.Reporters; -public abstract class FileBasedReporter(T settings, IServiceProvider serviceProvider) : ReporterBase(settings, serviceProvider) where T : FileBasedReporterSettings +public abstract class FileBasedReporter(T settings, IServiceProvider serviceProvider) + : ReporterBase(settings, serviceProvider) where T : FileBasedReporterSettings { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); diff --git a/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs b/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs index 731482a..941a9d5 100644 --- a/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs +++ b/src/ModVerify/Reporting/Reporters/JSON/JsonReporter.cs @@ -7,7 +7,8 @@ namespace AET.ModVerify.Reporting.Reporters.JSON; -internal class JsonReporter(JsonReporterSettings settings, IServiceProvider serviceProvider) : FileBasedReporter(settings, serviceProvider) +internal class JsonReporter(JsonReporterSettings settings, IServiceProvider serviceProvider) + : FileBasedReporter(settings, serviceProvider) { public const string FileName = "VerificationResult.json"; diff --git a/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs b/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs index fe19d68..bc3bdf7 100644 --- a/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs +++ b/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs @@ -5,4 +5,6 @@ public record GlobalVerificationReportSettings : VerificationReportSettings public VerificationBaseline Baseline { get; init; } = VerificationBaseline.Empty; public SuppressionList Suppressions { get; init; } = SuppressionList.Empty; + + public bool ReportAsserts { get; init; } = true; } \ No newline at end of file diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs index 81cb8b3..3051394 100644 --- a/src/ModVerify/Reporting/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/VerificationBaseline.cs @@ -20,7 +20,7 @@ public sealed class VerificationBaseline : IReadOnlyCollection(baseline.Errors.Select(x => new VerificationError(x))); + _errors = new(baseline.Errors.Select(x => new VerificationError(x))); } public VerificationBaseline(IEnumerable errors) diff --git a/src/ModVerify/Reporting/VerificationError.cs b/src/ModVerify/Reporting/VerificationError.cs index fa25cf3..89b7189 100644 --- a/src/ModVerify/Reporting/VerificationError.cs +++ b/src/ModVerify/Reporting/VerificationError.cs @@ -31,7 +31,7 @@ public VerificationError(string id, string message, string verifier, IEnumerable Message = message ?? throw new ArgumentNullException(nameof(message)); Verifier = verifier; Severity = severity; - _assets = new HashSet(affectedAssets); + _assets = new(affectedAssets); AffectedAssets = _assets.ToList(); } @@ -40,7 +40,7 @@ internal VerificationError(JsonVerificationError error) Id = error.Id; Message = error.Message; Verifier = error.Verifier; - _assets = new HashSet(error.Assets); + _assets = new(error.Assets); AffectedAssets = _assets.ToList(); } diff --git a/src/ModVerify/Reporting/VerificationReportBroker.cs b/src/ModVerify/Reporting/VerificationReportBroker.cs index ea4d127..cdb1aec 100644 --- a/src/ModVerify/Reporting/VerificationReportBroker.cs +++ b/src/ModVerify/Reporting/VerificationReportBroker.cs @@ -13,7 +13,6 @@ internal class VerificationReportBroker(GlobalVerificationReportSettings reportS { private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(VerificationReportBroker)); - public async Task> ReportAsync(IEnumerable steps) { var suppressions = new SuppressionList(reportSettings.Suppressions); diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify/VerificationProvider.cs index 66bb8b1..92aa2c9 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify/VerificationProvider.cs @@ -10,9 +10,10 @@ internal class VerificationProvider(IServiceProvider serviceProvider) : IVerific { public IEnumerable GetAllDefaultVerifiers(IGameDatabase database, GameVerifySettings settings) { + 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 ReferencedTexturesVerifier(database, settings, serviceProvider); + //yield return new ReferencedTexturesVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs b/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs deleted file mode 100644 index 102b8c4..0000000 --- a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using AET.ModVerify.Reporting; -using PG.StarWarsGame.Engine.ErrorReporting; -using PG.StarWarsGame.Engine.IO; - -namespace AET.ModVerify.Verifiers; - -internal sealed class InitializationErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) : InitializationErrorReporterBase(gameRepository, serviceProvider) -{ - public override string Name => "InitializationErrors"; - - protected override void CreateError(InitializationError error, out ErrorData errorData) - { - errorData = new ErrorData("INIT00", error.Message, [error.GameManager], VerificationSeverity.Critical); - } -} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs b/src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs new file mode 100644 index 0000000..8e55f67 --- /dev/null +++ b/src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AET.ModVerify.Reporting; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO; + +namespace AET.ModVerify.Verifiers; + +internal sealed class GameAssertErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) + : InitializationErrorReporterBase(gameRepository, serviceProvider) +{ + public override string Name => "GameAsserts"; + + protected override ErrorData CreateError(EngineAssert assert) + { + var assets = new List + { + GetLocation(assert) + }; + if (assert.Value is not null) + assets.Add($"value='{assert.Value}'"); + if (assert.Context is not null) + assets.Add($"context='{assert.Context}'"); + return new ErrorData(GetIdFromError(assert.Kind), assert.Message, assets, VerificationSeverity.Warning); + } + + private static string GetLocation(EngineAssert assert) + { + var sb = new StringBuilder("method='"); + if (assert.TypeName is not null) + { + sb.Append(assert.TypeName); + sb.Append("::"); + } + sb.Append(assert.Method); + sb.Append('\''); + return sb.ToString(); + } + + private static string GetIdFromError(EngineAssertKind assertKind) + { + return assertKind switch + { + EngineAssertKind.NullOrEmptyValue => VerifierErrorCodes.AssertValueNullOrEmpty, + EngineAssertKind.ValueOutOfRange => VerifierErrorCodes.AssertValueOutOfRange, + EngineAssertKind.InvalidValue => VerifierErrorCodes.AssertValueInvalid, + _ => throw new ArgumentOutOfRangeException(nameof(assertKind), assertKind, null) + }; + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs b/src/ModVerify/Verifiers/Engine/GameEngineErrorCollector.cs similarity index 75% rename from src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs rename to src/ModVerify/Verifiers/Engine/GameEngineErrorCollector.cs index e8543d6..ef1a967 100644 --- a/src/ModVerify/Verifiers/DatabaseError/GameDatabaseInitializationErrorCollector.cs +++ b/src/ModVerify/Verifiers/Engine/GameEngineErrorCollector.cs @@ -7,18 +7,20 @@ namespace AET.ModVerify.Verifiers; -internal sealed class GameDatabaseInitializationErrorCollector( +internal sealed class GameEngineErrorCollector( IDatabaseErrorCollection errorCollection, IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) : GameVerifierBase(gameDatabase, settings, serviceProvider) { - public override string FriendlyName => "Reporting Game Initialization Errors"; + public override string FriendlyName => "Reporting Game Engine Errors"; protected override void RunVerification(CancellationToken token) { AddErrors(new InitializationErrorReporter(Repository, Services).GetErrors(errorCollection.InitializationErrors)); AddErrors(new XmlParseErrorReporter(Repository, Services).GetErrors(errorCollection.XmlErrors)); + if (Settings.GlobalReportSettings.ReportAsserts) + AddErrors(new GameAssertErrorReporter(Repository, Services).GetErrors(errorCollection.Asserts)); } private void AddErrors(IEnumerable errors) diff --git a/src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs b/src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs new file mode 100644 index 0000000..db175a3 --- /dev/null +++ b/src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs @@ -0,0 +1,21 @@ +using System; +using AET.ModVerify.Reporting; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO; + +namespace AET.ModVerify.Verifiers; + +internal sealed class InitializationErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) + : InitializationErrorReporterBase(gameRepository, serviceProvider) +{ + public override string Name => "InitializationErrors"; + + protected override ErrorData CreateError(InitializationError error) + { + return new ErrorData( + VerifierErrorCodes.InitializationError, + error.Message, + [error.GameManager], + VerificationSeverity.Critical); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs b/src/ModVerify/Verifiers/Engine/InitializationErrorReporterBase.cs similarity index 86% rename from src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs rename to src/ModVerify/Verifiers/Engine/InitializationErrorReporterBase.cs index 529dd19..6626bca 100644 --- a/src/ModVerify/Verifiers/DatabaseError/InitializationErrorReporterBase.cs +++ b/src/ModVerify/Verifiers/Engine/InitializationErrorReporterBase.cs @@ -17,12 +17,12 @@ public IEnumerable GetErrors(IEnumerable errors) { foreach (var error in errors) { - CreateError(error, out var data); - yield return new VerificationError(data.Identifier, data.Message, Name, data.Assets, data.Severity); + var errorData = CreateError(error); + yield return new VerificationError(errorData.Identifier, errorData.Message, Name, errorData.Assets, errorData.Severity); } } - protected abstract void CreateError(T error, out ErrorData errorData); + protected abstract ErrorData CreateError(T error); protected readonly ref struct ErrorData { diff --git a/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs b/src/ModVerify/Verifiers/Engine/XmlParseErrorReporter.cs similarity index 96% rename from src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs rename to src/ModVerify/Verifiers/Engine/XmlParseErrorReporter.cs index a3c4047..0557c65 100644 --- a/src/ModVerify/Verifiers/DatabaseError/XmlParseErrorReporter.cs +++ b/src/ModVerify/Verifiers/Engine/XmlParseErrorReporter.cs @@ -17,12 +17,11 @@ internal sealed class XmlParseErrorReporter(IGameRepository gameRepository, ISer public override string Name => "XMLError"; - protected override void CreateError(XmlError error, out ErrorData errorData) + protected override ErrorData CreateError(XmlError error) { var id = GetIdFromError(error.ErrorKind); var severity = GetSeverityFromError(error.ErrorKind); - var strippedFileName = _fileSystem.Path .GetGameStrippedPath(GameRepository.Path.AsSpan(), error.FileLocation.XmlFile.ToUpperInvariant().AsSpan()).ToString(); var assets = new List @@ -47,8 +46,7 @@ protected override void CreateError(XmlError error, out ErrorData errorData) } var errorMessage = CreateErrorMessage(error, strippedFileName); - - errorData = new ErrorData(id, errorMessage, assets, severity); + return new ErrorData(id, errorMessage, assets, severity); } private static string CreateErrorMessage(XmlError error, string strippedFileName) diff --git a/src/ModVerify/Verifiers/VerifierErrorCodes.cs b/src/ModVerify/Verifiers/VerifierErrorCodes.cs index 6a68e91..10b5414 100644 --- a/src/ModVerify/Verifiers/VerifierErrorCodes.cs +++ b/src/ModVerify/Verifiers/VerifierErrorCodes.cs @@ -2,6 +2,12 @@ public static class VerifierErrorCodes { + public const string InitializationError = "INIT00"; + + public const string AssertValueNullOrEmpty = "ASRT00"; + public const string AssertValueOutOfRange = "ASRT01"; + public const string AssertValueInvalid = "ASRT02"; + public const string GenericExceptionErrorCode = "MV00"; public const string DuplicateFound = "DUP00"; diff --git a/src/ModVerify/VerifyGamePipeline.cs b/src/ModVerify/VerifyGamePipeline.cs index 4ae7ccf..6d24a3f 100644 --- a/src/ModVerify/VerifyGamePipeline.cs +++ b/src/ModVerify/VerifyGamePipeline.cs @@ -24,6 +24,8 @@ public abstract class VerifyGamePipeline : Pipeline protected GameVerifySettings Settings { get; } + protected override bool FailFast { get; } + public IReadOnlyCollection Errors { get; private set; } = Array.Empty(); protected VerifyGamePipeline(GameEngineType targetType, GameLocations gameLocations, GameVerifySettings settings, IServiceProvider serviceProvider) @@ -37,6 +39,7 @@ protected VerifyGamePipeline(GameEngineType targetType, GameLocations gameLocati throw new ArgumentException("Settings has invalid parallel worker number.", nameof(settings)); _verifyRunner = new ParallelStepRunner(settings.ParallelVerifiers, serviceProvider); + FailFast = settings.AbortSettings.FailFast; } protected sealed override Task PrepareCoreAsync() @@ -47,54 +50,47 @@ protected sealed override Task PrepareCoreAsync() protected sealed override async Task RunCoreAsync(CancellationToken token) { - Logger?.LogInformation("Verifying..."); + var databaseService = ServiceProvider.GetRequiredService(); + + var initializationErrorListener = new ConcurrentGameGameErrorReporter(); + var initOptions = new GameInitializationOptions + { + Locations = _gameLocations, + TargetEngineType = _targetType, + GameErrorReporter = initializationErrorListener + + }; + var database = await databaseService.InitializeGameAsync(initOptions, token); + + AddStep(new GameEngineErrorCollector(initializationErrorListener, database, Settings, ServiceProvider)); + + foreach (var gameVerificationStep in CreateVerificationSteps(database)) + AddStep(gameVerificationStep); + try { - var databaseService = ServiceProvider.GetRequiredService(); - - var initializationErrorListener = new ConcurrentGameGameErrorReporter(); - var initOptions = new GameInitializationOptions - { - Locations = _gameLocations, - TargetEngineType = _targetType, - GameErrorReporter = initializationErrorListener - - }; - var database = await databaseService.InitializeGameAsync(initOptions, token); - - AddStep(new GameDatabaseInitializationErrorCollector(initializationErrorListener, database, Settings, ServiceProvider)); - - foreach (var gameVerificationStep in CreateVerificationSteps(database)) - AddStep(gameVerificationStep); - - try - { - Logger?.LogInformation("Verifying..."); - _verifyRunner.Error += OnError; - await _verifyRunner.RunAsync(token); - } - finally - { - _verifyRunner.Error -= OnError; - Logger?.LogInformation("Finished Verifying"); - } - - - Logger?.LogInformation("Reporting Errors"); - - var reportBroker = new VerificationReportBroker(Settings.GlobalReportSettings, ServiceProvider); - var errors = await reportBroker.ReportAsync(_verificationSteps); - - Errors = errors; - - if (Settings.AbortSettings.ThrowsGameVerificationException && - errors.Any(x => x.Severity >= Settings.AbortSettings.MinimumAbortSeverity)) - throw new GameVerificationException(errors); + Logger?.LogInformation("Verifying..."); + _verifyRunner.Error += OnError; + await _verifyRunner.RunAsync(token); } finally { - Logger?.LogInformation("Finished game verification"); + _verifyRunner.Error -= OnError; + Logger?.LogInformation("Finished verification"); } + + + Logger?.LogInformation("Reporting Errors"); + + var reportBroker = new VerificationReportBroker(Settings.GlobalReportSettings, ServiceProvider); + var errors = await reportBroker.ReportAsync(_verificationSteps); + + Errors = errors; + + if (Settings.AbortSettings.ThrowsGameVerificationException && + errors.Any(x => x.Severity >= Settings.AbortSettings.MinimumAbortSeverity)) + throw new GameVerificationException(errors); + } protected abstract IEnumerable CreateVerificationSteps(IGameDatabase database); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 2712250..1957576 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -15,13 +15,17 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.Rendering; +using PG.StarWarsGame.Engine.Rendering.Font; namespace PG.StarWarsGame.Engine.CommandBar; internal class CommandBarGameManager( GameRepository repository, PGRender pgRender, + IGameConstants gameConstants, + IFontManager fontManager, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager @@ -34,11 +38,26 @@ internal class CommandBarGameManager( private readonly PGRender _pgRender = pgRender; private bool _megaTextureExists; + private FontData? _defaultFont; public ICollection Components => Entries; public IReadOnlyDictionary Groups => _groups; + public FontData? DefaultFont + { + get + { + ThrowIfNotInitialized(); + return _defaultFont; + } + private set + { + ThrowIfAlreadyInitialized(); + _defaultFont = value; + } + } + public IMtdFile? MegaTextureFile { get @@ -124,6 +143,7 @@ private void LinkComponentsToShell() foreach (var shellComponent in shellGroup.Components) { + // TODO: Create verifier that shell group only contains shell components if (LinkToShell(component, shellComponent as CommandBarShellComponent)) break; } @@ -134,7 +154,9 @@ private bool LinkToShell(CommandBarBaseComponent component, CommandBarShellCompo { if (shell is null) { - ErrorReporter.Assert(EngineAssert.CreateCapture($"Cannot link component '{component}' because shell component is null.")); + ErrorReporter.Assert( + EngineAssert.FromNullOrEmpty(component, + $"Cannot link component '{component}' because shell component is null.")); return false; } @@ -149,7 +171,9 @@ private bool LinkToShell(CommandBarBaseComponent component, CommandBarShellCompo using var model = _pgRender.LoadModelAndAnimations(modelPath!); if (model is null) { - ErrorReporter.Assert(EngineAssert.CreateCapture($"Cannot link component '{componentName}' to shell '{shell.Name}' because model '{modelPath}' could not be loaded.")); + ErrorReporter.Assert( + EngineAssert.FromNullOrEmpty(new ComponentLinkTuple(component, shell), + $"Cannot link component '{componentName}' to shell '{shell.Name}' because model '{modelPath}' could not be loaded.")); return false; } @@ -168,16 +192,16 @@ private void SetDefaultFont() if (Components.FirstOrDefault(x => x is CommandBarTextComponent or CommandBarTextButtonComponent) is null) return; - /* - 140ab82bd if (!BaseComponentClass::DefaultFont) - 140ab82bd { - 140ab82c6 int32_t point_size = j_GameConstantsClass::Get_Command_Bar_Default_Font_Size(&TheGameConstants); - 140ab830c BaseComponentClass::DefaultFont = j_FontManagerClass::Create_Font(&FontManager, j_GameConstantsClass::Get_Command_Bar_Default_Font_Name(&TheGameConstants), point_size, 1, 0, 0, 1f); - 140ab830c - 140ab831b if (!BaseComponentClass::DefaultFont) - 140ab8331 j_Assert_Handler("DefaultFont != NULL", "C:\BuildSystem\AB_SW_STEAM_FOC_BUILDSERV04_BUILD\StarWars_Steam\FOC\Code\RTS\CommandBar\CommandBarComponent.cpp", 0x1325); - 140ab82bd } - */ + if (_defaultFont is null) + { + // TODO: From GameConstants + string fontName = PGConstants.DefaultUnicodeFontName; + int size = 11; + var font = fontManager.CreateFont(fontName, size, true, false, false, 1.0f); + if (font is null) + ErrorReporter.Assert(EngineAssert.FromNullOrEmpty(this, $"Unable to create Default from name {fontName}")); + DefaultFont = font; + } } private void SetMegaTexture() @@ -238,5 +262,15 @@ private void VerifyFilePathLength(string filePath) }); } } -} + private sealed class ComponentLinkTuple(CommandBarBaseComponent component, CommandBarShellComponent shell) + { + public CommandBarBaseComponent Component { get; } = component; + public CommandBarShellComponent Shell { get; } = shell; + + public override string ToString() + { + return $"component='{Component.Name}' - shell='{Shell.Name}'"; + } + } +} diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs index 2bbc4c9..1b69b9d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs @@ -46,7 +46,9 @@ public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) return new CommandBarSelectComponent(xmlData); } - errorReporter.Assert(EngineAssert.CreateCapture( + // TODO: Verifier for any invalid type value + errorReporter.Assert( + EngineAssert.Create(EngineAssertKind.InvalidValue, type, xmlData.Name, $"Invalid type value '{xmlData.Type}' for CommandbarComponent '{xmlData.Name}')")); return null; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs index 910c6fe..a3d594d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs @@ -6,11 +6,14 @@ using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; +using PG.StarWarsGame.Engine.Rendering.Font; namespace PG.StarWarsGame.Engine.Database; internal class GameDatabase : IGameDatabase { + public required IFontManager FontManager { get; init; } + public required ICommandBarGameManager CommandBarManager { get; init; } public required IGameRepository GameRepository { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index 29fbadb..c974ee9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -10,6 +10,7 @@ using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Rendering; +using PG.StarWarsGame.Engine.Rendering.Font; namespace PG.StarWarsGame.Engine.Database; @@ -70,13 +71,20 @@ public async Task InitializeAsync(GameErrorReporterWrapper errorR var gameConstants = new GameConstants.GameConstants(repository, errorReporter, serviceProvider); await gameConstants.InitializeAsync( _cancellationTokenSource.Token); + // AudioConstants + + // MousePointer + + var fontManger = new FontManager(repository, errorReporter, serviceProvider); + await fontManger.InitializeAsync(_cancellationTokenSource.Token); + var guiDialogs = new GuiDialogGameManager(repository, errorReporter, serviceProvider); await guiDialogs.InitializeAsync(_cancellationTokenSource.Token); var sfxGameManager = new SfxEventGameManager(repository, errorReporter, serviceProvider); await sfxGameManager.InitializeAsync( _cancellationTokenSource.Token); - var commandBarManager = new CommandBarGameManager(repository, pgRender, errorReporter, serviceProvider); + var commandBarManager = new CommandBarGameManager(repository, pgRender, gameConstants, fontManger, errorReporter, serviceProvider); await commandBarManager.InitializeAsync( _cancellationTokenSource.Token); var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorReporter, serviceProvider); @@ -86,6 +94,7 @@ public async Task InitializeAsync(GameErrorReporterWrapper errorR return new GameDatabase { + FontManager = fontManger, GameRepository = repository, GameConstants = gameConstants, GuiDialogManager = guiDialogs, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs index 4484e3b..1c3f7d0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs @@ -5,11 +5,14 @@ using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; +using PG.StarWarsGame.Engine.Rendering.Font; namespace PG.StarWarsGame.Engine.Database; public interface IGameDatabase { + IFontManager FontManager { get; } + IGameRepository GameRepository { get; } IGameConstants GameConstants { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs index 8c1d082..636701e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs @@ -6,33 +6,72 @@ namespace PG.StarWarsGame.Engine.ErrorReporting; -public sealed class EngineAssert(string source, string message) +public sealed class EngineAssert { - public string Message { get; } = message ?? throw new ArgumentNullException(nameof(message)); + private static readonly string ThisNameSpace = typeof(EngineAssert).Namespace!; - public string Source { get; } = source ?? throw new ArgumentNullException(nameof(source)); + public object? Value { get; } - public static EngineAssert CreateCapture(string message) + public object? Context { get; } + + public string Message { get; } + + public string Method { get; } + + public string? TypeName { get; } + + public EngineAssertKind Kind { get; } + + internal EngineAssert(EngineAssertKind kind, object? value, object? context, string? type, string method, string message) { - var stackTrace = new StackTrace(); - var frame = stackTrace.GetFrame(1); + Kind = kind; + Value = value; + Context = context; + Message = message ?? throw new ArgumentNullException(nameof(message)); + TypeName = type ?? throw new ArgumentNullException(nameof(type)); + Method = method ?? throw new ArgumentNullException(nameof(method)); + } + + internal static EngineAssert FromNullOrEmpty(object? context = null, string? message = null) + { + return Create(EngineAssertKind.NullOrEmptyValue, null, context, message ?? "Expected value to be not null or empty"); + } + + internal static EngineAssert Create(EngineAssertKind kind, object? value, object? context, string message) + { + var frame = GetCausingFrame(new StackTrace()); if (frame is null) - return new EngineAssert("UNKNOWN SOURCE", message); + return new EngineAssert(kind, value, context, null, "UNKNOWN SOURCE", message); var method = frame.GetMethod(); - var methodName = CreateMethodName(method); - return new EngineAssert(methodName, message); + var methodInfo = GetMethodInfo(method); + return new EngineAssert(kind, value, context, methodInfo.type, methodInfo.method, message); } - private static string CreateMethodName(MethodBase method) + private static StackFrame? GetCausingFrame(StackTrace trace) { - var methodName = new StringBuilder(); + if (trace.FrameCount == 0) + return null; + for (var i = 0; i < trace.FrameCount; i++) + { + var frame = trace.GetFrame(i); + var method = frame.GetMethod(); + if (method.DeclaringType is null || method.DeclaringType.Namespace?.Equals(ThisNameSpace) == false) + return frame; + } + return null; + } + + private static (string? type, string method) GetMethodInfo(MethodBase method) + { + string? type = null; if (method.DeclaringType is not null) - methodName.Append(method.DeclaringType.FullName); - methodName.Append("::"); + type = method.DeclaringType.FullName; + + var methodName = new StringBuilder(); methodName.Append(method.Name); methodName.Append('('); methodName.Append(string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name))); methodName.Append(')'); - return methodName.ToString(); + return (type, methodName.ToString()); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs new file mode 100644 index 0000000..37830c9 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs @@ -0,0 +1,8 @@ +namespace PG.StarWarsGame.Engine.ErrorReporting; + +public enum EngineAssertKind +{ + NullOrEmptyValue, + ValueOutOfRange, + InvalidValue +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs index ab9556f..32fff7a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepository.cs @@ -15,6 +15,8 @@ public interface IGameRepository : IRepository IRepository TextureRepository { get; } + IRepository ModelRepository { get; } + bool FileExists(string filePath, string[] extensions, bool megFileOnly = false); bool IsLanguageInstalled(LanguageType languageType); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index 0c1dd05..537ded5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -41,6 +41,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository public IRepository EffectsRepository { get; } public IRepository TextureRepository { get; } + public IRepository ModelRepository { get; } private readonly List _loadedMegFiles = new(); @@ -82,7 +83,7 @@ protected GameRepository(GameLocations gameLocations, GameErrorReporterWrapper e EffectsRepository = new EffectsRepository(this, serviceProvider); TextureRepository = new TextureRepository(this, serviceProvider); - + ModelRepository = new ModelRepository(this, serviceProvider); var path = ModPaths.Any() ? ModPaths.First() : GameDirectory; if (!FileSystem.Path.HasTrailingDirectorySeparator(path)) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs index 9f20117..928754e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs @@ -2,6 +2,9 @@ using System.Runtime.CompilerServices; using PG.StarWarsGame.Engine.IO.Utilities; using PG.StarWarsGame.Engine.Utilities; +#if NETSTANDARD2_0 || NETFRAMEWORK +using AnakinRaW.CommonUtilities.FileSystem; +#endif namespace PG.StarWarsGame.Engine.IO.Repositories; @@ -20,21 +23,19 @@ private protected override FileFoundInfo MultiPassAction( var fileInfo = BaseRepository.FindFile(filePath, ref destination, megFileOnly); if (fileInfo.FileFound) return fileInfo; + + destination.Length = 0; - return default; + var stripped = PGPathUtilities.StripFileName(filePath); - //destination.Length = 0; + var path = FileSystem.Path.GetDirectoryName(filePath); + FileSystem.Path.Join(path, stripped, ref reusableStringBuilder); + reusableStringBuilder.Append(".ALO"); - //var stripped = PGPathUtilities.StripFileName(filePath); - - //var path = FileSystem.Path.GetDirectoryName(filePath); - //FileSystem.Path.Join(path, stripped, ref reusableStringBuilder); - //reusableStringBuilder.Append(".ALO"); - - //var alternatePath = reusableStringBuilder.AsSpan(); - //return !IsValidSize(alternatePath) - // ? default - // : BaseRepository.FindFile(alternatePath, ref destination, megFileOnly); + var alternatePath = reusableStringBuilder.AsSpan(); + return !IsValidSize(alternatePath) + ? default + : BaseRepository.FindFile(alternatePath, ref destination, megFileOnly); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 0b38f76..00d6fb1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -18,9 +18,9 @@ preview - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -33,6 +33,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs index 9370806..0f03ec2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs @@ -15,6 +15,9 @@ public static class PGConstants public const int MaxSFXEventName = 255; public const int MaxGameObjectDatabaseFileName = 127; public const int MaxCommandBarDatabaseFileName = 259; + public const int MaxCommandBarComponentName = 255; public const int MaxGuiDialogMegaTextureFileName = 255; + + public const string DefaultUnicodeFontName = "Arial"; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontData.cs new file mode 100644 index 0000000..fdd3322 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontData.cs @@ -0,0 +1,16 @@ +namespace PG.StarWarsGame.Engine.Rendering.Font; + +public readonly struct FontData +{ + public int FontSize { get; init; } + + public string FontName { get; init; } + + public bool Bold { get; init; } + + public bool Italic { get; init; } + + public bool StaticSize { get; init; } + + public float StretchFactor { get; init; } +} \ 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 new file mode 100644 index 0000000..a9ec312 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs @@ -0,0 +1,72 @@ +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO.Repositories; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using PG.StarWarsGame.Engine.Localization; + +namespace PG.StarWarsGame.Engine.Rendering.Font; + +internal class FontManager : GameManagerBase, IFontManager +{ + private readonly IOSFontManager _fontManager; + + private ISet _fontNames = null!; + + public FontManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : base(repository, errorReporter, serviceProvider) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + _fontManager = new WindowsFontManager(); + else + _fontManager = new NetFontManager(); + } + + public IReadOnlyCollection FontNames => [.._fontNames]; + + public FontData? CreateFont(string fontName, int size, bool bold, bool italic, bool staticSize, float stretchFactor) + { + ThrowIfNotInitialized(); + + if (size <= 0) + { + ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.ValueOutOfRange, size, fontName, + $"Font size '{size}' for '{fontName}' must be greater than 0.")); + } + + if (!_fontNames.Contains(fontName)) + fontName = PGConstants.DefaultUnicodeFontName; + + if (!_fontNames.Contains(fontName)) + { + ErrorReporter.Assert(EngineAssert.FromNullOrEmpty(fontName, $"Unable to find font '{fontName}'")); + return null; + } + + return new FontData(); + } + + public void NormalizeFontData(LanguageType language, ref string fontName, ref int fontSize) + { + if (language >= LanguageType.Japanese) + fontName = PGConstants.DefaultUnicodeFontName; + if (language == LanguageType.Russian && fontSize < 7) + fontSize = 7; + } + + protected override Task InitializeCoreAsync(CancellationToken token) + { + var fontNames = new HashSet(_fontManager.GetFontFamilies(), StringComparer.OrdinalIgnoreCase) + { + "EmpireAtWar-Bold", + "EmpireAtWar-Light", + "EmpireAtWar-Medium", + "EmpireAtWar-Stencil" + }; + _fontNames = fontNames; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IFontManager.cs new file mode 100644 index 0000000..e5802aa --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IFontManager.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using PG.StarWarsGame.Engine.Localization; + +namespace PG.StarWarsGame.Engine.Rendering.Font; + +public interface IFontManager +{ + public IReadOnlyCollection FontNames { get; } + + FontData? CreateFont(string fontName, int size, bool bold, bool italic, bool stat, float stretchFactor); + + public void NormalizeFontData(LanguageType language, ref string fontName, ref int fontSize); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IOSFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IOSFontManager.cs new file mode 100644 index 0000000..c930cd2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/IOSFontManager.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace PG.StarWarsGame.Engine.Rendering.Font; + +internal interface IOSFontManager +{ + IEnumerable GetFontFamilies(); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/NetFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/NetFontManager.cs new file mode 100644 index 0000000..2515dc5 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/NetFontManager.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace PG.StarWarsGame.Engine.Rendering.Font; + +internal class NetFontManager : IOSFontManager +{ + public IEnumerable GetFontFamilies() + { + // TODO: At the moment we do not have a platform-independent way to get font names. + yield return "Arial"; + yield return "Arial Medium"; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs new file mode 100644 index 0000000..1bf53c9 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; +using Vanara.PInvoke; + +namespace PG.StarWarsGame.Engine.Rendering.Font; + +[SupportedOSPlatform("windows")] +internal class WindowsFontManager : IOSFontManager +{ + public IEnumerable GetFontFamilies() + { + var hdc = Gdi32.CreateCompatibleDC(); + + var fonts = new HashSet(StringComparer.OrdinalIgnoreCase); + try + { + foreach (var windowsFontData in Gdi32.EnumFontFamiliesEx(hdc, CharacterSet.DEFAULT_CHARSET)) + { + var fontName = windowsFontData.lpelfe.elfEnumLogfontEx.elfFullName; + fonts.Add(fontName); + } + } + finally + { + Gdi32.DeleteDC(hdc); + } + return fonts; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index 92ed70e..6b8134f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -1,6 +1,7 @@ using System; using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Files.ALO.Services; @@ -9,14 +10,15 @@ namespace PG.StarWarsGame.Engine.Rendering; internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) { private readonly IAloFileService _aloFileService = serviceProvider.GetRequiredService(); + private readonly IRepository _modelRepository = gameRepository.ModelRepository; - public ModelClass? LoadModelAndAnimations(string path) + public ModelClass? LoadModelAndAnimations(string path, bool metadataOnly = true) { - if (string.IsNullOrEmpty(path)) - { - errorReporter.Assert(EngineAssert.CreateCapture("Model path is null or empty.")); - } + if (string.IsNullOrEmpty(path)) + errorReporter.Assert(EngineAssert.FromNullOrEmpty(null, "Model path is null or empty.")); - return new ModelClass(null!); + using var aloStream = _modelRepository.TryOpenFile(path); + + return null; } } \ 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/Data/CommandBarComponentParser.cs index c6d2e03..1e20a8b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/Data/CommandBarComponentParser.cs @@ -22,6 +22,7 @@ public override CommandBarComponentData Parse(XElement element, out Crc32 crc32) 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; } @@ -351,5 +352,14 @@ protected override bool ParseTag(XElement tag, CommandBarComponentData component } } + private void ValidateValues(CommandBarComponentData xmlData, XElement element) + { + if (xmlData.Name.Length > PGConstants.MaxCommandBarComponentName) + { + OnParseError(new XmlParseErrorEventArgs(element, XmlParseErrorKind.TooLongData, + $"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.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/PG.StarWarsGame.Files.ChunkFiles.csproj index 8daab61..2d0c68c 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 @@ -16,7 +16,7 @@ snupkg - + 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 740bd97..7a405bb 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 @@ -17,7 +17,7 @@ true - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 853c6b681e68853c073fa8abc4edb416c35449d3 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Mar 2025 17:06:38 +0100 Subject: [PATCH 17/34] load animations --- .../CommandBar/CommandBarGameManager.cs | 26 +- .../Database/GameDatabase.cs | 3 + .../Database/GameInitializer.cs | 1 + .../Database/IGameDatabase.cs | 7 +- .../PG.StarWarsGame.Engine/PGConstants.cs | 1 + .../Rendering/AnimationCollection.cs | 62 +++++ .../Rendering/IPGRender.cs | 14 + .../Rendering/ModelAnimationType.cs | 124 +++++++++ .../Rendering/ModelClass.cs | 28 +- .../Rendering/PGRender.cs | 242 +++++++++++++++++- .../Utilities/ValueStringBuilder.cs | 62 ++--- .../Binary/AloFileReaderFactory.cs | 23 +- .../Identifier/AloContentInfoIdentifier.cs | 25 ++ .../Identifier/IAloContentInfoIdentifier.cs | 2 +- .../Reader/Animations/AnimationChunkTypes.cs | 19 ++ .../Animations/AnimationInformationData.cs | 11 + .../Reader/Animations/AnimationReaderBase.cs | 170 ++++++++++++ .../Reader/Animations/AnimationReaderV1.cs | 6 + .../Reader/Animations/AnimationReaderV2.cs | 6 + .../Binary/Reader/Models/ModelChunkTypes.cs | 19 ++ .../Reader/{ => Models}/ModelFileReader.cs | 23 +- .../Reader/Particles/ParticleChunkType.cs | 18 ++ .../{ => Particles}/ParticleReaderV1.cs | 17 +- .../{ => Particles}/ParticleReaderV2.cs | 2 +- .../Data/AlamoAnimation.cs | 25 ++ .../Data/AlamoModel.cs | 2 +- .../Data/AlamoParticle.cs | 2 +- .../Files/AloFileInformation.cs | 4 +- .../Files/AloType.cs | 3 +- .../Files/Animations/AloAnimationFile.cs | 10 + .../Files/Animations/IAloAnimationFile.cs | 5 + .../Services/AloFileService.cs | 13 +- .../Services/AloLoadOptions.cs | 4 + .../Services/IAloFileService.cs | 9 +- .../Binary/Metadata/ChunkType.cs | 2 + .../Binary/Reader/ChunkReader.cs | 7 + 36 files changed, 916 insertions(+), 81 deletions(-) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationChunkTypes.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationInformationData.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderBase.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV1.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV2.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelChunkTypes.cs rename src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/{ => Models}/ModelFileReader.cs (89%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleChunkType.cs rename src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/{ => Particles}/ParticleReaderV1.cs (86%) rename src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/{ => Particles}/ParticleReaderV2.cs (84%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoAnimation.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/AloAnimationFile.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/IAloAnimationFile.cs diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 1957576..e75411b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -136,6 +136,7 @@ private void LinkComponentsToShell() if (!Groups.TryGetValue("Shells", out var shellGroup)) return; + var modelCache = new Dictionary(); foreach (var component in Components) { if (component.Type == CommandBarComponentType.Shell) @@ -144,13 +145,19 @@ private void LinkComponentsToShell() foreach (var shellComponent in shellGroup.Components) { // TODO: Create verifier that shell group only contains shell components - if (LinkToShell(component, shellComponent as CommandBarShellComponent)) + if (LinkToShell(component, shellComponent as CommandBarShellComponent, modelCache)) break; } } + + foreach (var model in modelCache.Values) + model?.Dispose(); } - private bool LinkToShell(CommandBarBaseComponent component, CommandBarShellComponent? shell) + private bool LinkToShell( + CommandBarBaseComponent component, + CommandBarShellComponent? shell, + IDictionary modelCache) { if (shell is null) { @@ -168,7 +175,12 @@ private bool LinkToShell(CommandBarBaseComponent component, CommandBarShellCompo if (string.IsNullOrEmpty(modelPath)) return false; - using var model = _pgRender.LoadModelAndAnimations(modelPath!); + 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( @@ -177,6 +189,14 @@ private bool LinkToShell(CommandBarBaseComponent component, CommandBarShellCompo return false; } + if (!model.IsModel) + { + ErrorReporter.Assert( + EngineAssert.FromNullOrEmpty(new ComponentLinkTuple(component, shell), + $"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) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs index a3d594d..94665b6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs @@ -6,12 +6,15 @@ using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; +using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; namespace PG.StarWarsGame.Engine.Database; internal class GameDatabase : IGameDatabase { + public required IPGRender PGRender { get; init; } + public required IFontManager FontManager { get; init; } public required ICommandBarGameManager CommandBarManager { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index c974ee9..a06c596 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -94,6 +94,7 @@ public async Task InitializeAsync(GameErrorReporterWrapper errorR return new GameDatabase { + PGRender = pgRender, FontManager = fontManger, GameRepository = repository, GameConstants = gameConstants, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs index 1c3f7d0..224218f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs @@ -1,16 +1,19 @@ -using System.Collections.Generic; -using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; +using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; +using System.Collections.Generic; namespace PG.StarWarsGame.Engine.Database; public interface IGameDatabase { + IPGRender PGRender { get; } + IFontManager FontManager { get; } IGameRepository GameRepository { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs index 0f03ec2..b7a9218 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PGConstants.cs @@ -11,6 +11,7 @@ public static class PGConstants public const int MaxEffectFileName = 259; public const int MaxTextureFileName = 259; public const int MaxModelFileName = 259; + public const int MaxAnimationFileName = 255; public const int MaxSFXEventDatabaseFileName = 259; public const int MaxSFXEventName = 255; public const int MaxGameObjectDatabaseFileName = 127; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs new file mode 100644 index 0000000..1aeb0ff --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs @@ -0,0 +1,62 @@ +using System; +using AnakinRaW.CommonUtilities; +using AnakinRaW.CommonUtilities.Collections; +using PG.Commons.Collections; +using PG.Commons.Hashing; + +namespace PG.StarWarsGame.Engine.Rendering; + +public sealed class AnimationCollection : DisposableObject +{ + private readonly ValueListDictionary _animations = new(); + private readonly ValueListDictionary _animationCrc = new(); + + public int Cout => _animations.Count; + + public Crc32 GetAnimationCrc(ModelAnimationType type, int subIndex) + { + if (subIndex < 0) + throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be negative."); + var checksumsForType = _animationCrc.GetValues(type); + if (subIndex >= checksumsForType.Count) + throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be larger than stored animations."); + return checksumsForType[subIndex]; + } + + public ReadOnlyFrugalList GetAnimations(ModelAnimationType type) + { + return _animations.GetValues(type); + } + + public bool TryGetAnimations(ModelAnimationType type, out ReadOnlyFrugalList animations) + { + return _animations.TryGetValues(type, out animations); + } + + public object GetAnimation(ModelAnimationType type, int subIndex) + { + if (subIndex < 0) + throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be negative."); + var animations = GetAnimations(type); + if (subIndex >= animations.Count) + throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be larger than stored animations."); + return animations[subIndex]; + } + + internal void AddAnimation(ModelAnimationType type, object animation, Crc32 crc) + { + _animations.Add(type, animation); + _animationCrc.Add(type, crc); + } + + protected override void DisposeResources() + { + foreach (var animation in _animations) + { + if (animation.Value is IDisposable disposable) + disposable.Dispose(); + } + //_animationCrc.Clear(); + //_animations.Clear(); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs new file mode 100644 index 0000000..2b67ed4 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs @@ -0,0 +1,14 @@ +using System; +using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Files; + +namespace PG.StarWarsGame.Engine.Rendering; + +public interface IPGRender +{ + IAloFile? Load3DAsset(string path, bool metadataOnly = true); + + IAloFile? Load3DAsset(ReadOnlySpan path, bool metadataOnly = true); + + ModelClass? LoadModelAndAnimations(ReadOnlySpan path, string? animOverrideName, bool metadataOnly = true); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs new file mode 100644 index 0000000..d21c1da --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs @@ -0,0 +1,124 @@ +namespace PG.StarWarsGame.Engine.Rendering; + +public enum ModelAnimationType +{ + 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, + 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/ModelClass.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs index 899c23f..f46cd3b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs @@ -1,15 +1,36 @@ using System; +using System.Diagnostics.CodeAnalysis; using AnakinRaW.CommonUtilities; +using PG.StarWarsGame.Files; using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Files; namespace PG.StarWarsGame.Engine.Rendering; -internal sealed class ModelClass(AlamoModel model) : DisposableObject +public sealed class ModelClass : DisposableObject { - public AlamoModel Model { get; } = model ?? throw new ArgumentNullException(nameof(model)); + public IAloDataContent RenderableContent { get; } + + public IPetroglyphFileHolder File { get; } + + public AlamoModel? Model { get; } + + [MemberNotNullWhen(true, nameof(Model))] + public bool IsModel => Model is not null; + + public AnimationCollection Animations { get; } = new(); + + public ModelClass(IAloFile aloFile) + { + File = aloFile; + RenderableContent = aloFile.Content; + Model = aloFile.Content as AlamoModel; + } public int IndexOfBone(string boneName) { + if (!IsModel) + return -1; var bones = Model.Bones; for (var i = 0; i < bones.Count; i++) { @@ -21,6 +42,7 @@ public int IndexOfBone(string boneName) protected override void DisposeResources() { - Model.Dispose(); + File.Dispose(); + Animations.Dispose(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index 6b8134f..77411e2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -1,24 +1,256 @@ -using System; +using AnakinRaW.CommonUtilities.FileSystem; using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.IO.Repositories; +using PG.StarWarsGame.Engine.Utilities; +using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Files; using PG.StarWarsGame.Files.ALO.Services; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions; +using PG.Commons.Hashing; namespace PG.StarWarsGame.Engine.Rendering; -internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + : IPGRender { private readonly IAloFileService _aloFileService = serviceProvider.GetRequiredService(); private readonly IRepository _modelRepository = gameRepository.ModelRepository; + private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); + private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); - public ModelClass? LoadModelAndAnimations(string path, bool metadataOnly = true) + [SuppressMessage("ReSharper", "StringLiteralTypo")] + private static readonly Dictionary AnimationTypeToName = new() { - if (string.IsNullOrEmpty(path)) + { 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"}, + { 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"}, + }; + + public IAloFile? Load3DAsset(string path, bool metadataOnly = true) + { + return Load3DAsset(path.AsSpan(), metadataOnly); + } + + public IAloFile? Load3DAsset(ReadOnlySpan path, bool metadataOnly = true) + { + if (path.IsEmpty) errorReporter.Assert(EngineAssert.FromNullOrEmpty(null, "Model path is null or empty.")); using var aloStream = _modelRepository.TryOpenFile(path); + if (aloStream is null) + return null; + + var loadOptions = metadataOnly ? AloLoadOptions.MetadataOnly : AloLoadOptions.Full; + return _aloFileService.Load(aloStream, loadOptions); + } + + public ModelClass? LoadModelAndAnimations(ReadOnlySpan path, string? animOverrideName, bool metadataOnly = true) + { + var aloFile = Load3DAsset(path, metadataOnly); + + if (aloFile is null) + return null; + + var modelClass = new ModelClass(aloFile); + + if (!modelClass.IsModel) + return modelClass; + + var dir = _fileSystem.Path.GetDirectoryName(path); + var fileName = _fileSystem.Path.GetFileNameWithoutExtension(path); - return null; + if (!string.IsNullOrEmpty(animOverrideName)) + fileName = _fileSystem.Path.GetFileNameWithoutExtension(animOverrideName.AsSpan()); + + Span stringBuffer = stackalloc char[256]; + + foreach (ModelAnimationType animType in Enum.GetValues(typeof(ModelAnimationType))) + { + var subIndex = 0; + var loadingNumberedAnimations = true; + + var animName = AnimationTypeToName[animType]; + + while (loadingNumberedAnimations) + { + var stringBuilder = new ValueStringBuilder(stringBuffer); + + CreateAnimationFilePath(ref stringBuilder, fileName, animName, subIndex); + var animationFilenameWithoutExtension = + _fileSystem.Path.GetFileNameWithoutExtension(stringBuilder.AsSpan()); + InsertPath(ref stringBuilder, dir); + + if (stringBuilder.Length > PGConstants.MaxAnimationFileName) + { + var animFile = stringBuilder.AsSpan().ToString(); + var model = path.ToString(); + errorReporter.Assert( + EngineAssert.Create(EngineAssertKind.ValueOutOfRange, animFile, model, + $"Cannot get animation file '{animFile}' for model '{model}', because animation file path is too long.")); + return null; + } + var animationFile = Load3DAsset(stringBuilder.AsSpan(), metadataOnly); + if (animationFile is not null) + { + loadingNumberedAnimations = true; + var crc = _hashingService.GetCrc32(animationFilenameWithoutExtension, PGConstants.DefaultPGEncoding); + modelClass.Animations.AddAnimation(animType, animationFile, crc); + } + else + { + loadingNumberedAnimations = false; + } + + stringBuilder.Dispose(); + subIndex++; + } + } + return modelClass; + } + + private void InsertPath(ref ValueStringBuilder stringBuilder, ReadOnlySpan directory) + { + if (!_fileSystem.Path.HasTrailingDirectorySeparator(directory)) + stringBuilder.Insert(0, '\\', 1); + stringBuilder.Insert(0, directory); + } + + private static void CreateAnimationFilePath( + ref ValueStringBuilder stringBuilder, + ReadOnlySpan fileName, + string animationTypeName, + int subIndex) + { + stringBuilder.Append(fileName); + stringBuilder.Append('_'); + stringBuilder.Append(animationTypeName); + stringBuilder.Append('_'); +#if NETSTANDARD2_0 || NETFRAMEWORK + stringBuilder.Append(subIndex.ToString("D2")); +#else + subIndex.TryFormat(stringBuilder.AppendSpan(2), out var n, "D2"); +#endif + stringBuilder.Append(".ALA"); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs index 8701551..0c6b95c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Utilities/ValueStringBuilder.cs @@ -85,7 +85,7 @@ public ref char this[int index] public override string ToString() { - string s = _chars.Slice(0, _pos).ToString(); + var s = _chars.Slice(0, _pos).ToString(); Dispose(); return s; } @@ -134,41 +134,41 @@ public void Insert(int index, char value, int count) Grow(count); } - int remaining = _pos - index; + var remaining = _pos - index; _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); _chars.Slice(index, count).Fill(value); _pos += count; } - public void Insert(int index, string? s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Insert(int index, scoped ReadOnlySpan value) { - if (s == null) - { + if (value.IsEmpty) return; - } - int count = s.Length; + var count = value.Length; - if (_pos > (_chars.Length - count)) - { + if (_pos > _chars.Length - count) Grow(count); - } - int remaining = _pos - index; + var remaining = _pos - index; _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); - s -#if !NETCOREAPP - .AsSpan() -#endif - .CopyTo(_chars.Slice(index)); + value.CopyTo(_chars.Slice(index)); _pos += count; } + public void Insert(int index, string? s) + { + if (s == null) + return; + Insert(index, s.AsSpan()); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(char c) { - int pos = _pos; - Span chars = _chars; + var pos = _pos; + var chars = _chars; if ((uint)pos < (uint)chars.Length) { chars[pos] = c; @@ -188,7 +188,7 @@ public void Append(string? s) return; } - int pos = _pos; + var pos = _pos; if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. { _chars[pos] = s[0]; @@ -202,7 +202,7 @@ public void Append(string? s) private void AppendSlow(string s) { - int pos = _pos; + var pos = _pos; if (pos > _chars.Length - s.Length) { Grow(s.Length); @@ -223,8 +223,8 @@ public void Append(char c, int count) Grow(count); } - Span dst = _chars.Slice(_pos, count); - for (int i = 0; i < dst.Length; i++) + var dst = _chars.Slice(_pos, count); + for (var i = 0; i < dst.Length; i++) { dst[i] = c; } @@ -233,14 +233,14 @@ public void Append(char c, int count) public unsafe void Append(char* value, int length) { - int pos = _pos; + var pos = _pos; if (pos > _chars.Length - length) { Grow(length); } - Span dst = _chars.Slice(_pos, length); - for (int i = 0; i < dst.Length; i++) + var dst = _chars.Slice(_pos, length); + for (var i = 0; i < dst.Length; i++) { dst[i] = *value++; } @@ -249,7 +249,7 @@ public unsafe void Append(char* value, int length) public void Append(scoped ReadOnlySpan value) { - int pos = _pos; + var pos = _pos; if (pos > _chars.Length - value.Length) { Grow(value.Length); @@ -262,7 +262,7 @@ public void Append(scoped ReadOnlySpan value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span AppendSpan(int length) { - int origPos = _pos; + var origPos = _pos; if (origPos > _chars.Length - length) { Grow(length); @@ -297,17 +297,17 @@ private void Grow(int additionalCapacityBeyondPos) // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try // to double the size if possible, bounding the doubling to not go beyond the max array length. - int newCapacity = (int)Math.Max( + var newCapacity = (int)Math.Max( (uint)(_pos + additionalCapacityBeyondPos), Math.Min((uint)_chars.Length * 2, ArrayMaxLength)); // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative. // This could also go negative if the actual required length wraps around. - char[] poolArray = ArrayPool.Shared.Rent(newCapacity); + var poolArray = ArrayPool.Shared.Rent(newCapacity); _chars.Slice(0, _pos).CopyTo(poolArray); - char[]? toReturn = _arrayToReturnToPool; + var toReturn = _arrayToReturnToPool; _chars = _arrayToReturnToPool = poolArray; if (toReturn != null) { @@ -348,7 +348,7 @@ public void Remove(int startIndex, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - char[]? toReturn = _arrayToReturnToPool; + var toReturn = _arrayToReturnToPool; this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again if (toReturn != null) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/AloFileReaderFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/AloFileReaderFactory.cs index 48a5607..f6324f8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/AloFileReaderFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/AloFileReaderFactory.cs @@ -1,6 +1,9 @@ using System; using System.IO; using PG.StarWarsGame.Files.ALO.Binary.Reader; +using PG.StarWarsGame.Files.ALO.Binary.Reader.Animations; +using PG.StarWarsGame.Files.ALO.Binary.Reader.Models; +using PG.StarWarsGame.Files.ALO.Binary.Reader.Particles; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; using PG.StarWarsGame.Files.ALO.Services; @@ -11,10 +14,20 @@ internal class AloFileReaderFactory(IServiceProvider serviceProvider) : IAloFile { public IAloFileReader GetReader(AloContentInfo contentInfo, Stream dataStream, AloLoadOptions loadOptions) { - if (contentInfo.Type == AloType.Model) - return new ModelFileReader(loadOptions, dataStream); - if (contentInfo.Version == AloVersion.V1) - return new ParticleReaderV1(loadOptions, dataStream); - return new ParticleReaderV2(loadOptions, dataStream); + switch (contentInfo.Type) + { + case AloType.Model: + return new ModelFileReader(loadOptions, dataStream); + case AloType.Animation when contentInfo.Version == AloVersion.V1: + return new AnimationReaderV1(loadOptions, dataStream); + case AloType.Animation: + return new AnimationReaderV2(loadOptions, dataStream); + case AloType.Particle when contentInfo.Version == AloVersion.V1: + return new ParticleReaderV1(loadOptions, dataStream); + case AloType.Particle: + return new ParticleReaderV2(loadOptions, dataStream); + default: + throw new NotSupportedException($"ALO content type {contentInfo.Type} is not supported."); + } } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs index ea13493..6fce5c0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/AloContentInfoIdentifier.cs @@ -26,11 +26,36 @@ public AloContentInfo GetContentInfo(Stream stream) return new AloContentInfo(AloType.Particle, AloVersion.V1); case ChunkType.ParticleUaW: return new AloContentInfo(AloType.Particle, AloVersion.V2); + case ChunkType.Animation: + return FromAnimation(chunkReader); default: throw new BinaryCorruptedException("Unable to get ALO content information."); } } + private static AloContentInfo FromAnimation(ChunkReader chunkReader) + { + var chunk = chunkReader.TryReadChunk(); + while (chunk.HasValue) + { + switch ((ChunkType)chunk.Value.Type) + { + case ChunkType.AnimationInformation: + return chunk.Value.Size switch + { + 36 => new AloContentInfo(AloType.Animation, AloVersion.V2), + 18 => new AloContentInfo(AloType.Animation, AloVersion.V1), + _ => throw new BinaryCorruptedException("Invalid ALA animation.") + }; + default: + chunkReader.Skip(chunk.Value.Size); + break; + } + chunk = chunkReader.TryReadChunk(); + } + throw new BinaryCorruptedException("Invalid ALA animation."); + } + private static AloContentInfo FromConnection(ChunkReader chunkReader) { var chunk = chunkReader.TryReadChunk(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/IAloContentInfoIdentifier.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/IAloContentInfoIdentifier.cs index 5853410..591a54c 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/IAloContentInfoIdentifier.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Identifier/IAloContentInfoIdentifier.cs @@ -3,7 +3,7 @@ namespace PG.StarWarsGame.Files.ALO.Binary.Identifier; -interface IAloContentInfoIdentifier +internal interface IAloContentInfoIdentifier { AloContentInfo GetContentInfo(Stream stream); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationChunkTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationChunkTypes.cs new file mode 100644 index 0000000..4382ac2 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationChunkTypes.cs @@ -0,0 +1,19 @@ +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Animations; + +public enum AnimationChunkTypes +{ + NumFrames = 0x01, + Fps = 0x02, + NumBones = 0x03, + BoneName = 0x04, + BoneIndex = 0x05, + RotationBlockSize = 0x0b, + TranslationBlockSize = 0x0c, + ScaleBlockSize = 0x0d, + AnimationFile = 0x1000, + AnimationInfo = 0x1001, + BoneData = 0x1002, + BoneDataInfo = 0x1003, + BoneVisibility = 0x1007, + BoneStepKeyTrack = 0x1008, +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationInformationData.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationInformationData.cs new file mode 100644 index 0000000..529e41b --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationInformationData.cs @@ -0,0 +1,11 @@ +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Animations; + +internal ref struct AnimationInformationData +{ + public float FPS; + public uint NumberFrames; + public uint NumberBones; + public uint RotationBlockSize; + public uint TranslationBlockSize; + public uint ScaleBlockSize; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderBase.cs new file mode 100644 index 0000000..632caf3 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderBase.cs @@ -0,0 +1,170 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Services; +using PG.StarWarsGame.Files.Binary; +using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; + +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Animations; + +internal abstract class AnimationReaderBase(AloLoadOptions loadOptions, Stream stream) + : AloFileReader(loadOptions, stream) +{ + public sealed override AlamoAnimation Read() + { + var bones = new List(); + AnimationInformationData info = default; + + var rootChunk = ChunkReader.ReadChunk(); + + if (rootChunk is not { Type: (int)AnimationChunkTypes.AnimationFile }) + throw new BinaryCorruptedException("Unable to read animation file."); + + var actualSize = 0; + + do + { + var chunk = ChunkReader.ReadChunk(ref actualSize); + ReadAnimation(chunk, ref info, bones); + actualSize += chunk.Size; + + } while (actualSize < rootChunk.Size); + + if (actualSize != rootChunk.Size) + throw new BinaryCorruptedException(); + + if (info.NumberBones != bones.Count) + throw new BinaryCorruptedException("The number of bones does not match the number of bone data."); + + return new AlamoAnimation + { + FPS = info.FPS, + NumberBones = info.NumberBones, + NumberFrames = info.NumberFrames, + BoneData = bones, + }; + } + + protected virtual void ReadAnimation( + ChunkMetadata chunk, + ref AnimationInformationData animationInformation, + List bones) + { + switch (chunk.Type) + { + case (int)AnimationChunkTypes.AnimationInfo: + animationInformation = ReadAnimationInfo(chunk.Size); + break; + case (int)AnimationChunkTypes.BoneData: + ReadBonesData(chunk.Size, bones); + break; + default: + ChunkReader.Skip(chunk.Size); + break; + } + } + + protected virtual void ReadBoneDataCore(ChunkMetadata chunk, List bones) + { + switch (chunk.Type) + { + case (int)AnimationChunkTypes.BoneDataInfo: + ReadBoneInfo(chunk.Size, bones); + break; + default: + ChunkReader.Skip(chunk.Size); + break; + } + } + + private void ReadBonesData(int chunkSize, List bones) + { + var actualSize = 0; + + do + { + var chunk = ChunkReader.ReadChunk(ref actualSize); + ReadBoneDataCore(chunk, bones); + actualSize += chunk.Size; + + } while (actualSize < chunkSize); + + if (actualSize != chunkSize) + throw new BinaryCorruptedException("Unable to read particle"); + } + + private void ReadBoneInfo(int chunkSize, List bones) + { + var actualSize = 0; + + string name = null!; + uint index = 0; + + do + { + var chunk = ChunkReader.ReadMiniChunk(ref actualSize); + + switch (chunk.Type) + { + case (int)AnimationChunkTypes.BoneName: + name = ChunkReader.ReadString(chunk.Size, Encoding.ASCII, true, ref actualSize); + break; + case (int)AnimationChunkTypes.BoneIndex: + index = ChunkReader.ReadDword(ref actualSize); + break; + default: + ChunkReader.Skip(chunk.Size, ref actualSize); + break; + } + + } while (actualSize < chunkSize); + + if (actualSize != chunkSize) + throw new BinaryCorruptedException("Unable to read particle"); + + bones.Add(new AnimationBoneData(index, name)); + } + + private AnimationInformationData ReadAnimationInfo(int chunkSize) + { + var actualSize = 0; + + var info = new AnimationInformationData(); + do + { + var chunk = ChunkReader.ReadMiniChunk(ref actualSize); + + switch (chunk.Type) + { + case (int)AnimationChunkTypes.NumFrames: + info.NumberFrames = ChunkReader.ReadDword(ref actualSize); + break; + case (int)AnimationChunkTypes.Fps: + info.FPS = ChunkReader.ReadFloat(ref actualSize); + break; + case (int)AnimationChunkTypes.NumBones: + info.NumberBones = ChunkReader.ReadDword(ref actualSize); + break; + case (int)AnimationChunkTypes.TranslationBlockSize: + info.TranslationBlockSize = ChunkReader.ReadDword(ref actualSize); + break; + case (int)AnimationChunkTypes.RotationBlockSize: + info.RotationBlockSize = ChunkReader.ReadDword(ref actualSize); + break; + case (int)AnimationChunkTypes.ScaleBlockSize: + info.ScaleBlockSize = ChunkReader.ReadDword(ref actualSize); + break; + default: + ChunkReader.Skip(chunk.Size, ref actualSize); + break; + } + + } while (actualSize < chunkSize); + + if (actualSize != chunkSize) + throw new BinaryCorruptedException("Unable to read animation"); + + return info; + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV1.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV1.cs new file mode 100644 index 0000000..838fc86 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV1.cs @@ -0,0 +1,6 @@ +using PG.StarWarsGame.Files.ALO.Services; +using System.IO; + +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Animations; + +internal class AnimationReaderV1(AloLoadOptions loadOptions, Stream stream) : AnimationReaderBase(loadOptions, stream); \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV2.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV2.cs new file mode 100644 index 0000000..e6d1eb4 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Animations/AnimationReaderV2.cs @@ -0,0 +1,6 @@ +using System.IO; +using PG.StarWarsGame.Files.ALO.Services; + +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Animations; + +internal class AnimationReaderV2(AloLoadOptions loadOptions, Stream stream) : AnimationReaderBase(loadOptions, stream); \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelChunkTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelChunkTypes.cs new file mode 100644 index 0000000..c15818e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelChunkTypes.cs @@ -0,0 +1,19 @@ +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Models; + +public enum ModelChunkTypes +{ + Name = 0x0, + Id = 0x1, + Skeleton = 0x200, + BoneCount = 0x201, + Bone = 0x202, + BoneName = 0x203, + Mesh = 0x400, + Connections = 0x600, + ProxyConnection = 0x603, + Particle = 0x900, + Animation = 0x1000, + SubMeshMaterialInformation = 0x00010100, + ShaderFileName = 0x00010101, + ShaderTexture = 0x00010105, +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelFileReader.cs similarity index 89% rename from src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelFileReader.cs index 9e2df93..13da425 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ModelFileReader.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Models/ModelFileReader.cs @@ -6,9 +6,8 @@ using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Services; using PG.StarWarsGame.Files.Binary; -using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; -namespace PG.StarWarsGame.Files.ALO.Binary.Reader; +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Models; internal class ModelFileReader(AloLoadOptions loadOptions, Stream stream) : AloFileReader(loadOptions, stream) { @@ -25,13 +24,13 @@ public override AlamoModel Read() { switch (chunk.Value.Type) { - case (int)ChunkType.Skeleton: + case (int)ModelChunkTypes.Skeleton: ReadSkeleton(chunk.Value.Size, bones); break; - case (int)ChunkType.Mesh: + case (int)ModelChunkTypes.Mesh: ReadMesh(chunk.Value.Size, textures, shaders); break; - case (int)ChunkType.Connections: + case (int)ModelChunkTypes.Connections: ReadConnections(chunk.Value.Size, proxies); break; default: @@ -61,7 +60,7 @@ private void ReadConnections(int size, HashSet proxies) { var chunk = ChunkReader.ReadChunk(ref actualSize); - if (chunk.Type == (int)ChunkType.ProxyConnection) + if (chunk.Type == (int)ModelChunkTypes.ProxyConnection) { ReadProxy(chunk.Size, out var proxy, ref actualSize); proxies.Add(proxy); @@ -110,7 +109,7 @@ private void ReadMesh(int size, ISet textures, ISet shaders) { var chunk = ChunkReader.ReadChunk(ref actualSize); - if (chunk.Type == (int)ChunkType.SubMeshMaterialInformation) + if (chunk.Type == (int)ModelChunkTypes.SubMeshMaterialInformation) ReadSubMeshMaterialInformation(chunk.Size, textures, shaders, ref actualSize); else ChunkReader.Skip(chunk.Size, ref actualSize); @@ -131,13 +130,13 @@ private void ReadSubMeshMaterialInformation(int size, ISet textures, ISe switch (chunk.Type) { - case (int)ChunkType.ShaderFileName: + case (int)ModelChunkTypes.ShaderFileName: { var shader = ChunkReader.ReadString(chunk.Size, Encoding.ASCII, true, ref actualSize); shaders.Add(shader); break; } - case (int)ChunkType.ShaderTexture: + case (int)ModelChunkTypes.ShaderTexture: ReadShaderTexture(chunk.Size, textures, ref actualSize); break; default: @@ -192,7 +191,7 @@ private void ReadSkeleton(int size, IList bones) var boneCountChunk = ChunkReader.ReadChunk(ref actualSize); - Debug.Assert(boneCountChunk is { Size: 128, Type: (int)ChunkType.BoneCount }); + Debug.Assert(boneCountChunk is { Size: 128, Type: (int)ModelChunkTypes.BoneCount }); var boneCount = ChunkReader.ReadDword(ref actualSize); @@ -202,7 +201,7 @@ private void ReadSkeleton(int size, IList bones) { var bone = ChunkReader.ReadChunk(ref actualSize); - Debug.Assert(bone is { Type: (int)ChunkType.Bone, IsContainer: true }); + Debug.Assert(bone is { Type: (int)ModelChunkTypes.Bone, IsContainer: true }); var boneReadSize = 0; @@ -210,7 +209,7 @@ private void ReadSkeleton(int size, IList bones) { var innerBoneChunk = ChunkReader.ReadChunk(ref boneReadSize); - if (innerBoneChunk.Type == (int)ChunkType.BoneName) + if (innerBoneChunk.Type == (int)ModelChunkTypes.BoneName) { var nameSize = innerBoneChunk.Size; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleChunkType.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleChunkType.cs new file mode 100644 index 0000000..19c2f64 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleChunkType.cs @@ -0,0 +1,18 @@ +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Particles; + +public enum ParticleChunkType +{ + Name = 0x0, + Id = 0x1, + Properties = 0x2, + ColorTextureName = 0x3, + BumpTextureName = 0x45, + Bone = 0x202, + BoneName = 0x203, + Mesh = 0x400, + Connections = 0x600, + Emitter = 0x700, + Emitters = 0x800, + Particle = 0x900, + Animation = 0x1000, +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleReaderV1.cs similarity index 86% rename from src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleReaderV1.cs index 200a918..ed6de4b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV1.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleReaderV1.cs @@ -5,9 +5,8 @@ using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Services; using PG.StarWarsGame.Files.Binary; -using PG.StarWarsGame.Files.ChunkFiles.Binary.Metadata; -namespace PG.StarWarsGame.Files.ALO.Binary.Reader; +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Particles; internal class ParticleReaderV1(AloLoadOptions loadOptions, Stream stream) : AloFileReader(loadOptions, stream) { @@ -19,7 +18,7 @@ public override AlamoParticle Read() var rootChunk = ChunkReader.ReadChunk(); - if (rootChunk.Type != (int)ChunkType.Particle) + if (rootChunk.Type != (int)ParticleChunkType.Particle) throw new NotSupportedException("This reader only support V1 particles."); var actualSize = 0; @@ -30,10 +29,10 @@ public override AlamoParticle Read() switch (chunk.Type) { - case (int)ChunkType.Name: + case (int)ParticleChunkType.Name: ReadName(chunk.Size, out name); break; - case (int)ChunkType.Emitters: + case (int)ParticleChunkType.Emitters: ReadEmitters(chunk.Size, textures); break; default: @@ -68,7 +67,7 @@ private void ReadEmitters(int size, HashSet textures) { var chunk = ChunkReader.ReadChunk(ref actualSize); - if (chunk.Type != (int)ChunkType.Emitter) + if (chunk.Type != (int)ParticleChunkType.Emitter) throw new BinaryCorruptedException("Unable to read particle"); ReadEmitter(chunk.Size, textures); @@ -90,17 +89,17 @@ private void ReadEmitter(int chunkSize, HashSet textures) { var chunk = ChunkReader.ReadChunk(ref actualSize); - if (chunk.Type == (int)ChunkType.Properties) + if (chunk.Type == (int)ParticleChunkType.Properties) { var shader = ChunkReader.ReadDword(); ChunkReader.Skip(chunk.Size - sizeof(uint)); } - else if (chunk.Type == (int)ChunkType.ColorTextureName) + else if (chunk.Type == (int)ParticleChunkType.ColorTextureName) { var texture = ChunkReader.ReadString(chunk.Size, Encoding.ASCII, true); textures.Add(texture); } - else if (chunk.Type == (int)ChunkType.BumpTextureName) + else if (chunk.Type == (int)ParticleChunkType.BumpTextureName) { var bump = ChunkReader.ReadString(chunk.Size, Encoding.ASCII, true); textures.Add(bump); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV2.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleReaderV2.cs similarity index 84% rename from src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV2.cs rename to src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleReaderV2.cs index fc79903..014f377 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/ParticleReaderV2.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Binary/Reader/Particles/ParticleReaderV2.cs @@ -3,7 +3,7 @@ using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Services; -namespace PG.StarWarsGame.Files.ALO.Binary.Reader; +namespace PG.StarWarsGame.Files.ALO.Binary.Reader.Particles; internal class ParticleReaderV2(AloLoadOptions loadOptions, Stream stream) : AloFileReader(loadOptions, stream) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoAnimation.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoAnimation.cs new file mode 100644 index 0000000..80938f4 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoAnimation.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace PG.StarWarsGame.Files.ALO.Data; + +public sealed class AlamoAnimation : IAloDataContent +{ + public float FPS { get; init; } + + public uint NumberFrames { get; init; } + + public uint NumberBones { get; init; } + + public IReadOnlyCollection BoneData { get; init; } + + public void Dispose() + { + } +} + +public readonly struct AnimationBoneData(uint index, string name) +{ + public uint BoneIndex { get; } = index; + + public string Name { get; } = name; +} diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoModel.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoModel.cs index 63a7089..86f7bf9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoModel.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoModel.cs @@ -2,7 +2,7 @@ namespace PG.StarWarsGame.Files.ALO.Data; -public class AlamoModel : IAloDataContent +public sealed class AlamoModel : IAloDataContent { public IList Bones { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoParticle.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoParticle.cs index b2d94cb..e810f3a 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoParticle.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Data/AlamoParticle.cs @@ -2,7 +2,7 @@ namespace PG.StarWarsGame.Files.ALO.Data; -public class AlamoParticle : IAloDataContent +public sealed class AlamoParticle : IAloDataContent { public required string Name { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs index 4e88947..cb70248 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloFileInformation.cs @@ -12,7 +12,9 @@ public sealed record AloFileInformation : PetroglyphMegPackableFileInformation public bool IsModel => ContentInfo.Type == AloType.Model; - public bool IsParticle => !IsModel; + public bool IsParticle => ContentInfo.Type == AloType.Particle; + + public bool IsAnimation => ContentInfo.Type == AloType.Animation; /// /// Initializes a new instance of the class. diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloType.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloType.cs index 2a152c3..cc32355 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/AloType.cs @@ -3,5 +3,6 @@ public enum AloType { Model, - Particle + Particle, + Animation } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/AloAnimationFile.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/AloAnimationFile.cs new file mode 100644 index 0000000..0ad88db --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/AloAnimationFile.cs @@ -0,0 +1,10 @@ +using System; +using PG.StarWarsGame.Files.ALO.Data; + +namespace PG.StarWarsGame.Files.ALO.Files.Animations; + +public sealed class AloAnimationFile( + AlamoAnimation animation, + AloFileInformation fileInformation, + IServiceProvider serviceProvider) + : PetroglyphFileHolder(animation, fileInformation, serviceProvider), IAloAnimationFile; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/IAloAnimationFile.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/IAloAnimationFile.cs new file mode 100644 index 0000000..f6bcaed --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Files/Animations/IAloAnimationFile.cs @@ -0,0 +1,5 @@ +using PG.StarWarsGame.Files.ALO.Data; + +namespace PG.StarWarsGame.Files.ALO.Files.Animations; + +public interface IAloAnimationFile : IAloFile; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs index d98e3f4..4eabac4 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloFileService.cs @@ -7,6 +7,7 @@ using PG.StarWarsGame.Files.ALO.Binary.Identifier; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.ALO.Files.Animations; using PG.StarWarsGame.Files.ALO.Files.Models; using PG.StarWarsGame.Files.ALO.Files.Particles; @@ -17,7 +18,7 @@ public class AloFileService(IServiceProvider serviceProvider) : ServiceBase(serv private readonly IAloFileReaderFactory _readerFactory = serviceProvider.GetRequiredService(); private readonly IAloContentInfoIdentifier _aloContentIdentifier = serviceProvider.GetRequiredService(); - public IAloFile Load(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full) + public IAloFile Load(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full) { return Load(stream, null, loadOptions); } @@ -32,7 +33,12 @@ public IAloParticleFile LoadParticle(Stream stream, AloLoadOptions loadOptions = return (IAloParticleFile)Load(stream, AloType.Particle, loadOptions); } - public IAloFile Load(Stream stream, AloType? supportedType = null, + public IAloAnimationFile LoadAnimation(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full) + { + return (IAloAnimationFile)Load(stream, AloType.Animation, loadOptions); + } + + public IAloFile Load(Stream stream, AloType? supportedType = null, AloLoadOptions loadOptions = AloLoadOptions.Full) { if (stream == null) @@ -63,6 +69,9 @@ public IAloFile Load(Stre if (alo is AlamoParticle particle) return new AloParticleFile(particle, fileInfo, Services); + if (alo is AlamoAnimation animation) + return new AloAnimationFile(animation, fileInfo, Services); + throw new InvalidOperationException(); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs index 84fed92..50aabc0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs @@ -20,4 +20,8 @@ public enum AloLoadOptions /// If the file is a model, this option gets the list of bones from the model. /// Bones = 2, + /// + /// Extracts only metadata from the model/particle + /// + MetadataOnly = Assets | Bones } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs index e0cd995..4632466 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/IAloFileService.cs @@ -1,18 +1,21 @@ -using System.IO; -using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.ALO.Files.Animations; using PG.StarWarsGame.Files.ALO.Files.Models; using PG.StarWarsGame.Files.ALO.Files.Particles; +using System.IO; namespace PG.StarWarsGame.Files.ALO.Services; public interface IAloFileService { - IAloFile Load(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full); + IAloFile Load(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full); IAloModelFile LoadModel(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full); IAloParticleFile LoadParticle(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full); + IAloAnimationFile LoadAnimation(Stream stream, AloLoadOptions loadOptions = AloLoadOptions.Full); + AloContentInfo GetAloContentInfo(Stream stream); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Metadata/ChunkType.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Metadata/ChunkType.cs index 9b34a66..70af55d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Metadata/ChunkType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Metadata/ChunkType.cs @@ -25,6 +25,8 @@ public enum ChunkType Emitters = 0x800, Particle = 0x900, Animation = 0x1000, + AnimationInformation = 0x1001, + AnimationBoneData = 0x1002, Light = 0x1300, ParticleUaW = 0x1500, SubMeshData = 0x00010000, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs index ca188c3..742924d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ChunkFiles/Binary/Reader/ChunkReader.cs @@ -53,6 +53,13 @@ public uint ReadDword(ref int readSize) return value; } + public float ReadFloat(ref int readSize) + { + var value = _binaryReader.ReadSingle(); + readSize += sizeof(float); + return value; + } + public uint ReadDword() { return _binaryReader.ReadUInt32(); From d03bcfc3bc902d6a9d3aa3a0e7650c2eb3fc27e3 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Mar 2025 22:47:54 +0100 Subject: [PATCH 18/34] support model loading --- .../GameErrorReporterWrapper.cs | 2 +- .../IO/Repositories/GameRepository.Files.cs | 2 +- .../PG.StarWarsGame.Engine.csproj | 4 ++-- .../Rendering/AnimationCollection.cs | 22 +++++++++---------- .../Rendering/PGRender.cs | 22 ++++++++++++++++--- .../PG.StarWarsGame.Files.ChunkFiles.csproj | 2 +- .../PG.StarWarsGame.Files.XML.csproj | 2 +- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs index 1946691..32359ef 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs @@ -44,7 +44,7 @@ public override void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs if (_errorReporter is null) return; - _logger?.LogWarning($"Xml parser '{parser}' reported error: {error}"); + _logger?.LogWarning($"Xml parser '{parser}' reported error: {error.Message}"); Report(new XmlError { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs index 8b151c9..c69851d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs @@ -97,7 +97,7 @@ protected FileFoundInfo GetFileInfoFromMasterMeg(ReadOnlySpan filePath) var crc = _crc32HashingService.GetCrc32(fileName, MegFileConstants.MegDataEntryPathEncoding); - var entry = MasterMegArchive!.FirstEntryWithCrc(crc); + var entry = MasterMegArchive!.EntriesWithCrc(crc).FirstOrDefault(); return new FileFoundInfo(entry); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 00d6fb1..dc0cc13 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -18,8 +18,8 @@ preview - - + + all diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs index 1aeb0ff..1c286bb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs @@ -3,12 +3,13 @@ using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Collections; using PG.Commons.Hashing; +using PG.StarWarsGame.Files.ALO.Files.Animations; namespace PG.StarWarsGame.Engine.Rendering; public sealed class AnimationCollection : DisposableObject { - private readonly ValueListDictionary _animations = new(); + private readonly ValueListDictionary _animations = new(); private readonly ValueListDictionary _animationCrc = new(); public int Cout => _animations.Count; @@ -23,17 +24,17 @@ public Crc32 GetAnimationCrc(ModelAnimationType type, int subIndex) return checksumsForType[subIndex]; } - public ReadOnlyFrugalList GetAnimations(ModelAnimationType type) + public ReadOnlyFrugalList GetAnimations(ModelAnimationType type) { return _animations.GetValues(type); } - public bool TryGetAnimations(ModelAnimationType type, out ReadOnlyFrugalList animations) + public bool TryGetAnimations(ModelAnimationType type, out ReadOnlyFrugalList animations) { return _animations.TryGetValues(type, out animations); } - public object GetAnimation(ModelAnimationType type, int subIndex) + public IAloAnimationFile GetAnimation(ModelAnimationType type, int subIndex) { if (subIndex < 0) throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be negative."); @@ -43,7 +44,7 @@ public object GetAnimation(ModelAnimationType type, int subIndex) return animations[subIndex]; } - internal void AddAnimation(ModelAnimationType type, object animation, Crc32 crc) + internal void AddAnimation(ModelAnimationType type, IAloAnimationFile animation, Crc32 crc) { _animations.Add(type, animation); _animationCrc.Add(type, crc); @@ -51,12 +52,9 @@ internal void AddAnimation(ModelAnimationType type, object animation, Crc32 crc) protected override void DisposeResources() { - foreach (var animation in _animations) - { - if (animation.Value is IDisposable disposable) - disposable.Dispose(); - } - //_animationCrc.Clear(); - //_animations.Clear(); + foreach (var animation in _animations.Values) + animation.Dispose(); + _animationCrc.Clear(); + _animations.Clear(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index 77411e2..8b32f5f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -11,7 +11,10 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; +using Microsoft.Extensions.Logging; using PG.Commons.Hashing; +using PG.StarWarsGame.Files.ALO.Files.Animations; +using PG.StarWarsGame.Files.Binary; namespace PG.StarWarsGame.Engine.Rendering; @@ -22,6 +25,7 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper private readonly IRepository _modelRepository = gameRepository.ModelRepository; private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); + private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(PGRender)); [SuppressMessage("ReSharper", "StringLiteralTypo")] private static readonly Dictionary AnimationTypeToName = new() @@ -162,7 +166,19 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper return null; var loadOptions = metadataOnly ? AloLoadOptions.MetadataOnly : AloLoadOptions.Full; - return _aloFileService.Load(aloStream, loadOptions); + + try + { + return _aloFileService.Load(aloStream, loadOptions); + } + catch (BinaryCorruptedException e) + { + var pathString = path.ToString(); + var errorMessage = $"Unable to load 3D asset '{pathString}': {e.Message}"; + _logger?.LogWarning(e, errorMessage); + errorReporter.Assert(EngineAssert.Create(EngineAssertKind.InvalidValue, pathString, null, errorMessage)); + return null; + } } public ModelClass? LoadModelAndAnimations(ReadOnlySpan path, string? animOverrideName, bool metadataOnly = true) @@ -210,8 +226,8 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper $"Cannot get animation file '{animFile}' for model '{model}', because animation file path is too long.")); return null; } - var animationFile = Load3DAsset(stringBuilder.AsSpan(), metadataOnly); - if (animationFile is not null) + var animationAsset = Load3DAsset(stringBuilder.AsSpan(), metadataOnly); + if (animationAsset is IAloAnimationFile animationFile) { loadingNumberedAnimations = true; var crc = _hashingService.GetCrc32(animationFilenameWithoutExtension, PGConstants.DefaultPGEncoding); 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 2d0c68c..d6a7939 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 @@ -16,7 +16,7 @@ snupkg - + 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 7a405bb..066e32d 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 @@ -17,7 +17,7 @@ true - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 6d18591ac0cf191ceb83fa99ec13425ef646fe22 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Mar 2025 23:03:46 +0100 Subject: [PATCH 19/34] better error reporting --- .../CommandBar/CommandBarGameManager.cs | 13 ++++++++++++- .../ErrorReporting/EngineAssertKind.cs | 3 ++- .../GuiDialogGameManager_Initialization.cs | 12 +++++++++++- .../PG.StarWarsGame.Engine/Rendering/PGRender.cs | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index e75411b..c8435a0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -18,6 +18,7 @@ using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; +using PG.StarWarsGame.Files.Binary; namespace PG.StarWarsGame.Engine.CommandBar; @@ -232,7 +233,17 @@ private void SetMegaTexture() // Note: The tag is not used by the engine var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{MegaTextureBaseName}.mtd"); using var megaTexture = GameRepository.TryOpenFile(mtdPath); - MegaTextureFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); + + 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, null, message)); + } _megaTextureExists = GameRepository.TextureRepository.FileExists($"{MegaTextureBaseName}.tga"); } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs index 37830c9..da0e52e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs @@ -4,5 +4,6 @@ public enum EngineAssertKind { NullOrEmptyValue, ValueOutOfRange, - InvalidValue + InvalidValue, + CorruptBinary } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index a186e5a..3890e5d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -10,6 +10,7 @@ using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Engine.Xml.Parsers.File; using PG.StarWarsGame.Engine.Xml.Tags; +using PG.StarWarsGame.Files.Binary; namespace PG.StarWarsGame.Engine.GuiDialog; @@ -146,7 +147,16 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs) } using var megaTexture = GameRepository.TryOpenFile(mtdPath); - MtdFile = megaTexture is null ? null : _mtdFileService.Load(megaTexture); + try + { + MtdFile = 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, null, message)); + } } if (guiDialogs.CompressedMegaTexture is null) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index 8b32f5f..e04870b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -176,7 +176,7 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper var pathString = path.ToString(); var errorMessage = $"Unable to load 3D asset '{pathString}': {e.Message}"; _logger?.LogWarning(e, errorMessage); - errorReporter.Assert(EngineAssert.Create(EngineAssertKind.InvalidValue, pathString, null, errorMessage)); + errorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, pathString, null, errorMessage)); return null; } } From fb6fe1a4cca6003458ee61d30cdd72921469fe26 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 3 Mar 2025 23:42:35 +0100 Subject: [PATCH 20/34] reporting --- .../IO/Repositories/GameRepository.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index 537ded5..f4f9f9f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -115,7 +115,10 @@ public void AddMegFile(string megFile) var megArchive = LoadMegArchive(megFile); if (megArchive is null) { - Logger.LogWarning($"Unable to find MEG file at '{megFile}'"); + if (IsSpeechMeg(megFile)) + Logger.LogDebug($"Unable to find Speech MEG file at '{megFile}'"); + else + Logger.LogWarning($"Unable to find MEG file at '{megFile}'"); return; } @@ -247,7 +250,10 @@ internal void Seal() using var megFileStream = TryOpenFile(megPath); if (megFileStream is not FileSystemStream fileSystemStream) { - Logger.LogWarning($"Unable to find MEG file '{megPath}'"); + if (IsSpeechMeg(megPath)) + Logger.LogDebug($"Unable to find Speech MEG file '{megPath}'"); + else + Logger.LogWarning($"Unable to find MEG file '{megPath}'"); return null; } @@ -259,6 +265,11 @@ internal void Seal() return megFile; } + private bool IsSpeechMeg(string megFile) + { + return FileSystem.Path.GetFileName(megFile.AsSpan()).EndsWith("Speech.meg".AsSpan(), StringComparison.OrdinalIgnoreCase); + } + private void ThrowIfSealed() { if (_sealed) From 27d0b906395f98ac55b50946a07ea2639358794e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Tue, 4 Mar 2025 11:03:56 +0100 Subject: [PATCH 21/34] nicer reporting --- .../GameFinder/GameFinderService.cs | 4 +- .../ModSelectors/ConsoleModSelector.cs | 21 ++++-- .../ModSelectors/IModSelector.cs | 5 +- .../ModSelectors/SettingsBasedModSelector.cs | 3 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 1 + src/ModVerify.CliApp/ModVerifyApp.cs | 6 +- src/ModVerify.CliApp/Program.cs | 70 ++++++++++++------- src/ModVerify/VerificationProvider.cs | 9 ++- 8 files changed, 78 insertions(+), 41 deletions(-) diff --git a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs index 2e5f5f4..78fe408 100644 --- a/src/ModVerify.CliApp/GameFinder/GameFinderService.cs +++ b/src/ModVerify.CliApp/GameFinder/GameFinderService.cs @@ -97,7 +97,7 @@ private GameFinderResult FindGames(IList detectors) if (result.GameLocation is null) throw new GameNotFoundException("Unable to find game installation: Wrong install path?"); - _logger?.LogTrace($"Found game installation: {result.GameIdentity} at {result.GameLocation.FullName}"); + _logger?.LogInformation($"Found game installation: {result.GameIdentity} at {result.GameLocation.FullName}"); var game = _gameFactory.CreateGame(result, CultureInfo.InvariantCulture); @@ -118,7 +118,7 @@ private GameFinderResult FindGames(IList detectors) if (!TryDetectGame(GameType.Eaw, fallbackDetectors, out var fallbackResult) || fallbackResult.GameLocation is null) throw new GameNotFoundException("Unable to find fallback game installation: Wrong install path?"); - _logger?.LogTrace($"Found fallback game installation: {fallbackResult.GameIdentity} at {fallbackResult.GameLocation.FullName}"); + _logger?.LogInformation($"Found fallback game installation: {fallbackResult.GameIdentity} at {fallbackResult.GameLocation.FullName}"); fallbackGame = _gameFactory.CreateGame(fallbackResult, CultureInfo.InvariantCulture); diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index 953b0fe..1e2bbd9 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -20,13 +20,15 @@ public override GameLocations Select(GameInstallationsSettings settings, out IPh return GetLocations(targetObject, gameResult, settings.AdditionalFallbackPaths); } - private IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResult) + private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResult) { var list = new List(); var game = finderResult.Game; list.Add(finderResult.Game); + Console.WriteLine("================="); + Console.WriteLine(); Console.WriteLine($"0: {game.Name}"); var counter = 1; @@ -52,6 +54,8 @@ private IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResu { var fallbackGame = finderResult.FallbackGame; list.Add(fallbackGame); + + Console.WriteLine("_________________"); Console.WriteLine($"{counter++}: {fallbackGame.Name}"); foreach (var mod in fallbackGame.Mods) @@ -72,12 +76,19 @@ private IPhysicalPlayableObject SelectPlayableObject(GameFinderResult finderResu } } - Console.WriteLine("Workshop Items:"); - foreach (var mod in workshopMods) + if (workshopMods.Count > 0) { - Console.WriteLine($"{counter++}:\t{mod.Name}"); - list.Add(mod); + Console.WriteLine("_________________"); + Console.WriteLine("Workshop Items:"); + foreach (var mod in workshopMods) + { + Console.WriteLine($"{counter++}:\t{mod.Name}"); + list.Add(mod); + } } + + Console.WriteLine(); + Console.WriteLine("================="); while (true) { diff --git a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs index 00f4c9e..f0b30a0 100644 --- a/src/ModVerify.CliApp/ModSelectors/IModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/IModSelector.cs @@ -6,5 +6,8 @@ namespace AET.ModVerifyTool.ModSelectors; internal interface IModSelector { - GameLocations? Select(GameInstallationsSettings settings, out IPhysicalPlayableObject? targetObject, out GameEngineType? actualEngineType); + GameLocations? Select( + GameInstallationsSettings settings, + out IPhysicalPlayableObject? targetObject, + out GameEngineType? actualEngineType); } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index c620aae..2c98ba2 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -10,7 +10,8 @@ internal class SettingsBasedModSelector(IServiceProvider serviceProvider) { public VerifyGameInstallationData CreateInstallationDataFromSettings(GameInstallationsSettings settings) { - var gameLocations = new ModSelectorFactory(serviceProvider).CreateSelector(settings) + var gameLocations = new ModSelectorFactory(serviceProvider) + .CreateSelector(settings) .Select(settings, out var targetObject, out var engineType); if (gameLocations is null) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 1ba6fa0..b91c1db 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -43,6 +43,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApp.cs index 5147f48..ec49d86 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApp.cs @@ -21,9 +21,11 @@ public async Task RunApplication() { var returnCode = 0; - var installData = new SettingsBasedModSelector(services).CreateInstallationDataFromSettings(settings.GameInstallationsSettings); + var installData = new SettingsBasedModSelector(services) + .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); - _logger?.LogInformation($"Verify install data: {installData}"); + Console.WriteLine(); + _logger?.LogDebug($"Verify install data: {installData}"); var verifyPipeline = new ModVerifyPipeline(installData.EngineType, installData.GameLocations, settings.GameVerifySettings, services); diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index ec15a53..2488fcb 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -15,7 +15,6 @@ using CommandLine; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; using PG.Commons; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Files.ALO; @@ -28,7 +27,9 @@ using Serilog; using Serilog.Events; using Serilog.Filters; +using Serilog.Sinks.SystemConsole.Themes; using Testably.Abstractions; +using ILogger = Serilog.ILogger; namespace AET.ModVerifyTool; @@ -36,6 +37,7 @@ internal class Program { private const string EngineParserNamespace = "PG.StarWarsGame.Engine.Xml.Parsers"; private const string ParserNamespace = "PG.StarWarsGame.Files.XML.Parsers"; + private const string GameInfrastructureNamespace = "PG.StarWarsGame.Infrastructure"; private static async Task Main(string[] args) { @@ -88,6 +90,14 @@ await parseResult.WithNotParsedAsync(e => logger?.LogCritical(e, e.Message); return e.HResult; } + finally + { +#if NET + await Log.CloseAndFlushAsync(); +#else + Log.CloseAndFlush(); +#endif + } } private static IServiceCollection CreateCoreServices(bool verboseLogging) @@ -154,49 +164,59 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem loggingBuilder.ClearProviders(); // ReSharper disable once RedundantAssignment - var logLevel = LogLevel.Information; + var logLevel = LogEventLevel.Information; #if DEBUG - logLevel = LogLevel.Debug; + logLevel = LogEventLevel.Debug; loggingBuilder.AddDebug(); #else if (verbose) { - logLevel = LogLevel.Debug; + logLevel = LogLevel.Verbose; loggingBuilder.AddDebug(); } #endif - loggingBuilder.SetMinimumLevel(logLevel); - - SetupFileLogging(loggingBuilder, fileSystem); - + var fileLogger = SetupFileLogging(fileSystem, logLevel); + loggingBuilder.AddSerilog(fileLogger); - loggingBuilder.AddFilter((category, level) => + var cLogger = new LoggerConfiguration() + .WriteTo.Async(c => { - if (level < logLevel) - return false; - if (string.IsNullOrEmpty(category)) - return false; - if (category.StartsWith(EngineParserNamespace) || category.StartsWith(ParserNamespace)) - return false; - return true; + c.Console(logLevel, theme: AnsiConsoleTheme.Code); }) - .AddSimpleConsole(); + .Filter.ByExcluding(x => + { + if (!x.Properties.TryGetValue("SourceContext", out var value)) + return true; + var source = value.ToString().AsSpan().Trim('\"'); + + if (source.StartsWith(EngineParserNamespace.AsSpan())) + return true; + if (source.StartsWith(ParserNamespace.AsSpan())) + return true; + if (source.StartsWith(GameInfrastructureNamespace.AsSpan())) + return true; + return false; + }) + .CreateLogger(); + loggingBuilder.AddSerilog(cLogger); } - private static void SetupFileLogging(ILoggingBuilder loggingBuilder, IFileSystem fileSystem) + private static ILogger SetupFileLogging(IFileSystem fileSystem, LogEventLevel minLevel) { var logPath = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), "ModVerify_log.txt"); - var logger = new LoggerConfiguration() + return new LoggerConfiguration() .Enrich.FromLogContext() - .MinimumLevel.Verbose() + .MinimumLevel.Is(minLevel) .Filter.ByExcluding(IsXmlParserLogging) - .WriteTo.RollingFile( - logPath, - outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}") + .WriteTo.Async(c => + { + c.RollingFile( + logPath, + outputTemplate: + "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}"); + }) .CreateLogger(); - - loggingBuilder.AddSerilog(logger); } private static bool IsXmlParserLogging(LogEvent logEvent) diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify/VerificationProvider.cs index 92aa2c9..ca0f42d 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify/VerificationProvider.cs @@ -10,10 +10,9 @@ internal class VerificationProvider(IServiceProvider serviceProvider) : IVerific { public IEnumerable GetAllDefaultVerifiers(IGameDatabase database, GameVerifySettings settings) { - 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 ReferencedTexturesVerifier(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 ReferencedTexturesVerifier(database, settings, serviceProvider); } } \ No newline at end of file From ab585935de3b83f9703d113e7c0fc5da0da7eb0f Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Wed, 5 Mar 2025 11:34:46 +0100 Subject: [PATCH 22/34] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index b91c1db..1d9ec6c 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -20,15 +20,15 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all From ea414cce9801324703e7bd22cd1a05e9aecbaacd Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Fri, 7 Mar 2025 16:23:07 +0100 Subject: [PATCH 23/34] start creating commandbar verifiers --- src/ModVerify/ModVerify.csproj.DotSettings | 1 + src/ModVerify/VerificationProvider.cs | 1 + .../CommandBar/CommandBarVerifier.Base.cs | 134 ++++++++++++++++++ .../Verifiers/DuplicateNameFinder.cs | 70 +++++++-- .../CommandBar/CommandBarConstants.cs | 7 + .../CommandBar/CommandBarGameManager.cs | 9 +- .../Components/CommandBarBaseComponent.cs | 2 +- .../CommandBar/ICommandBarGameManager.cs | 5 + .../SupportedCommandBarComponentData.cs | 2 +- .../Database/GameDatabase.cs | 2 +- .../Database/GameInitializer.cs | 2 +- .../Database/IGameDatabase.cs | 3 + 12 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarConstants.cs diff --git a/src/ModVerify/ModVerify.csproj.DotSettings b/src/ModVerify/ModVerify.csproj.DotSettings index b49eb73..fcd6f14 100644 --- a/src/ModVerify/ModVerify.csproj.DotSettings +++ b/src/ModVerify/ModVerify.csproj.DotSettings @@ -1,3 +1,4 @@  + True True True \ No newline at end of file diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify/VerificationProvider.cs index ca0f42d..b076d29 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify/VerificationProvider.cs @@ -14,5 +14,6 @@ public IEnumerable GetAllDefaultVerifiers(IGameDatabase databa yield return new DuplicateNameFinder(database, settings, serviceProvider); yield return new AudioFilesVerifier(database, settings, serviceProvider); yield return new ReferencedTexturesVerifier(database, settings, serviceProvider); + yield return new CommandBarVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs new file mode 100644 index 0000000..74be606 --- /dev/null +++ b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using AET.ModVerify.Reporting; +using AET.ModVerify.Settings; +using AnakinRaW.CommonUtilities.Collections; +using PG.StarWarsGame.Engine.CommandBar; +using PG.StarWarsGame.Engine.CommandBar.Components; +using PG.StarWarsGame.Engine.Database; + +namespace AET.ModVerify.Verifiers; + +public partial class CommandBarVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) + : GameVerifierBase(gameDatabase, settings, serviceProvider) +{ + public const string CommandBarNoShellsGroup = "CMDBAR00"; + public const string CommandBarManyShellsGroup = "CMDBAR01"; + public const string CommandBarNoShellsComponentInShellGroup = "CMDBAR02"; + public const string CommandBarDuplicateComponent = "CMDBAR03"; + public const string CommandBarUnsupportedComponent = "CMDBAR04"; + + public override string FriendlyName => "CommandBar Verifiers"; + + protected override void RunVerification(CancellationToken token) + { + VerifyCommandBarShellsGroups(); + VerifyCommandBarComponents(); + VerifyCommandBarModels(); + } +} + +partial class CommandBarVerifier +{ + private void VerifyCommandBarModels() + { + foreach (var component in Database.CommandBar.Components) + { + } + } +} + +partial class CommandBarVerifier +{ + private void VerifyCommandBarComponents() + { + var occupiedComponentIds = Enum.GetValues(typeof(CommandBarComponentId)) + .Cast() + .ToDictionary(value => value, _ => false); + + foreach (var component in Database.CommandBar.Components) + { + if (occupiedComponentIds[component.Id]) + { + AddError(VerificationError.Create(this, + CommandBarDuplicateComponent, + $"The CommandBar component '{component.Name}' with ID '{component.Id}' already exists.", + VerificationSeverity.Warning, + component.Name)); + } + + + // TODO: Foc supports more types. The verifier should be aware of that. + if (component.Id is CommandBarComponentId.None or CommandBarComponentId.Count) + { + AddError(VerificationError.Create(this, + CommandBarUnsupportedComponent, + $"The CommandBar component '{component.Name}' is not supported by the game.", + VerificationSeverity.Information, + component.Name)); + } + } + + // TODO: Foc supports more types. The verifier should be aware of that. + var missingComponents = occupiedComponentIds + .Where(x => x is { Value: false, Key: not CommandBarComponentId.None and not CommandBarComponentId.Count }) + .Select(x => x.Key); + + foreach (var componentId in missingComponents) + { + AddError(VerificationError.Create(this, + CommandBarUnsupportedComponent, + $"The CommandBar is missing the required component id, which is named '{componentId}' in XML.", + VerificationSeverity.Error, + componentId.ToString())); + } + } +} + +partial class CommandBarVerifier +{ + private void VerifyCommandBarShellsGroups() + { + var shellGroups = new FrugalList(); + foreach (var groupPair in Database.CommandBar.Groups) + { + if (groupPair.Key == CommandBarConstants.ShellGroupName) + { + shellGroups.Add(groupPair.Key); + VerifyShellGroup(groupPair.Value); + } + else if (groupPair.Key.Equals(CommandBarConstants.ShellGroupName, StringComparison.OrdinalIgnoreCase)) + { + shellGroups.Add(groupPair.Key); + } + } + + if (shellGroups.Count == 0) + AddError(VerificationError.Create(this, + CommandBarNoShellsGroup, + $"No CommandBarGroup '{CommandBarConstants.ShellGroupName}' found.", + VerificationSeverity.Error, + "GameCommandBar")); + + if (shellGroups.Count >= 1) + AddError(VerificationError.Create(this, + CommandBarManyShellsGroup, + $"Found more than one Shells CommandBarGroup. Mind that group names are case-sensitive. Correct name is '{CommandBarConstants.ShellGroupName}'", + VerificationSeverity.Warning, + shellGroups.Concat(["GameCommandBar"]))); + } + + private void VerifyShellGroup(CommandBarComponentGroup shellGroup) + { + foreach (var shellComponent in shellGroup.Components) + { + if (shellComponent is not CommandBarShellComponent || shellComponent.Type is not CommandBarComponentType.Shell) + AddError(VerificationError.Create(this, + CommandBarNoShellsComponentInShellGroup, + $"The CommandBar component '{shellComponent.Name}' is not a shell component, but part of the '{CommandBarConstants.ShellGroupName}' group.", + VerificationSeverity.Warning)); + } + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 0a60b50..ab77c7d 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -1,11 +1,15 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AnakinRaW.CommonUtilities.Collections; +using PG.Commons.Hashing; using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.Xml; +using PG.StarWarsGame.Files.MTD.Data; +using PG.StarWarsGame.Files.MTD.Files; namespace AET.ModVerify.Verifiers; @@ -15,41 +19,81 @@ public sealed class DuplicateNameFinder( IServiceProvider serviceProvider) : GameVerifierBase(gameDatabase, settings, serviceProvider) { - public override string FriendlyName => "Duplicate Definitions"; + public override string FriendlyName => "Duplicates"; protected override void RunVerification(CancellationToken token) { - CheckDatabaseForDuplicates(Database.GameObjectTypeManager, "GameObject"); - CheckDatabaseForDuplicates(Database.SfxGameManager, "SFXEvent"); - } + CheckXmlObjectsForDuplicates("GameObject", Database.GameObjectTypeManager); + CheckXmlObjectsForDuplicates("SFXEvent", Database.SfxGameManager); + + if (Database.GuiDialogManager.MtdFile is not null) + CheckXmlObjectsForDuplicates(Database.GuiDialogManager.MtdFile); - private void CheckDatabaseForDuplicates(IGameManager gameManager, string databaseName) where T : NamedXmlObject + if (Database.CommandBar.MegaTextureFile is not null) + { + if (!Database.CommandBar.MegaTextureFile.FilePath.Equals(Database.GuiDialogManager.MtdFile?.FileName)) + CheckXmlObjectsForDuplicates(Database.CommandBar.MegaTextureFile); + } + } + + private void CheckForDuplicateCrcEntries( + string sourceName, + TSource source, + Func> crcSelector, + Func> entrySelector, + Func, IEnumerable> entryToStringSelector, + Func, string, string> errorMessageCreator) { - foreach (var key in gameManager.EntryKeys) + foreach (var crc32 in crcSelector(source)) { - var entries = gameManager.GetEntries(key); + var entries = entrySelector(source, crc32); if (entries.Count > 1) { - var entryNames = entries.Select(x => x.Name); + var entryNames = entryToStringSelector(entries); AddError(VerificationError.Create( this, VerifierErrorCodes.DuplicateFound, - CreateDuplicateErrorMessage(databaseName, entries), - VerificationSeverity.Warning, + errorMessageCreator(entries, sourceName), + VerificationSeverity.Error, entryNames)); } } } - private string CreateDuplicateErrorMessage(string databaseName, ReadOnlyFrugalList entries) where T : NamedXmlObject + private void CheckXmlObjectsForDuplicates(IMtdFile mtdFile) + { + CheckForDuplicateCrcEntries( + mtdFile.FileName, + mtdFile, + mtd => mtd.Content.Select(x => x.Crc32), + (mtd, crc32) => mtd.Content.EntriesWithCrc(crc32), + list => list.Select(x => x.FileName), + CreateDuplicateMtdErrorMessage); + } + + private void CheckXmlObjectsForDuplicates(string databaseName, IGameManager gameManager) where T : NamedXmlObject + { + CheckForDuplicateCrcEntries( + databaseName, + gameManager, + manager => manager.EntryKeys, + (manager, crc32) => manager.GetEntries(crc32), + list => list.Select(x => x.Name), + CreateDuplicateXmlErrorMessage); + } + + private static string CreateDuplicateMtdErrorMessage(ReadOnlyFrugalList entries, string fileName) { var firstEntry = entries.First(); + return $"MTD File '{fileName}' has duplicate definitions for CRC ({firstEntry}): {string.Join(",", entries.Select(x => x.FileName))}"; + } + private static string CreateDuplicateXmlErrorMessage(ReadOnlyFrugalList entries, string databaseName) where T : NamedXmlObject + { + var firstEntry = entries.First(); var message = $"{databaseName} '{firstEntry.Name}' ({firstEntry.Crc32}) has duplicate definitions: "; - foreach (var entry in entries) message += $"['{entry.Name}' in {entry.Location.XmlFile}:{entry.Location.Line}] "; - return message.TrimEnd(); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarConstants.cs new file mode 100644 index 0000000..bec58ce --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarConstants.cs @@ -0,0 +1,7 @@ +namespace PG.StarWarsGame.Engine.CommandBar; + +public static class CommandBarConstants +{ + public const string MegaTextureBaseName = "MT_COMMANDBAR"; + public const string ShellGroupName = "Shells"; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index c8435a0..8f1dbd9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -31,8 +31,6 @@ internal class CommandBarGameManager( IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager { - public const string MegaTextureBaseName = "MT_COMMANDBAR"; - private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); private readonly Dictionary _groups = new(); @@ -134,7 +132,7 @@ private void LinkComponentsWithActions() private void LinkComponentsToShell() { - if (!Groups.TryGetValue("Shells", out var shellGroup)) + if (!Groups.TryGetValue(CommandBarConstants.ShellGroupName, out var shellGroup)) return; var modelCache = new Dictionary(); @@ -145,7 +143,6 @@ private void LinkComponentsToShell() foreach (var shellComponent in shellGroup.Components) { - // TODO: Create verifier that shell group only contains shell components if (LinkToShell(component, shellComponent as CommandBarShellComponent, modelCache)) break; } @@ -231,7 +228,7 @@ private void SetMegaTexture() 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", $"{MegaTextureBaseName}.mtd"); + var mtdPath = FileSystem.Path.Combine("DATA\\ART\\TEXTURES", $"{CommandBarConstants.MegaTextureBaseName}.mtd"); using var megaTexture = GameRepository.TryOpenFile(mtdPath); try @@ -244,7 +241,7 @@ private void SetMegaTexture() Logger?.LogError(e, message); ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, null, message)); } - _megaTextureExists = GameRepository.TextureRepository.FileExists($"{MegaTextureBaseName}.tga"); + _megaTextureExists = GameRepository.TextureRepository.FileExists($"{CommandBarConstants.MegaTextureBaseName}.tga"); } private void SetComponentGroup(IEnumerable components) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs index 1b69b9d..78e340b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs @@ -16,7 +16,7 @@ public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) public bool Hidden { get; internal set; } = xmlData.Hidden; public bool Disabled { get; } = xmlData.Disabled; public abstract CommandBarComponentType Type { get; } - public CommandBarComponentId Id { get; internal set; } + public CommandBarComponentId Id { get; internal set; } = CommandBarComponentId.None; public CommandBarComponentGroup? Group { get; internal set; } public CommandBarShellComponent? ParentShell { get; internal set; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs index 2d788c9..ec56c35 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs @@ -1,10 +1,15 @@ using System.Collections.Generic; using PG.StarWarsGame.Engine.CommandBar.Components; using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Files.MTD.Files; namespace PG.StarWarsGame.Engine.CommandBar; public interface ICommandBarGameManager : IGameManager { + IMtdFile? MegaTextureFile { get; } + ICollection Components { get; } + + IReadOnlyDictionary Groups { get; } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs index 1e7e2ee..5b09d6f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs @@ -4,7 +4,7 @@ namespace PG.StarWarsGame.Engine.CommandBar; internal static class SupportedCommandBarComponentData { - public static IReadOnlyDictionary SupportedComponents = + public static readonly IReadOnlyDictionary SupportedComponents = new Dictionary { { CommandBarComponentId.MainShell, "i_main_commandbar" }, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs index 94665b6..9a63eff 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs @@ -17,7 +17,7 @@ internal class GameDatabase : IGameDatabase public required IFontManager FontManager { get; init; } - public required ICommandBarGameManager CommandBarManager { get; init; } + public required ICommandBarGameManager CommandBar { get; init; } public required IGameRepository GameRepository { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs index a06c596..9158a0e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs @@ -99,7 +99,7 @@ public async Task InitializeAsync(GameErrorReporterWrapper errorR GameRepository = repository, GameConstants = gameConstants, GuiDialogManager = guiDialogs, - CommandBarManager = commandBarManager, + CommandBar = commandBarManager, GameObjectTypeManager = gameObjetTypeManager, SfxGameManager = sfxGameManager, InstalledLanguages = sfxGameManager.InstalledLanguages diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs index 224218f..ca18eec 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs @@ -7,6 +7,7 @@ using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; using System.Collections.Generic; +using PG.StarWarsGame.Engine.CommandBar; namespace PG.StarWarsGame.Engine.Database; @@ -22,6 +23,8 @@ public interface IGameDatabase IGuiDialogManager GuiDialogManager { get; } + ICommandBarGameManager CommandBar { get; } + IGameObjectTypeGameManager GameObjectTypeManager { get; } ISfxEventGameManager SfxGameManager { get; } From 36bfad3a5663314f20b344b8021fd4eb23e752cc Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 22 Mar 2025 19:10:09 +0100 Subject: [PATCH 24/34] updated reporting and fixes --- .../ModSelectors/ConsoleModSelector.cs | 5 +- src/ModVerify.CliApp/ModVerifyPipeline.cs | 3 +- .../Properties/launchSettings.json | 2 +- .../VerifyGamePipeline.cs | 39 +- src/ModVerify/IGameVerifier.cs | 5 + .../ConcurrentGameGameErrorReporter.cs | 2 +- .../Reporting/IDatabaseErrorCollection.cs | 2 +- .../Reporting/Json/JsonVerificationError.cs | 10 +- .../Engine/EngineErrorReporterBase.cs} | 7 +- .../Engine/GameAssertErrorReporter.cs | 6 +- .../Engine/InitializationErrorReporter.cs | 6 +- .../Engine/XmlParseErrorReporter.cs | 6 +- .../Reporters/Text/TextFileReporter.cs | 2 +- src/ModVerify/Reporting/SuppressionFilter.cs | 6 +- .../Reporting/VerificationBaseline.cs | 4 +- src/ModVerify/Reporting/VerificationError.cs | 62 +- .../Reporting/VerificationReportBroker.cs | 7 +- src/ModVerify/VerificationProvider.cs | 1 + src/ModVerify/Verifiers/AudioFilesVerifier.cs | 14 +- .../CommandBar/CommandBarVerifier.Base.cs | 107 +- .../{ => Commons}/ReferencedModelsVerifier.cs | 188 +- .../ReferencedTexturesVerifier.cs | 4 +- .../Verifiers/DuplicateNameFinder.cs | 6 +- .../{Engine => }/GameEngineErrorCollector.cs | 13 +- src/ModVerify/Verifiers/GameVerifierBase.cs | 92 +- .../ReferencedTexturesVerifier.GUI.cs | 10 +- .../Verifiers/VerificationErrorEventArgs.cs | 9 + .../CommandBar/CommandBarComponentId.cs | 1020 +------ .../CommandBar/CommandBarGameManager.cs | 19 +- .../CommandBar/EawCommandBarComponentIds.cs | 665 +++++ .../CommandBar/FocCommandBarComponentIds.cs | 970 ++++++ .../SupportedCommandBarComponentData.cs | 2612 +++++++++++------ .../GameErrorReporterWrapper.cs | 6 +- .../IO/Repositories/EffectsRepository.cs | 19 +- .../IO/Repositories/ModelRepository.cs | 18 +- .../IO/Utilities/PathExtensions.cs | 20 - .../{ => Animations}/AnimationCollection.cs | 31 +- .../Animations/EawModelAnimationTypes.cs | 109 + .../Animations/FocModelAnimationTypes.cs | 124 + .../Animations/ModelAnimationType.cs | 47 + .../SupportedModelAnimationTypes.cs | 251 ++ .../Rendering/IPGRender.cs | 28 +- .../Rendering/ModelAnimationType.cs | 124 - .../Rendering/ModelClass.cs | 23 +- .../Rendering/PGRender.cs | 243 +- .../Services/AloLoadOptions.cs | 2 +- 46 files changed, 4416 insertions(+), 2533 deletions(-) rename src/{ModVerify => ModVerify.CliApp}/VerifyGamePipeline.cs (77%) rename src/ModVerify/{Verifiers/Engine/InitializationErrorReporterBase.cs => Reporting/Reporters/Engine/EngineErrorReporterBase.cs} (85%) rename src/ModVerify/{Verifiers => Reporting/Reporters}/Engine/GameAssertErrorReporter.cs (90%) rename src/ModVerify/{Verifiers => Reporting/Reporters}/Engine/InitializationErrorReporter.cs (76%) rename src/ModVerify/{Verifiers => Reporting/Reporters}/Engine/XmlParseErrorReporter.cs (96%) rename src/ModVerify/Verifiers/{ => Commons}/ReferencedModelsVerifier.cs (59%) rename src/ModVerify/Verifiers/{ => Commons}/ReferencedTexturesVerifier.cs (84%) rename src/ModVerify/Verifiers/{Engine => }/GameEngineErrorCollector.cs (70%) create mode 100644 src/ModVerify/Verifiers/VerificationErrorEventArgs.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/EawCommandBarComponentIds.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/FocCommandBarComponentIds.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/{ => Animations}/AnimationCollection.cs (66%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/FocModelAnimationTypes.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/ModelAnimationType.cs create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index 1e2bbd9..670beee 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -5,6 +5,7 @@ using AET.ModVerifyTool.Options; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Infrastructure; +using PG.StarWarsGame.Infrastructure.Games; using PG.StarWarsGame.Infrastructure.Mods; namespace AET.ModVerifyTool.ModSelectors; @@ -56,7 +57,9 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin list.Add(fallbackGame); Console.WriteLine("_________________"); - Console.WriteLine($"{counter++}: {fallbackGame.Name}"); + Console.WriteLine(fallbackGame.Type == GameType.Eaw + ? $"{counter++}: {fallbackGame.Name} [Not yet supported]" + : $"{counter++}: {fallbackGame.Name}"); foreach (var mod in fallbackGame.Mods) { diff --git a/src/ModVerify.CliApp/ModVerifyPipeline.cs b/src/ModVerify.CliApp/ModVerifyPipeline.cs index a2a798c..1fca580 100644 --- a/src/ModVerify.CliApp/ModVerifyPipeline.cs +++ b/src/ModVerify.CliApp/ModVerifyPipeline.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using AET.ModVerify; using AET.ModVerify.Settings; -using AET.ModVerify.Verifiers; using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; @@ -16,7 +15,7 @@ internal class ModVerifyPipeline( IServiceProvider serviceProvider) : VerifyGamePipeline(targetType, gameLocations, settings, serviceProvider) { - protected override IEnumerable CreateVerificationSteps(IGameDatabase database) + protected override IEnumerable CreateVerificationSteps(IGameDatabase database) { var verifyProvider = ServiceProvider.GetRequiredService(); return verifyProvider.GetAllDefaultVerifiers(database, Settings); diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index 7f5c34d..d84156a 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive": { "commandName": "Project", - "commandLineArgs": "-o verifyResults --minFailSeverity Information" + "commandLineArgs": "-o verifyResults --minFailSeverity Information --baseline c:/test/focBaseline.json" }, "FromModPath": { diff --git a/src/ModVerify/VerifyGamePipeline.cs b/src/ModVerify.CliApp/VerifyGamePipeline.cs similarity index 77% rename from src/ModVerify/VerifyGamePipeline.cs rename to src/ModVerify.CliApp/VerifyGamePipeline.cs index 6d24a3f..5d3ac3e 100644 --- a/src/ModVerify/VerifyGamePipeline.cs +++ b/src/ModVerify.CliApp/VerifyGamePipeline.cs @@ -1,23 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; using AnakinRaW.CommonUtilities.SimplePipeline; using AnakinRaW.CommonUtilities.SimplePipeline.Runners; +using AnakinRaW.CommonUtilities.SimplePipeline.Steps; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace AET.ModVerify; +public sealed class GameVerifierPipelineStep(IGameVerifier verifier, IServiceProvider serviceProvider) : PipelineStep(serviceProvider) +{ + private readonly IGameVerifier _gameVerifier = verifier ?? throw new ArgumentNullException(nameof(verifier)); + + protected override void RunCore(CancellationToken token) + { + Logger?.LogDebug($"Running verifier '{_gameVerifier.FriendlyName}'..."); + try + { + _gameVerifier.Verify(token); + } + finally + { + Logger?.LogDebug($"Finished verifier '{_gameVerifier.FriendlyName}'"); + } + } +} + public abstract class VerifyGamePipeline : Pipeline { - private readonly List _verificationSteps = new(); + private readonly List _verificationSteps = new(); private readonly GameEngineType _targetType; private readonly GameLocations _gameLocations; private readonly ParallelStepRunner _verifyRunner; @@ -93,11 +112,11 @@ protected sealed override async Task RunCoreAsync(CancellationToken token) } - protected abstract IEnumerable CreateVerificationSteps(IGameDatabase database); + protected abstract IEnumerable CreateVerificationSteps(IGameDatabase database); - private void AddStep(GameVerifierBase verifier) + private void AddStep(IGameVerifier verifier) { - _verifyRunner.AddStep(verifier); + _verifyRunner.AddStep(new GameVerifierPipelineStep(verifier, ServiceProvider)); _verificationSteps.Add(verifier); } } \ No newline at end of file diff --git a/src/ModVerify/IGameVerifier.cs b/src/ModVerify/IGameVerifier.cs index 1184c86..42ae278 100644 --- a/src/ModVerify/IGameVerifier.cs +++ b/src/ModVerify/IGameVerifier.cs @@ -1,13 +1,18 @@ using System.Collections.Generic; +using System.Threading; using AET.ModVerify.Reporting; namespace AET.ModVerify; public interface IGameVerifier { + IGameVerifier? Parent { get; } + string Name { get; } string FriendlyName { get; } IReadOnlyCollection VerifyErrors { get; } + + void Verify(CancellationToken token); } \ No newline at end of file diff --git a/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs b/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs index c185518..c11e62e 100644 --- a/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs +++ b/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs @@ -5,7 +5,7 @@ namespace AET.ModVerify.Reporting; -internal class ConcurrentGameGameErrorReporter : GameErrorReporter, IDatabaseErrorCollection +public sealed class ConcurrentGameGameErrorReporter : GameErrorReporter, IDatabaseErrorCollection { private readonly ConcurrentBag _xmlErrors = new(); private readonly ConcurrentBag _initializationErrors = new(); diff --git a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs index a2453a0..7cbcbb1 100644 --- a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs +++ b/src/ModVerify/Reporting/IDatabaseErrorCollection.cs @@ -3,7 +3,7 @@ namespace AET.ModVerify.Reporting; -internal interface IDatabaseErrorCollection +public interface IDatabaseErrorCollection { IEnumerable XmlErrors { get; } IEnumerable InitializationErrors { get; } diff --git a/src/ModVerify/Reporting/Json/JsonVerificationError.cs b/src/ModVerify/Reporting/Json/JsonVerificationError.cs index 9c69d8d..5303944 100644 --- a/src/ModVerify/Reporting/Json/JsonVerificationError.cs +++ b/src/ModVerify/Reporting/Json/JsonVerificationError.cs @@ -8,8 +8,8 @@ internal class JsonVerificationError [JsonPropertyName("id")] public string Id { get; } - [JsonPropertyName("verifier")] - public string Verifier { get; } + [JsonPropertyName("verifiers")] + public IReadOnlyList VerifierChain { get; } [JsonPropertyName("message")] public string Message { get; } @@ -21,10 +21,10 @@ internal class JsonVerificationError public IEnumerable Assets { get; } [JsonConstructor] - private JsonVerificationError(string id, string verifier, string message, VerificationSeverity severity, IEnumerable assets) + private JsonVerificationError(string id, IReadOnlyList verifierChain, string message, VerificationSeverity severity, IEnumerable assets) { Id = id; - Verifier = verifier; + VerifierChain = verifierChain; Message = message; Severity = severity; Assets = assets; @@ -33,7 +33,7 @@ private JsonVerificationError(string id, string verifier, string message, Verifi public JsonVerificationError(VerificationError error) { Id = error.Id; - Verifier = error.Verifier; + VerifierChain = error.VerifierChain; Message = error.Message; Severity = error.Severity; Assets = error.AffectedAssets; diff --git a/src/ModVerify/Verifiers/Engine/InitializationErrorReporterBase.cs b/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs similarity index 85% rename from src/ModVerify/Verifiers/Engine/InitializationErrorReporterBase.cs rename to src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs index 6626bca..839256e 100644 --- a/src/ModVerify/Verifiers/Engine/InitializationErrorReporterBase.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; -using AET.ModVerify.Reporting; using AnakinRaW.CommonUtilities; using PG.StarWarsGame.Engine.IO; -namespace AET.ModVerify.Verifiers; +namespace AET.ModVerify.Reporting.Reporters.Engine; -internal abstract class InitializationErrorReporterBase(IGameRepository gameRepository, IServiceProvider serviceProvider) +internal abstract class EngineErrorReporterBase(IGameRepository gameRepository, IServiceProvider serviceProvider) { protected readonly IGameRepository GameRepository = gameRepository ?? throw new ArgumentNullException(nameof(gameRepository)); protected readonly IServiceProvider ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); @@ -18,7 +17,7 @@ public IEnumerable GetErrors(IEnumerable errors) foreach (var error in errors) { var errorData = CreateError(error); - yield return new VerificationError(errorData.Identifier, errorData.Message, Name, errorData.Assets, errorData.Severity); + yield return new VerificationError(errorData.Identifier, errorData.Message, [Name], errorData.Assets, errorData.Severity); } } diff --git a/src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs similarity index 90% rename from src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs rename to src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs index 8e55f67..7d9aac4 100644 --- a/src/ModVerify/Verifiers/Engine/GameAssertErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.Text; -using AET.ModVerify.Reporting; +using AET.ModVerify.Verifiers; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO; -namespace AET.ModVerify.Verifiers; +namespace AET.ModVerify.Reporting.Reporters.Engine; internal sealed class GameAssertErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) - : InitializationErrorReporterBase(gameRepository, serviceProvider) + : EngineErrorReporterBase(gameRepository, serviceProvider) { public override string Name => "GameAsserts"; diff --git a/src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs similarity index 76% rename from src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs rename to src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs index db175a3..e4903f6 100644 --- a/src/ModVerify/Verifiers/Engine/InitializationErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs @@ -1,12 +1,12 @@ using System; -using AET.ModVerify.Reporting; +using AET.ModVerify.Verifiers; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO; -namespace AET.ModVerify.Verifiers; +namespace AET.ModVerify.Reporting.Reporters.Engine; internal sealed class InitializationErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) - : InitializationErrorReporterBase(gameRepository, serviceProvider) + : EngineErrorReporterBase(gameRepository, serviceProvider) { public override string Name => "InitializationErrors"; diff --git a/src/ModVerify/Verifiers/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs similarity index 96% rename from src/ModVerify/Verifiers/Engine/XmlParseErrorReporter.cs rename to src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs index 0557c65..c9ce55b 100644 --- a/src/ModVerify/Verifiers/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; -using AET.ModVerify.Reporting; 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; -namespace AET.ModVerify.Verifiers; +namespace AET.ModVerify.Reporting.Reporters.Engine; internal sealed class XmlParseErrorReporter(IGameRepository gameRepository, IServiceProvider serviceProvider) : - InitializationErrorReporterBase(gameRepository, serviceProvider) + EngineErrorReporterBase(gameRepository, serviceProvider) { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); diff --git a/src/ModVerify/Reporting/Reporters/Text/TextFileReporter.cs b/src/ModVerify/Reporting/Reporters/Text/TextFileReporter.cs index 9ff34dd..cfc5c91 100644 --- a/src/ModVerify/Reporting/Reporters/Text/TextFileReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Text/TextFileReporter.cs @@ -32,7 +32,7 @@ private async Task ReportWhole(IReadOnlyCollection errors) private async Task ReportByVerifier(IReadOnlyCollection errors) { - var grouped = errors.GroupBy(x => x.Verifier); + var grouped = errors.GroupBy(x => x.VerifierChain.Last()); foreach (var group in grouped) await ReportToSingleFile(group); } diff --git a/src/ModVerify/Reporting/SuppressionFilter.cs b/src/ModVerify/Reporting/SuppressionFilter.cs index 7fb41de..d2f3a94 100644 --- a/src/ModVerify/Reporting/SuppressionFilter.cs +++ b/src/ModVerify/Reporting/SuppressionFilter.cs @@ -22,7 +22,7 @@ public SuppressionFilter(string? id, string? verifier, ICollection? asse Id = id; Verifier = verifier; if (assets is not null) - _assets = new HashSet(assets); + _assets = [..assets]; Assets = _assets?.ToList() ?? null; } @@ -32,7 +32,7 @@ internal SuppressionFilter(JsonSuppressionFilter filter) Verifier = filter.Verifier; if (filter.Assets is not null) - _assets = new HashSet(filter.Assets); + _assets = [..filter.Assets]; Assets = _assets?.ToList() ?? null; } @@ -50,7 +50,7 @@ public bool Suppresses(VerificationError error) if (Verifier is not null) { - if (Verifier.Equals(error.Verifier)) + if (error.VerifierChain.Contains(Verifier)) suppresses = true; else return false; diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs index 3051394..0eea20a 100644 --- a/src/ModVerify/Reporting/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/VerificationBaseline.cs @@ -20,12 +20,12 @@ public sealed class VerificationBaseline : IReadOnlyCollection new VerificationError(x))); + _errors = [..baseline.Errors.Select(x => new VerificationError(x))]; } public VerificationBaseline(IEnumerable errors) { - _errors = new(errors); + _errors = [..errors]; } public void ToJson(Stream stream) diff --git a/src/ModVerify/Reporting/VerificationError.cs b/src/ModVerify/Reporting/VerificationError.cs index 89b7189..5bdb62c 100644 --- a/src/ModVerify/Reporting/VerificationError.cs +++ b/src/ModVerify/Reporting/VerificationError.cs @@ -16,22 +16,23 @@ public sealed class VerificationError : IEquatable public string Message { get; } - public string Verifier { get; } + public IReadOnlyList VerifierChain { get; } public IReadOnlyCollection AffectedAssets { get; } public VerificationSeverity Severity { get; } - public VerificationError(string id, string message, string verifier, IEnumerable affectedAssets, VerificationSeverity severity) + public VerificationError(string id, string message, IReadOnlyList verifiers, IEnumerable affectedAssets, + VerificationSeverity severity) { - if (affectedAssets == null) + if (affectedAssets == null) throw new ArgumentNullException(nameof(affectedAssets)); ThrowHelper.ThrowIfNullOrEmpty(id); Id = id; Message = message ?? throw new ArgumentNullException(nameof(message)); - Verifier = verifier; + VerifierChain = verifiers; Severity = severity; - _assets = new(affectedAssets); + _assets = [..affectedAssets]; AffectedAssets = _assets.ToList(); } @@ -39,61 +40,31 @@ internal VerificationError(JsonVerificationError error) { Id = error.Id; Message = error.Message; - Verifier = error.Verifier; - _assets = new(error.Assets); + VerifierChain = error.VerifierChain; + _assets = [..error.Assets]; AffectedAssets = _assets.ToList(); } public static VerificationError Create( - IGameVerifier verifier, + IReadOnlyList verifiers, string id, string message, - VerificationSeverity severity, + VerificationSeverity severity, IEnumerable assets) { - return new VerificationError(id, message, verifier.Name, assets, severity); - } - - - public static VerificationError Create( - IGameVerifier verifier, - string id, - string message, - VerificationSeverity severity, - params string[] assets) - { - return new VerificationError(id, message, verifier.Name, assets, severity); + return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), assets, severity); } public static VerificationError Create( - IGameVerifier verifier, - string id, + IReadOnlyList verifiers, + string id, string message, - VerificationSeverity severity) - { - return Create(verifier, id, message, severity, []); - } - - internal static VerificationError Create( - IGameVerifier verifier, - string id, - Exception exception, VerificationSeverity severity, params string[] assets) { - return new VerificationError(id, exception.Message, verifier.Name, assets, severity); - } - - internal static VerificationError Create( - IGameVerifier verifier, - string id, - Exception exception, - VerificationSeverity severity) - { - return Create(verifier, id, exception, severity, []); + return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), assets, severity); } - public bool Equals(VerificationError? other) { if (other is null) @@ -103,8 +74,6 @@ public bool Equals(VerificationError? other) if (!Id.Equals(other.Id)) return false; - if (!Verifier.Equals(other.Verifier)) - return false; return AssetComparer.Equals(_assets, other._assets); } @@ -118,13 +87,12 @@ public override int GetHashCode() { var hashCode = new HashCode(); hashCode.Add(Id); - hashCode.Add(Verifier); hashCode.Add(_assets, AssetComparer); return hashCode.ToHashCode(); } public override string ToString() { - return $"[{Severity}] [{Verifier}] {Id}: Message={Message}; Affected Assets=[{string.Join(",", AffectedAssets)}];"; + return $"[{Severity}] [{string.Join(" --> ", VerifierChain)}] {Id}: Message={Message}; Affected Assets=[{string.Join(",", AffectedAssets)}];"; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/VerificationReportBroker.cs b/src/ModVerify/Reporting/VerificationReportBroker.cs index cdb1aec..26712a1 100644 --- a/src/ModVerify/Reporting/VerificationReportBroker.cs +++ b/src/ModVerify/Reporting/VerificationReportBroker.cs @@ -3,17 +3,16 @@ using System.Linq; using System.Threading.Tasks; using AET.ModVerify.Reporting.Settings; -using AET.ModVerify.Verifiers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace AET.ModVerify.Reporting; -internal class VerificationReportBroker(GlobalVerificationReportSettings reportSettings, IServiceProvider serviceProvider) +public sealed class VerificationReportBroker(GlobalVerificationReportSettings reportSettings, IServiceProvider serviceProvider) { private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(VerificationReportBroker)); - public async Task> ReportAsync(IEnumerable steps) + public async Task> ReportAsync(IEnumerable steps) { var suppressions = new SuppressionList(reportSettings.Suppressions); var errors = GetReportableErrors(steps, suppressions); @@ -34,7 +33,7 @@ public async Task> ReportAsync(IEnumerabl return errors; } - private IReadOnlyCollection GetReportableErrors(IEnumerable steps, SuppressionList suppressions) + private IReadOnlyCollection GetReportableErrors(IEnumerable steps, SuppressionList suppressions) { var allErrors = steps.SelectMany(s => s.VerifyErrors); diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify/VerificationProvider.cs index b076d29..9d14187 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify/VerificationProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; +using AET.ModVerify.Verifiers.Commons; using PG.StarWarsGame.Engine.Database; namespace AET.ModVerify; diff --git a/src/ModVerify/Verifiers/AudioFilesVerifier.cs b/src/ModVerify/Verifiers/AudioFilesVerifier.cs index e5ba70b..62a3297 100644 --- a/src/ModVerify/Verifiers/AudioFilesVerifier.cs +++ b/src/ModVerify/Verifiers/AudioFilesVerifier.cs @@ -38,7 +38,7 @@ public class AudioFilesVerifier : GameVerifierBase private readonly IGameLanguageManager _languageManager; public AudioFilesVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : base(gameDatabase, settings, serviceProvider) + : base(null, gameDatabase, settings, serviceProvider) { _hashingService = serviceProvider.GetRequiredService(); _fileSystem = serviceProvider.GetRequiredService(); @@ -48,7 +48,7 @@ public AudioFilesVerifier(IGameDatabase gameDatabase, GameVerifySettings setting public override string FriendlyName => "Verify Audio Files"; - protected override void RunVerification(CancellationToken token) + public override void Verify(CancellationToken token) { var visitedSamples = new HashSet(); var languagesToVerify = GetLanguagesToVerify().ToList(); @@ -127,7 +127,7 @@ private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent) { var sampleString = sample.ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.SampleNotFound, $"Audio file '{sampleString}' could not be found.", VerificationSeverity.Error, @@ -153,7 +153,7 @@ private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent) { var sampleString = sample.ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.SampleNotPCM, $"Audio file '{sampleString}' has an invalid format '{format}'. Supported is {WaveFormats.PCM}", VerificationSeverity.Error, @@ -164,7 +164,7 @@ private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent) { var sampleString = sample.ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.SampleNotMono, $"Audio file '{sampleString}' is not mono audio.", VerificationSeverity.Information, @@ -175,7 +175,7 @@ private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent) { var sampleString = sample.ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes. InvalidSampleRate, $"Audio file '{sampleString}' has a too high sample rate of {sampleRate}. Maximum is 48.000Hz.", VerificationSeverity.Error, @@ -186,7 +186,7 @@ private void VerifySample(ReadOnlySpan sample, SfxEvent sfxEvent) { var sampleString = sample.ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.InvalidBitsPerSeconds, $"Audio file '{sampleString}' has an invalid bit size of {bitPerSecondPerChannel}. Supported are 16bit.", VerificationSeverity.Error, diff --git a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs index 74be606..b15e7a4 100644 --- a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs +++ b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using AET.ModVerify.Reporting; @@ -12,30 +11,65 @@ namespace AET.ModVerify.Verifiers; public partial class CommandBarVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(gameDatabase, settings, serviceProvider) + : GameVerifierBase(null, gameDatabase, settings, serviceProvider) { public const string CommandBarNoShellsGroup = "CMDBAR00"; public const string CommandBarManyShellsGroup = "CMDBAR01"; public const string CommandBarNoShellsComponentInShellGroup = "CMDBAR02"; public const string CommandBarDuplicateComponent = "CMDBAR03"; public const string CommandBarUnsupportedComponent = "CMDBAR04"; + public const string CommandBarShellNoModel = "CMDBAR05"; public override string FriendlyName => "CommandBar Verifiers"; - protected override void RunVerification(CancellationToken token) + public override void Verify(CancellationToken token) { VerifyCommandBarShellsGroups(); VerifyCommandBarComponents(); - VerifyCommandBarModels(); + } + + private void VerifySingleComponent(CommandBarBaseComponent component) + { + VerifyCommandBarModel(component); + VerifyComponentBone(component); } } partial class CommandBarVerifier { - private void VerifyCommandBarModels() + private void VerifyCommandBarModel(CommandBarBaseComponent component) { - foreach (var component in Database.CommandBar.Components) + if (component is not CommandBarShellComponent shellComponent) + return; + + if (shellComponent.ModelPath is null) + { + AddError(VerificationError.Create(VerifierChain, + CommandBarShellNoModel, $"The CommandBarShellComponent '{component.Name}' has no model specified.", + VerificationSeverity.Error, shellComponent.Name)); + return; + } + + var model = Database.PGRender.LoadModelAndAnimations(shellComponent.ModelPath.AsSpan(), null); + if (model is null) { + AddError(VerificationError.Create(VerifierChain, + CommandBarShellNoModel, $"Could not find model '{shellComponent.ModelPath}' for CommandBarShellComponent '{component.Name}'.", + VerificationSeverity.Error, shellComponent.Name, shellComponent.ModelPath)); + return; + } + } + + private void VerifyComponentBone(CommandBarBaseComponent component) + { + if (component is CommandBarShellComponent) + return; + + if (component.Bone == -1) + { + AddError(VerificationError.Create(VerifierChain, + CommandBarShellNoModel, $"The CommandBar component '{component.Name}' is not connected to a shell component.", + VerificationSeverity.Warning, component.Name)); } } } @@ -44,45 +78,35 @@ partial class CommandBarVerifier { private void VerifyCommandBarComponents() { - var occupiedComponentIds = Enum.GetValues(typeof(CommandBarComponentId)) - .Cast() + var occupiedComponentIds = SupportedCommandBarComponentData.GetComponentIdsForEngine(Repository.EngineType).Keys .ToDictionary(value => value, _ => false); foreach (var component in Database.CommandBar.Components) { - if (occupiedComponentIds[component.Id]) + if (!occupiedComponentIds.TryGetValue(component.Id, out var alreadyOccupied)) { - AddError(VerificationError.Create(this, - CommandBarDuplicateComponent, - $"The CommandBar component '{component.Name}' with ID '{component.Id}' already exists.", - VerificationSeverity.Warning, + AddError(VerificationError.Create( + VerifierChain, + CommandBarUnsupportedComponent, + $"The CommandBar component '{component.Name}' is not supported by the game.", + VerificationSeverity.Information, component.Name)); } + else + { + occupiedComponentIds[component.Id] = true; + } - - // TODO: Foc supports more types. The verifier should be aware of that. - if (component.Id is CommandBarComponentId.None or CommandBarComponentId.Count) + if (alreadyOccupied) { - AddError(VerificationError.Create(this, - CommandBarUnsupportedComponent, - $"The CommandBar component '{component.Name}' is not supported by the game.", - VerificationSeverity.Information, + AddError(VerificationError.Create(VerifierChain, + CommandBarDuplicateComponent, + $"The CommandBar component '{component.Name}' with ID '{component.Id}' already exists.", + VerificationSeverity.Warning, component.Name)); } - } - - // TODO: Foc supports more types. The verifier should be aware of that. - var missingComponents = occupiedComponentIds - .Where(x => x is { Value: false, Key: not CommandBarComponentId.None and not CommandBarComponentId.Count }) - .Select(x => x.Key); - foreach (var componentId in missingComponents) - { - AddError(VerificationError.Create(this, - CommandBarUnsupportedComponent, - $"The CommandBar is missing the required component id, which is named '{componentId}' in XML.", - VerificationSeverity.Error, - componentId.ToString())); + VerifySingleComponent(component); } } } @@ -106,14 +130,14 @@ private void VerifyCommandBarShellsGroups() } if (shellGroups.Count == 0) - AddError(VerificationError.Create(this, + AddError(VerificationError.Create(VerifierChain, CommandBarNoShellsGroup, $"No CommandBarGroup '{CommandBarConstants.ShellGroupName}' found.", VerificationSeverity.Error, "GameCommandBar")); if (shellGroups.Count >= 1) - AddError(VerificationError.Create(this, + AddError(VerificationError.Create(VerifierChain, CommandBarManyShellsGroup, $"Found more than one Shells CommandBarGroup. Mind that group names are case-sensitive. Correct name is '{CommandBarConstants.ShellGroupName}'", VerificationSeverity.Warning, @@ -122,13 +146,16 @@ private void VerifyCommandBarShellsGroups() private void VerifyShellGroup(CommandBarComponentGroup shellGroup) { - foreach (var shellComponent in shellGroup.Components) + foreach (var component in shellGroup.Components) { - if (shellComponent is not CommandBarShellComponent || shellComponent.Type is not CommandBarComponentType.Shell) - AddError(VerificationError.Create(this, + var shellComponent = component as CommandBarShellComponent; + if (shellComponent?.Type is not CommandBarComponentType.Shell) + { + AddError(VerificationError.Create(VerifierChain, CommandBarNoShellsComponentInShellGroup, - $"The CommandBar component '{shellComponent.Name}' is not a shell component, but part of the '{CommandBarConstants.ShellGroupName}' group.", - VerificationSeverity.Warning)); + $"The CommandBar component '{component.Name}' is not a shell component, but part of the '{CommandBarConstants.ShellGroupName}' group.", + VerificationSeverity.Warning, shellGroup.Name, component.Name)); + } } } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/Commons/ReferencedModelsVerifier.cs similarity index 59% rename from src/ModVerify/Verifiers/ReferencedModelsVerifier.cs rename to src/ModVerify/Verifiers/Commons/ReferencedModelsVerifier.cs index bfb9f7c..c29dd91 100644 --- a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs +++ b/src/ModVerify/Verifiers/Commons/ReferencedModelsVerifier.cs @@ -1,93 +1,116 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AET.ModVerify.Utilities; -using Microsoft.Extensions.DependencyInjection; -using PG.Commons.Utilities; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Files.ALO.Files.Models; using PG.StarWarsGame.Files.ALO.Files.Particles; -using PG.StarWarsGame.Files.ALO.Services; using PG.StarWarsGame.Files.ChunkFiles.Data; using AnakinRaW.CommonUtilities.FileSystem; using PG.StarWarsGame.Files; +using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Files; using PG.StarWarsGame.Files.Binary; -namespace AET.ModVerify.Verifiers; +namespace AET.ModVerify.Verifiers.Commons; -public sealed class ReferencedModelsVerifier( +internal sealed class AlreadyVerifiedCache +{ + internal static readonly AlreadyVerifiedCache Instance = new(); + + private readonly ConcurrentDictionary _cachedModels = new(StringComparer.OrdinalIgnoreCase); + + private AlreadyVerifiedCache() + { + } + + public bool TryAddModel(string fileName) + { + return _cachedModels.TryAdd(fileName, 0); + } +} + +public sealed class SharedReferencedModelsVerifier( + IGameVerifier? parent, + IEnumerable modelSource, IGameDatabase database, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(database, settings, serviceProvider) + : GameVerifierBase(parent, database, settings, serviceProvider) { private const string ProxyAltIdentifier = "_ALT"; - private readonly IAloFileService _modelFileService = serviceProvider.GetRequiredService(); + private readonly AlreadyVerifiedCache _cache = AlreadyVerifiedCache.Instance; - public override string FriendlyName => "Referenced Models"; + public override string FriendlyName => "Models"; - protected override void RunVerification(CancellationToken token) + public override void Verify(CancellationToken token) { - var aloQueue = new Queue(Database.GameObjectTypeManager.Entries - .SelectMany(x => x.Models) - .Concat(FocHardcodedConstants.HardcodedModels)); - - var visitedAloFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + var aloQueue = new Queue(modelSource); while (aloQueue.Count != 0) { - var model = aloQueue.Dequeue(); - if (!visitedAloFiles.Add(model)) + var fileName = aloQueue.Dequeue(); + if (!_cache.TryAddModel(fileName)) continue; token.ThrowIfCancellationRequested(); - using var modelStream = Repository.TryOpenFile(BuildModelPath(model)); + var modelPath = BuildModelPath(fileName).AsSpan(); - if (modelStream is null) + IAloFile? aloFile = null; + try { - var error = VerificationError.Create( - this, - VerifierErrorCodes.ModelNotFound, - $"Unable to find .ALO file '{model}'", - VerificationSeverity.Error, - model); - - AddError(error); + try + { + aloFile = Database.PGRender.Load3DAsset(modelPath, true, true); + } + catch (BinaryCorruptedException e) + { + var aloFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), modelPath).ToString(); + var message = $"{aloFile} is corrupted: {e.Message}"; + AddError(VerificationError.Create(VerifierChain, VerifierErrorCodes.ModelBroken, message, VerificationSeverity.Critical, aloFilePath)); + continue; + } + + if (aloFile is null) + { + var error = VerificationError.Create( + VerifierChain, + VerifierErrorCodes.ModelNotFound, + $"Unable to find .ALO file '{fileName}'", + VerificationSeverity.Error, + fileName); + AddError(error); + continue; + } + + VerifyModelOrParticle(aloFile, aloQueue); + } + finally + { + aloFile?.Dispose(); } - else - VerifyModelOrParticle(modelStream, aloQueue); } } - private void VerifyModelOrParticle(Stream modelStream, Queue workingQueue) + private void VerifyModelOrParticle(IAloFile aloFile, Queue workingQueue) { - try - { - using var aloData = _modelFileService.Load(modelStream, AloLoadOptions.Assets); - switch (aloData) - { - case IAloModelFile model: - VerifyModel(model, workingQueue); - break; - case IAloParticleFile particle: - VerifyParticle(particle); - break; - default: - throw new InvalidOperationException("The data stream is neither a model nor particle."); - } - } - catch (BinaryCorruptedException e) + switch (aloFile) { - var aloFile = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), modelStream.GetFilePath().AsSpan()).ToString(); - var message = $"{aloFile} is corrupted: {e.Message}"; - AddError(VerificationError.Create(this, VerifierErrorCodes.ModelBroken, message, VerificationSeverity.Critical, aloFile)); + case IAloModelFile model: + VerifyModel(model, workingQueue); + break; + case IAloParticleFile particle: + VerifyParticle(particle); + break; + default: + throw new InvalidOperationException("The data stream is neither a model nor particle."); } } @@ -101,7 +124,7 @@ private void VerifyParticle(IAloParticleFile file) { var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.InvalidTexture, $"Invalid texture file name" + $" '{texture}' in particle 'modelFilePath'", @@ -118,9 +141,9 @@ private void VerifyParticle(IAloParticleFile file) { var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.InvalidParticleName, - $"The particle name '{file.Content.Name}' does not match file name '{modelFilePath}'", + $"The particle name '{file.Content.Name}' does not match file name '{modelFilePath}'", VerificationSeverity.Error, modelFilePath)); } @@ -138,10 +161,10 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( - this, - VerifierErrorCodes.InvalidTexture, + VerifierChain, + VerifierErrorCodes.InvalidTexture, $"Invalid texture file name '{texture}' in model '{modelFilePath}'", - VerificationSeverity.Error, + VerificationSeverity.Error, texture, modelFilePath)); }); } @@ -155,7 +178,7 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) var shaderPath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.InvalidShader, $"Invalid texture file name '{shader}' in model '{shaderPath}'", VerificationSeverity.Error, @@ -173,7 +196,7 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) var proxyPath = FileSystem.Path .GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.InvalidProxy, $"Invalid proxy file name '{proxy}' in model '{proxyPath}'", VerificationSeverity.Error, @@ -186,13 +209,13 @@ private void VerifyTextureExists(IPetroglyphFileHolder workingQueue) { var proxyName = ProxyNameWithoutAlt(proxy); - var particle = FileSystem.Path.ChangeExtension(proxyName, "alo"); - if (!Repository.FileExists(BuildModelPath(particle))) + + if (!Repository.ModelRepository.FileExists(BuildModelPath(proxyName))) { var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), model.FilePath.AsSpan()).ToString(); - var message = $"{modelFilePath} references missing proxy particle: {particle}"; - var error = VerificationError.Create(this, VerifierErrorCodes.ModelMissingProxy, message, VerificationSeverity.Error, modelFilePath, particle); + var message = $"{modelFilePath} references missing proxy particle: {proxyName}"; + var error = VerificationError.Create(VerifierChain, + VerifierErrorCodes.ModelMissingProxy, message, VerificationSeverity.Error, modelFilePath, proxyName); AddError(error); } else - workingQueue.Enqueue(particle); + workingQueue.Enqueue(proxyName); } private string BuildModelPath(string fileName) @@ -226,7 +250,7 @@ private void VerifyShaderExists(IPetroglyphFileHolder data, string shader) { var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), data.FilePath.AsSpan()).ToString(); var message = $"{modelFilePath} references missing shader effect: {shader}"; - var error = VerificationError.Create(this, VerifierErrorCodes.ModelMissingShader, message, VerificationSeverity.Error, modelFilePath, shader); + var error = VerificationError.Create(VerifierChain, VerifierErrorCodes.ModelMissingShader, message, VerificationSeverity.Error, modelFilePath, shader); AddError(error); } } @@ -251,3 +275,37 @@ private static string ProxyNameWithoutAlt(string proxy) return proxyName.ToString(); } } + + + +public sealed class ReferencedModelsVerifier( + IGameDatabase database, + GameVerifySettings settings, + IServiceProvider serviceProvider) + : GameVerifierBase(null, database, settings, serviceProvider) +{ + public override string FriendlyName => "Referenced Models"; + + public override void Verify(CancellationToken token) + { + var models = Database.GameObjectTypeManager.Entries + .SelectMany(x => x.Models) + .Concat(FocHardcodedConstants.HardcodedModels); + + var inner = new SharedReferencedModelsVerifier(this, models, Database, Settings, Services); + try + { + inner.Error += OnModelError; + inner.Verify(token); + } + finally + { + inner.Error -= OnModelError; + } + } + + private void OnModelError(object sender, VerificationErrorEventArgs e) + { + AddError(e.Error); + } +} diff --git a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs b/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs similarity index 84% rename from src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs rename to src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs index 58e600c..c9fe207 100644 --- a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.cs +++ b/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs @@ -10,13 +10,13 @@ public sealed partial class ReferencedTexturesVerifier( GameVerifySettings settings, IServiceProvider serviceProvider) : - GameVerifierBase(gameDatabase, settings, serviceProvider) + GameVerifierBase(null, gameDatabase, settings, serviceProvider) { public const string MtdNotFound = "TEX00"; public const string TexutreNotFound = "TEX01"; public const string FileNameTooLong = "PAT00"; - protected override void RunVerification(CancellationToken token) + public override void Verify(CancellationToken token) { var textures = new HashSet(StringComparer.OrdinalIgnoreCase); try diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index ab77c7d..6db240a 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -17,11 +17,11 @@ public sealed class DuplicateNameFinder( IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(gameDatabase, settings, serviceProvider) + : GameVerifierBase(null, gameDatabase, settings, serviceProvider) { public override string FriendlyName => "Duplicates"; - protected override void RunVerification(CancellationToken token) + public override void Verify(CancellationToken token) { CheckXmlObjectsForDuplicates("GameObject", Database.GameObjectTypeManager); CheckXmlObjectsForDuplicates("SFXEvent", Database.SfxGameManager); @@ -51,7 +51,7 @@ private void CheckForDuplicateCrcEntries( { var entryNames = entryToStringSelector(entries); AddError(VerificationError.Create( - this, + VerifierChain, VerifierErrorCodes.DuplicateFound, errorMessageCreator(entries, sourceName), VerificationSeverity.Error, diff --git a/src/ModVerify/Verifiers/Engine/GameEngineErrorCollector.cs b/src/ModVerify/Verifiers/GameEngineErrorCollector.cs similarity index 70% rename from src/ModVerify/Verifiers/Engine/GameEngineErrorCollector.cs rename to src/ModVerify/Verifiers/GameEngineErrorCollector.cs index ef1a967..1e3fbd2 100644 --- a/src/ModVerify/Verifiers/Engine/GameEngineErrorCollector.cs +++ b/src/ModVerify/Verifiers/GameEngineErrorCollector.cs @@ -2,30 +2,31 @@ using System.Collections.Generic; using System.Threading; using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Reporters.Engine; using AET.ModVerify.Settings; using PG.StarWarsGame.Engine.Database; namespace AET.ModVerify.Verifiers; -internal sealed class GameEngineErrorCollector( +public sealed class GameEngineErrorCollector( IDatabaseErrorCollection errorCollection, IGameDatabase gameDatabase, GameVerifySettings settings, - IServiceProvider serviceProvider) : GameVerifierBase(gameDatabase, settings, serviceProvider) + IServiceProvider serviceProvider) : GameVerifierBase(null, gameDatabase, settings, serviceProvider) { public override string FriendlyName => "Reporting Game Engine Errors"; - protected override void RunVerification(CancellationToken token) - { + public override void Verify(CancellationToken token) + { AddErrors(new InitializationErrorReporter(Repository, Services).GetErrors(errorCollection.InitializationErrors)); AddErrors(new XmlParseErrorReporter(Repository, Services).GetErrors(errorCollection.XmlErrors)); - if (Settings.GlobalReportSettings.ReportAsserts) + if (Settings.GlobalReportSettings.ReportAsserts) AddErrors(new GameAssertErrorReporter(Repository, Services).GetErrors(errorCollection.Asserts)); } private void AddErrors(IEnumerable errors) { - foreach (var error in errors) + foreach (var error in errors) AddError(error); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 21a9371..8d12f55 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -1,60 +1,66 @@ -using System; -using System.Collections.Generic; -using System.IO.Abstractions; -using System.Threading; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Settings; -using AnakinRaW.CommonUtilities.SimplePipeline.Steps; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.IO; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Threading; namespace AET.ModVerify.Verifiers; -public abstract class GameVerifierBase( - IGameDatabase gameDatabase, - GameVerifySettings settings, - IServiceProvider serviceProvider) - : PipelineStep(serviceProvider), IGameVerifier +public abstract class GameVerifierBase : IGameVerifier { + public event EventHandler? Error; - protected readonly IFileSystem FileSystem = serviceProvider.GetRequiredService(); + private readonly ConcurrentDictionary _verifyErrors = new(); - private readonly HashSet _verifyErrors = new(); - - public IReadOnlyCollection VerifyErrors => _verifyErrors; + protected readonly IFileSystem FileSystem; + protected readonly IServiceProvider Services; + private readonly IGameDatabase _gameDatabase; - protected GameVerifySettings Settings { get; } = settings; - - protected IGameDatabase Database { get; } = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); + protected GameVerifierBase(IGameVerifier? parent, + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) + { + _gameDatabase = gameDatabase; + FileSystem = serviceProvider.GetRequiredService(); + Services = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + Parent = parent; + Settings = settings; + Database = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); + VerifierChain = CreateVerifierChain(); + } - protected IGameRepository Repository => gameDatabase.GameRepository; + public IReadOnlyCollection VerifyErrors => [.._verifyErrors.Keys]; public virtual string FriendlyName => GetType().Name; public string Name => GetType().FullName; - protected sealed override void RunCore(CancellationToken token) - { - Logger?.LogInformation($"Running verifier '{FriendlyName}'..."); - try - { - RunVerification(token); - } - finally - { - Logger?.LogInformation($"Finished verifier '{FriendlyName}'"); - } - } + public IGameVerifier? Parent { get; } + + protected GameVerifySettings Settings { get; } + + protected IGameDatabase Database { get; } + + protected IGameRepository Repository => _gameDatabase.GameRepository; - protected abstract void RunVerification(CancellationToken token); + protected IReadOnlyList VerifierChain { get; } + + public abstract void Verify(CancellationToken token); protected void AddError(VerificationError error) { - _verifyErrors.Add(error); - if (Settings.AbortSettings.FailFast && error.Severity >= Settings.AbortSettings.MinimumAbortSeverity) - throw new GameVerificationException(error); + if (_verifyErrors.TryAdd(error, 0)) + { + Error?.Invoke(this, new VerificationErrorEventArgs(error)); + if (Settings.AbortSettings.FailFast && error.Severity >= Settings.AbortSettings.MinimumAbortSeverity) + throw new GameVerificationException(error); + } } protected void GuardedVerify(Action action, Predicate exceptionFilter, Action exceptionHandler) @@ -68,4 +74,18 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter exceptionHandler(e); } } + + private IReadOnlyList CreateVerifierChain() + { + var verifierChain = new List { this }; + + var parent = Parent; + while (parent != null) + { + verifierChain.Insert(0, parent); + parent = parent.Parent; + } + + return verifierChain; + } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs b/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs index 0594566..6704003 100644 --- a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs +++ b/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs @@ -36,7 +36,7 @@ private void VerifyMegaTexturesExist() if (Database.GuiDialogManager.MtdFile is null) { var mtdFileName = megaTextureName ?? "<>"; - VerificationError.Create(this, MtdNotFound, $"MtdFile '{mtdFileName}.mtd' could not be found", + VerificationError.Create(VerifierChain, MtdNotFound, $"MtdFile '{mtdFileName}.mtd' could not be found", VerificationSeverity.Critical, mtdFileName); } @@ -47,7 +47,7 @@ private void VerifyMegaTexturesExist() if (!Repository.TextureRepository.FileExists(megaTextureFileName)) { - VerificationError.Create(this, TexutreNotFound, $"Could not find texture '{megaTextureFileName}' could not be found", + VerificationError.Create(VerifierChain, TexutreNotFound, $"Could not find texture '{megaTextureFileName}' could not be found", VerificationSeverity.Error, megaTextureFileName); } } @@ -60,7 +60,7 @@ private void VerifyMegaTexturesExist() if (!Repository.TextureRepository.FileExists(compressedMegaTextureFieName)) { - VerificationError.Create(this, TexutreNotFound, $"Could not find texture '{compressedMegaTextureFieName}' could not be found", + VerificationError.Create(VerifierChain, TexutreNotFound, $"Could not find texture '{compressedMegaTextureFieName}' could not be found", VerificationSeverity.Error, compressedMegaTextureFieName); } } @@ -102,7 +102,7 @@ componentType is not GuiComponentType.Scanlines && if (origin == GuiTextureOrigin.MegaTexture && texture.Texture.Length > MtdFileConstants.MaxFileNameSize) { - AddError(VerificationError.Create(this, FileNameTooLong, + AddError(VerificationError.Create(VerifierChain, FileNameTooLong, $"The filename is too long. Max length is {MtdFileConstants.MaxFileNameSize} characters.", VerificationSeverity.Error, texture.Texture)); } @@ -113,7 +113,7 @@ componentType is not GuiComponentType.Scanlines && if (texture.Texture.Length > PGConstants.MaxMegEntryPathLength) message += " The file name is too long."; - AddError(VerificationError.Create(this, TexutreNotFound, + AddError(VerificationError.Create(VerifierChain, TexutreNotFound, message, VerificationSeverity.Error, texture.Texture, component, origin.ToString())); } diff --git a/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs b/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs new file mode 100644 index 0000000..dcb8566 --- /dev/null +++ b/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs @@ -0,0 +1,9 @@ +using System; +using AET.ModVerify.Reporting; + +namespace AET.ModVerify.Verifiers; + +public sealed class VerificationErrorEventArgs(VerificationError error) : EventArgs +{ + public VerificationError Error { get; } = error ?? throw new ArgumentNullException(nameof(error)); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs index ba42797..2ace3a0 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarComponentId.cs @@ -1,972 +1,52 @@ -namespace PG.StarWarsGame.Engine.CommandBar; +using System; -public enum CommandBarComponentId +namespace PG.StarWarsGame.Engine.CommandBar; + +public readonly struct CommandBarComponentId : IEquatable { - MainShell = 0x0, - ProductionOptions = 0x1, - GalacticCamera = 0x2, - PlanetName = 0x3, - PlanetValue = 0x4, - PlanetAffiliation = 0x5, - PlayerCredits = 0x6, - ReinforcementCap = 0x7, - PlanetNameTactical = 0x8, - PlanetInfo = 0x9, - Production0 = 0xa, - Production1 = 0xb, - Production2 = 0xc, - Production3 = 0xd, - Production4 = 0xe, - Production5 = 0xf, - Production6 = 0x10, - Production7 = 0x11, - Production8 = 0x12, - Production9 = 0x13, - Production10 = 0x14, - Production11 = 0x15, - Production12 = 0x16, - Production13 = 0x17, - Production14 = 0x18, - Production15 = 0x19, - Production16 = 0x1a, - Production17 = 0x1b, - Production18 = 0x1c, - Production19 = 0x1d, - Production20 = 0x1e, - Production21 = 0x1f, - Production22 = 0x20, - Production23 = 0x21, - Production24 = 0x22, - Production25 = 0x23, - Production26 = 0x24, - Production27 = 0x25, - Production28 = 0x26, - Production29 = 0x27, - Production30 = 0x28, - Production31 = 0x29, - Production32 = 0x2a, - Production33 = 0x2b, - Production34 = 0x2c, - Production35 = 0x2d, - Production36 = 0x2e, - Production37 = 0x2f, - Production38 = 0x30, - Production39 = 0x31, - Production40 = 0x32, - Production41 = 0x33, - Production42 = 0x34, - Production43 = 0x35, - Production44 = 0x36, - Production45 = 0x37, - Production46 = 0x38, - Production47 = 0x39, - DroidHelp = 0x3a, - DroidHelpTactical = 0x3b, - CurrentDay = 0x3c, - DayCredits = 0x3d, - PopulationCap = 0x3e, - Filter0 = 0x3f, - Filter1 = 0x40, - Filter2 = 0x41, - Filter3 = 0x42, - StoryArcButton = 0x43, - PlanetSummaryButton = 0x44, - SpaceTab = 0x45, - LandTab = 0x46, - Dial = 0x47, - ScrollRight = 0x48, - ScrollLeft = 0x49, - ZoomView = 0x4a, - PrevPlanet = 0x4b, - NextPlanet = 0x4c, - RadarGalactic = 0x4d, - TechLevel = 0x4e, - BalancePip = 0x4f, - BuildQueue00 = 0x50, - BuildQueue01 = 0x51, - BuildQueue02 = 0x52, - BuildQueue03 = 0x53, - BuildQueue04 = 0x54, - BuildQueue05 = 0x55, - BuildQueue06 = 0x56, - BuildQueue07 = 0x57, - BuildQueue08 = 0x58, - BuildQueue09 = 0x59, - OrganizationShell = 0x5a, - OrganizationCollision = 0x5b, - SmugglerBox = 0x5c, - SpaceStationUpgrade01 = 0x5d, - SpaceStationUpgrade02 = 0x5e, - HeroAbilitySlot = 0x5f, - PlanetOrganize0 = 0x60, - PlanetOrganize1 = 0x61, - PlanetOrganize2 = 0x62, - PlanetOrganize3 = 0x63, - PlanetOrganize4 = 0x64, - PlanetOrganize5 = 0x65, - PlanetOrganize6 = 0x66, - PlanetOrganize7 = 0x67, - PlanetOrganize8 = 0x68, - PlanetOrganize9 = 0x69, - SpecialStructureLand0 = 0x6a, - SpecialStructureLand1 = 0x6b, - SpecialStructureLand2 = 0x6c, - SpecialStructureLand3 = 0x6d, - SpecialStructureLand4 = 0x6e, - SpecialStructureLand5 = 0x6f, - SpecialStructureLand6 = 0x70, - SpecialStructureLand7 = 0x71, - SpecialStructureLand8 = 0x72, - SpecialStructureLandSell = 0x73, - BigFleet0 = 0x74, - BigFleet1 = 0x75, - BigFleet2 = 0x76, - BigFleet3 = 0x77, - Fleet0Slot0 = 0x78, - Fleet0Slot1 = 0x79, - Fleet0Slot2 = 0x7a, - Fleet0Slot3 = 0x7b, - Fleet0Slot4 = 0x7c, - Fleet0Slot5 = 0x7d, - Fleet0Slot6 = 0x7e, - Fleet0Slot7 = 0x7f, - Fleet0Slot8 = 0x80, - Fleet0Slot9 = 0x81, - Fleet0Slot10 = 0x82, - Fleet0Slot11 = 0x83, - Fleet0Slot12 = 0x84, - Fleet0Slot13 = 0x85, - Fleet0Slot14 = 0x86, - Fleet0Slot15 = 0x87, - Fleet0Slot16 = 0x88, - Fleet0Slot17 = 0x89, - Fleet0Slot18 = 0x8a, - Fleet0Slot19 = 0x8b, - Fleet0Slot20 = 0x8c, - Fleet0Slot21 = 0x8d, - Fleet0Slot22 = 0x8e, - Fleet0Slot23 = 0x8f, - Fleet0Slot24 = 0x90, - Fleet0Slot25 = 0x91, - Fleet0Slot26 = 0x92, - Fleet0Slot27 = 0x93, - Fleet0Slot28 = 0x94, - Fleet0Slot29 = 0x95, - Fleet0Slot30 = 0x96, - Fleet0Slot31 = 0x97, - Fleet0Slot32 = 0x98, - Fleet0Slot33 = 0x99, - Fleet0Slot34 = 0x9a, - Fleet1Slot0 = 0x9b, - Fleet1Slot1 = 0x9c, - Fleet1Slot2 = 0x9d, - Fleet1Slot3 = 0x9e, - Fleet1Slot4 = 0x9f, - Fleet1Slot5 = 0xa0, - Fleet1Slot6 = 0xa1, - Fleet1Slot7 = 0xa2, - Fleet1Slot8 = 0xa3, - Fleet1Slot9 = 0xa4, - Fleet1Slot10 = 0xa5, - Fleet1Slot11 = 0xa6, - Fleet1Slot12 = 0xa7, - Fleet1Slot13 = 0xa8, - Fleet1Slot14 = 0xa9, - Fleet1Slot15 = 0xaa, - Fleet1Slot16 = 0xab, - Fleet1Slot17 = 0xac, - Fleet1Slot18 = 0xad, - Fleet1Slot19 = 0xae, - Fleet1Slot20 = 0xaf, - Fleet1Slot21 = 0xb0, - Fleet1Slot22 = 0xb1, - Fleet1Slot23 = 0xb2, - Fleet1Slot24 = 0xb3, - Fleet1Slot25 = 0xb4, - Fleet1Slot26 = 0xb5, - Fleet1Slot27 = 0xb6, - Fleet1Slot28 = 0xb7, - Fleet1Slot29 = 0xb8, - Fleet1Slot30 = 0xb9, - Fleet1Slot31 = 0xba, - Fleet1Slot32 = 0xbb, - Fleet1Slot33 = 0xbc, - Fleet1Slot34 = 0xbd, - Fleet2Slot0 = 0xbe, - Fleet2Slot1 = 0xbf, - Fleet2Slot2 = 0xc0, - Fleet2Slot3 = 0xc1, - Fleet2Slot4 = 0xc2, - Fleet2Slot5 = 0xc3, - Fleet2Slot6 = 0xc4, - Fleet2Slot7 = 0xc5, - Fleet2Slot8 = 0xc6, - Fleet2Slot9 = 0xc7, - Fleet2Slot10 = 0xc8, - Fleet2Slot11 = 0xc9, - Fleet2Slot12 = 0xca, - Fleet2Slot13 = 0xcb, - Fleet2Slot14 = 0xcc, - Fleet2Slot15 = 0xcd, - Fleet2Slot16 = 0xce, - Fleet2Slot17 = 0xcf, - Fleet2Slot18 = 0xd0, - Fleet2Slot19 = 0xd1, - Fleet2Slot20 = 0xd2, - Fleet2Slot21 = 0xd3, - Fleet2Slot22 = 0xd4, - Fleet2Slot23 = 0xd5, - Fleet2Slot24 = 0xd6, - Fleet2Slot25 = 0xd7, - Fleet2Slot26 = 0xd8, - Fleet2Slot27 = 0xd9, - Fleet2Slot28 = 0xda, - Fleet2Slot29 = 0xdb, - Fleet2Slot30 = 0xdc, - Fleet2Slot31 = 0xdd, - Fleet2Slot32 = 0xde, - Fleet2Slot33 = 0xdf, - Fleet2Slot34 = 0xe0, - TacticalMain = 0xe1, - TacticalOptions = 0xe2, - TacticalCamera = 0xe3, - TacticalBeacon = 0xe4, - TacticalWeapon0 = 0xe5, - TacticalWeapon1 = 0xe6, - TacticalReinforce = 0xe7, - TacticalRetreat = 0xe8, - TacticalStoryArc = 0xe9, - TacticalTechLevel = 0xea, - TacticalAttack = 0xeb, - TacticalAttackMove = 0xec, - TacticalMove = 0xed, - TacticalWaypoint = 0xee, - TacticalStop = 0xef, - TacticalGuard = 0xf0, - SpaceTacticalRadar = 0xf1, - SpaceTacticalCredits = 0xf2, - SpaceTacticalGrabBar = 0xf3, - SpaceTacticalHealthBar = 0xf4, - SpaceTacticalBracketSmall = 0xf5, - SpaceTacticalBracketMedium = 0xf6, - SpaceTacticalBracketLarge = 0xf7, - SpaceTacticalHeroIcon = 0xf8, - SpaceTacticalHeroHealth = 0xf9, - SpaceTacticalHealth = 0xfa, - SpaceTacticalHealthMedium = 0xfb, - SpaceTacticalHealthLarge = 0xfc, - SpaceTacticalShields = 0xfd, - SpaceTacticalShieldsMedium = 0xfe, - SpaceTacticalShieldsLarge = 0xff, - SpaceTacticalPower = 0x100, - SpaceTacticalControlGroup = 0x101, - SpaceTacticalAbilityIcon = 0x102, - SpaceTacticalGarrisonIcon = 0x103, - LandTacticalWeatherIcon = 0x104, - GarrisonSlotIcon = 0x105, - DsFireShell = 0x106, - DsFire = 0x107, - DsCountdownTimer = 0x108, - TacticalSelect00 = 0x109, - TacticalSelect01 = 0x10a, - TacticalSelect02 = 0x10b, - TacticalSelect03 = 0x10c, - TacticalSelect04 = 0x10d, - TacticalSelect05 = 0x10e, - TacticalSelect06 = 0x10f, - TacticalSelect07 = 0x110, - TacticalSelect08 = 0x111, - TacticalSelect09 = 0x112, - TacticalSelect10 = 0x113, - TacticalSelect11 = 0x114, - TacticalSelect12 = 0x115, - TacticalSelect13 = 0x116, - TacticalSelect14 = 0x117, - TacticalSelect15 = 0x118, - TacticalSelect16 = 0x119, - TacticalSelect17 = 0x11a, - TacticalSelect18 = 0x11b, - TacticalSelect19 = 0x11c, - TacticalSelect20 = 0x11d, - TacticalSelect21 = 0x11e, - TacticalSelect22 = 0x11f, - TacticalSelect23 = 0x120, - TacticalSelect24 = 0x121, - TacticalSelect25 = 0x122, - TacticalSelect26 = 0x123, - TacticalSelect27 = 0x124, - TacticalSelect28 = 0x125, - TacticalSelect29 = 0x126, - TacticalSelect30 = 0x127, - TacticalSelect31 = 0x128, - TacticalSelect32 = 0x129, - TacticalSelect33 = 0x12a, - TacticalSelect34 = 0x12b, - TacticalSelect35 = 0x12c, - TacticalSelect36 = 0x12d, - TacticalSelect37 = 0x12e, - TacticalSelect38 = 0x12f, - TacticalSelect39 = 0x130, - TacticalSelect40 = 0x131, - TacticalSelect41 = 0x132, - TacticalSelect42 = 0x133, - TacticalSelect43 = 0x134, - TacticalSelect44 = 0x135, - TacticalSelect45 = 0x136, - TacticalSelect46 = 0x137, - TacticalSelect47 = 0x138, - TacticalSelectHealth00 = 0x139, - TacticalSelectHealth01 = 0x13a, - TacticalSelectHealth02 = 0x13b, - TacticalSelectHealth03 = 0x13c, - TacticalSelectHealth04 = 0x13d, - TacticalSelectHealth05 = 0x13e, - TacticalSelectHealth06 = 0x13f, - TacticalSelectHealth07 = 0x140, - TacticalSelectHealth08 = 0x141, - TacticalSelectHealth09 = 0x142, - TacticalSelectHealth10 = 0x143, - TacticalSelectHealth11 = 0x144, - TacticalSelectHealth12 = 0x145, - TacticalSelectHealth13 = 0x146, - TacticalSelectHealth14 = 0x147, - TacticalSelectHealth15 = 0x148, - TacticalSelectHealth16 = 0x149, - TacticalSelectHealth17 = 0x14a, - TacticalSelectHealth18 = 0x14b, - TacticalSelectHealth19 = 0x14c, - TacticalSelectHealth20 = 0x14d, - TacticalSelectHealth21 = 0x14e, - TacticalSelectHealth22 = 0x14f, - TacticalSelectHealth23 = 0x150, - TacticalSelectHealth24 = 0x151, - TacticalSelectHealth25 = 0x152, - TacticalSelectHealth26 = 0x153, - TacticalSelectHealth27 = 0x154, - TacticalSelectHealth28 = 0x155, - TacticalSelectHealth29 = 0x156, - TacticalSelectHealth30 = 0x157, - TacticalSelectHealth31 = 0x158, - TacticalSelectHealth32 = 0x159, - TacticalSelectHealth33 = 0x15a, - TacticalSelectHealth34 = 0x15b, - TacticalSelectHealth35 = 0x15c, - TacticalSelectHealth36 = 0x15d, - TacticalSelectHealth37 = 0x15e, - TacticalSelectHealth38 = 0x15f, - TacticalSelectHealth39 = 0x160, - TacticalSelectHealth40 = 0x161, - TacticalSelectHealth41 = 0x162, - TacticalSelectHealth42 = 0x163, - TacticalSelectHealth43 = 0x164, - TacticalSelectHealth44 = 0x165, - TacticalSelectHealth45 = 0x166, - TacticalSelectHealth46 = 0x167, - TacticalSelectHealth47 = 0x168, - TacticalSelectShield00 = 0x169, - TacticalSelectShield01 = 0x16a, - TacticalSelectShield02 = 0x16b, - TacticalSelectShield03 = 0x16c, - TacticalSelectShield04 = 0x16d, - TacticalSelectShield05 = 0x16e, - TacticalSelectShield06 = 0x16f, - TacticalSelectShield07 = 0x170, - TacticalSelectShield08 = 0x171, - TacticalSelectShield09 = 0x172, - TacticalSelectShield10 = 0x173, - TacticalSelectShield11 = 0x174, - TacticalSelectShield12 = 0x175, - TacticalSelectShield13 = 0x176, - TacticalSelectShield14 = 0x177, - TacticalSelectShield15 = 0x178, - TacticalSelectShield16 = 0x179, - TacticalSelectShield17 = 0x17a, - TacticalSelectShield18 = 0x17b, - TacticalSelectShield19 = 0x17c, - TacticalSelectShield20 = 0x17d, - TacticalSelectShield21 = 0x17e, - TacticalSelectShield22 = 0x17f, - TacticalSelectShield23 = 0x180, - TacticalSelectShield24 = 0x181, - TacticalSelectShield25 = 0x182, - TacticalSelectShield26 = 0x183, - TacticalSelectShield27 = 0x184, - TacticalSelectShield28 = 0x185, - TacticalSelectShield29 = 0x186, - TacticalSelectShield30 = 0x187, - TacticalSelectShield31 = 0x188, - TacticalSelectShield32 = 0x189, - TacticalSelectShield33 = 0x18a, - TacticalSelectShield34 = 0x18b, - TacticalSelectShield35 = 0x18c, - TacticalSelectShield36 = 0x18d, - TacticalSelectShield37 = 0x18e, - TacticalSelectShield38 = 0x18f, - TacticalSelectShield39 = 0x190, - TacticalSelectShield40 = 0x191, - TacticalSelectShield41 = 0x192, - TacticalSelectShield42 = 0x193, - TacticalSelectShield43 = 0x194, - TacticalSelectShield44 = 0x195, - TacticalSelectShield45 = 0x196, - TacticalSelectShield46 = 0x197, - TacticalSelectShield47 = 0x198, - TacticalBorder00 = 0x199, - TacticalBorder01 = 0x19a, - TacticalBorder02 = 0x19b, - TacticalBorder03 = 0x19c, - TacticalBorder04 = 0x19d, - TacticalBorder05 = 0x19e, - TacticalBorder06 = 0x19f, - TacticalBorder07 = 0x1a0, - TacticalBorder08 = 0x1a1, - TacticalBorder09 = 0x1a2, - TacticalBorder10 = 0x1a3, - TacticalBorder11 = 0x1a4, - TacticalSelectButton00 = 0x1a5, - TacticalSelectButton01 = 0x1a6, - TacticalSelectButton02 = 0x1a7, - TacticalSelectButton03 = 0x1a8, - TacticalSelectButton04 = 0x1a9, - TacticalSelectButton05 = 0x1aa, - TacticalSelectButton06 = 0x1ab, - TacticalSelectButton07 = 0x1ac, - TacticalSelectButton08 = 0x1ad, - TacticalSelectButton09 = 0x1ae, - TacticalSelectButton10 = 0x1af, - TacticalSelectButton11 = 0x1b0, - TacticalSelectButton12 = 0x1b1, - TacticalSelectButton13 = 0x1b2, - TacticalSelectButton14 = 0x1b3, - TacticalSelectButton15 = 0x1b4, - TacticalSelectButton16 = 0x1b5, - TacticalSelectButton17 = 0x1b6, - TacticalSelectButton18 = 0x1b7, - TacticalSelectButton19 = 0x1b8, - TacticalSelectButton20 = 0x1b9, - TacticalSelectButton21 = 0x1ba, - TacticalSelectButton22 = 0x1bb, - TacticalSelectButton23 = 0x1bc, - TacticalSelectButton24 = 0x1bd, - TacticalSelectButton25 = 0x1be, - TacticalSelectButton26 = 0x1bf, - TacticalSelectButton27 = 0x1c0, - TacticalSelectButton28 = 0x1c1, - TacticalSelectButton29 = 0x1c2, - TacticalSelectButton30 = 0x1c3, - TacticalSelectButton31 = 0x1c4, - TacticalSelectButton32 = 0x1c5, - TacticalSelectButton33 = 0x1c6, - TacticalSelectButton34 = 0x1c7, - TacticalSelectButton35 = 0x1c8, - TacticalSelectButton36 = 0x1c9, - TacticalSelectButton37 = 0x1ca, - TacticalSelectButton38 = 0x1cb, - TacticalSelectButton39 = 0x1cc, - TacticalSelectButton40 = 0x1cd, - TacticalSelectButton41 = 0x1ce, - TacticalSelectButton42 = 0x1cf, - TacticalSelectButton43 = 0x1d0, - TacticalSelectButton44 = 0x1d1, - TacticalSelectButton45 = 0x1d2, - TacticalSelectButton46 = 0x1d3, - TacticalSelectButton47 = 0x1d4, - TacticalBuildQueue00 = 0x1d5, - TacticalBuildQueue01 = 0x1d6, - TacticalBuildQueue02 = 0x1d7, - TacticalBuildQueue03 = 0x1d8, - TacticalBuildQueue04 = 0x1d9, - TacticalBuildQueue05 = 0x1da, - TacticalBuildQueue06 = 0x1db, - TacticalBuildQueue07 = 0x1dc, - TacticalBuildQueue08 = 0x1dd, - TacticalBuildQueue09 = 0x1de, - TooltipBack = 0x1df, - TooltipName = 0x1e0, - TooltipPrice = 0x1e1, - TooltipIcon = 0x1e2, - TooltipIconLand = 0x1e3, - TooltipLeftJustified = 0x1e4, - EncyclopediaBack = 0x1e5, - EncyclopediaHeaderText = 0x1e6, - EncyclopediaText = 0x1e7, - EncyclopediaRightText = 0x1e8, - EncyclopediaCenterText = 0x1e9, - EncyclopediaIcon = 0x1ea, - EncyclopediaCostText = 0x1eb, - ZoomedBack = 0x1ec, - ZoomedHeaderText = 0x1ed, - ZoomedText = 0x1ee, - ZoomedRightText = 0x1ef, - ZoomedCenterText = 0x1f0, - ZoomedCostText = 0x1f1, - GPlanetFleet = 0x1f2, - GPlanetName = 0x1f3, - GPlanetValue = 0x1f4, - GPoliticalControl = 0x1f5, - GSpaceLevel = 0x1f6, - GSpaceIcon = 0x1f7, - GSpaceLevelPips = 0x1f8, - GGroundLevel = 0x1f9, - GGroundIcon = 0x1fa, - GGroundLevelPips = 0x1fb, - GConflict = 0x1fc, - GHero = 0x1fd, - GEnemyHero = 0x1fe, - GBuild = 0x1ff, - GSmuggler = 0x200, - GBountyHunter = 0x201, - GPlanetLandForces = 0x202, - GGalacticRadarBlip = 0x203, - GGalacticRadarView = 0x204, - GSmuggled = 0x205, - GSpecialAbility = 0x206, - GHeroIcon = 0x207, - GPlanetRing = 0x208, - GWeather = 0x209, - GPlanetAbility = 0x20a, - GCorruptionText = 0x20b, - GCorruptionIcon = 0x20c, - TutorialText = 0x20d, - TutorialTextBack = 0x20e, - RadarBlip = 0x20f, - TacticalBuildButtonShell = 0x210, - TacticalBuildButton0 = 0x211, - TacticalBuildButton1 = 0x212, - TacticalBuildButton2 = 0x213, - TacticalBuildButton3 = 0x214, - TacticalBuildButton4 = 0x215, - TacticalBuildButton5 = 0x216, - TacticalSellButton = 0x217, - ReinforcementShell = 0x218, - ReinforcementCancel = 0x219, - ReinforcementSlot00 = 0x21a, - ReinforcementSlot01 = 0x21b, - ReinforcementSlot02 = 0x21c, - ReinforcementSlot03 = 0x21d, - ReinforcementSlot04 = 0x21e, - ReinforcementSlot05 = 0x21f, - ReinforcementSlot06 = 0x220, - ReinforcementSlot07 = 0x221, - ReinforcementSlot08 = 0x222, - ReinforcementSlot09 = 0x223, - ReinforcementSlot10 = 0x224, - ReinforcementSlot11 = 0x225, - ReinforcementSlot12 = 0x226, - ReinforcementSlot13 = 0x227, - ReinforcementSlot14 = 0x228, - ReinforcementSlot15 = 0x229, - ReinforcementSlot16 = 0x22a, - ReinforcementSlot17 = 0x22b, - ReinforcementSlot18 = 0x22c, - ReinforcementSlot19 = 0x22d, - ReinforcementCap2 = 0x22e, - ReinforcementCap2Text = 0x22f, - ReinforcementCounter = 0x230, - GarrisonRespawnCounter = 0x231, - SkirmishUpgrade = 0x232, - PendingBattleShell = 0x233, - PendingBattleText = 0x234, - PendingBattleButton = 0x235, - PendingBattleAutoresolve = 0x236, - PendingBattleGraphLeft = 0x237, - PendingBattleGraphRight = 0x238, - TatcicalAutoresolveShell = 0x239, - TacticalAutoresolveButton = 0x23a, - TacticalAutoresolveGraphLeft = 0x23b, - TacticalAutoresolveGraphRight = 0x23c, - ObjectiveBack = 0x23d, - ObjectiveHeaderText = 0x23e, - ObjectiveText = 0x23f, - ObjectiveIcon = 0x240, - GuiDialogTooltip = 0x241, - TargetUnitTypeShell = 0x242, - TargetUnitTypeTitle = 0x243, - TargetUnitTypeCancel = 0x244, - TargetUnitTypeDescription = 0x245, - TargetUnitTypeSlot00 = 0x246, - TargetUnitTypeSlot01 = 0x247, - TargetUnitTypeSlot02 = 0x248, - TargetUnitTypeSlot03 = 0x249, - TargetUnitTypeSlot04 = 0x24a, - TargetUnitTypeSlot05 = 0x24b, - TargetUnitTypeSlot06 = 0x24c, - TargetUnitTypeSlot07 = 0x24d, - TargetUnitTypeSlot08 = 0x24e, - TargetUnitTypeSlot09 = 0x24f, - TargetUnitTypeSlot10 = 0x250, - TargetUnitTypeSlot11 = 0x251, - TargetUnitTypeName00 = 0x252, - TargetUnitTypeName01 = 0x253, - TargetUnitTypeName02 = 0x254, - TargetUnitTypeName03 = 0x255, - TargetUnitTypeName04 = 0x256, - TargetUnitTypeName05 = 0x257, - TargetUnitTypeName06 = 0x258, - TargetUnitTypeName07 = 0x259, - TargetUnitTypeName08 = 0x25a, - TargetUnitTypeName09 = 0x25b, - TargetUnitTypeName10 = 0x25c, - TargetUnitTypeName11 = 0x25d, - TargetUnitTypePrice00 = 0x25e, - TargetUnitTypePrice01 = 0x25f, - TargetUnitTypePrice02 = 0x260, - TargetUnitTypePrice03 = 0x261, - TargetUnitTypePrice04 = 0x262, - TargetUnitTypePrice05 = 0x263, - TargetUnitTypePrice06 = 0x264, - TargetUnitTypePrice07 = 0x265, - TargetUnitTypePrice08 = 0x266, - TargetUnitTypePrice09 = 0x267, - TargetUnitTypePrice10 = 0x268, - TargetUnitTypePrice11 = 0x269, - VcrButtonPlayPause = 0x26a, - VcrButtonFastForward = 0x26b, - VcrButtonFastForwardTactical = 0x26c, - VcrButtonPlayPauseTactical = 0x26d, - AdvisorHintPopupGalactic = 0x26e, - AdvisorHintPopupTactical = 0x26f, - AdvisorHintBack = 0x270, - TextOrganizeFleet00 = 0x271, - TextOrganizeFleet01 = 0x272, - TextOrganizeFleet02 = 0x273, - IconOrganizeFleet00 = 0x274, - IconOrganizeFleet01 = 0x275, - IconOrganizeFleet02 = 0x276, - TextOrganizeLandFleet = 0x277, - CsAbilityButton = 0x278, - CsAbilityText = 0x279, - MovieBoneGalactic = 0x27a, - MovieBoneTactical = 0x27b, - GenericCollision = 0x27c, - GoodHeroShell = 0x27d, - GoodHeroSlot00 = 0x27e, - GoodHeroSlot01 = 0x27f, - GoodHeroSlot02 = 0x280, - GoodHeroSlot03 = 0x281, - GoodHeroSlot04 = 0x282, - GoodHeroSlot05 = 0x283, - GoodHeroSlot06 = 0x284, - GoodHeroSlot07 = 0x285, - GoodHeroSlot08 = 0x286, - GoodHeroSlot09 = 0x287, - GoodHeroSlot10 = 0x288, - GoodHeroHealth00 = 0x289, - GoodHeroHealth01 = 0x28a, - GoodHeroHealth02 = 0x28b, - GoodHeroHealth03 = 0x28c, - GoodHeroHealth04 = 0x28d, - GoodHeroHealth05 = 0x28e, - GoodHeroHealth06 = 0x28f, - GoodHeroHealth07 = 0x290, - GoodHeroHealth08 = 0x291, - GoodHeroHealth09 = 0x292, - GoodHeroHealth10 = 0x293, - EvilHeroShell = 0x294, - EvilHeroSlot00 = 0x295, - EvilHeroSlot01 = 0x296, - EvilHeroSlot02 = 0x297, - EvilHeroSlot03 = 0x298, - EvilHeroSlot04 = 0x299, - EvilHeroSlot05 = 0x29a, - EvilHeroSlot06 = 0x29b, - EvilHeroSlot07 = 0x29c, - EvilHeroSlot08 = 0x29d, - EvilHeroSlot09 = 0x29e, - EvilHeroSlot10 = 0x29f, - EvilHeroHealth00 = 0x2a0, - EvilHeroHealth01 = 0x2a1, - EvilHeroHealth02 = 0x2a2, - EvilHeroHealth03 = 0x2a3, - EvilHeroHealth04 = 0x2a4, - EvilHeroHealth05 = 0x2a5, - EvilHeroHealth06 = 0x2a6, - EvilHeroHealth07 = 0x2a7, - EvilHeroHealth08 = 0x2a8, - EvilHeroHealth09 = 0x2a9, - EvilHeroHealth10 = 0x2aa, - PauseShell = 0x2ab, - PauseText = 0x2ac, - PauseButton = 0x2ad, - StoryCampaignPendingBattleShell = 0x2ae, - StoryCampaignPendingBattleText = 0x2af, - StoryCampaignPendingBattleButton = 0x2b0, - MapActivate = 0x2b1, - MapShell = 0x2b2, - MapOverlayShell = 0x2b3, - MapGroundForcesText = 0x2b4, - MapDragDropText = 0x2b5, - MapUnitCapText = 0x2b6, - MapUnitCapNumber = 0x2b7, - MapBuildingCapText = 0x2b8, - MapBuildingCapNumber = 0x2b9, - MapFactionIcon = 0x2ba, - MapPlanetNameText = 0x2bb, - MapPlanetIncomeNumber = 0x2bc, - MapDayCounterText = 0x2bd, - MapDayCounter = 0x2be, - MapBackButton = 0x2bf, - MapWeatherIcon = 0x2c0, - MapPlanetBonusIcon = 0x2c1, - MapRadarMap = 0x2c2, - MapZoomOut = 0x2c3, - MapEncyclopediaPopup = 0x2c4, - AdvancedMapFilter0 = 0x2c5, - AdvancedMapFilter1 = 0x2c6, - AdvancedMapFilter2 = 0x2c7, - AdvancedMapFilter3 = 0x2c8, - AdvancedMapOptions = 0x2c9, - MapMainMap = 0x2ca, - MapSpaceUnit00 = 0x2cb, - MapSpaceUnit01 = 0x2cc, - MapSpaceUnit02 = 0x2cd, - MapSpaceUnit03 = 0x2ce, - MapSpaceUnit04 = 0x2cf, - MapSpaceUnit05 = 0x2d0, - MapSpaceUnit06 = 0x2d1, - MapSpaceUnit07 = 0x2d2, - MapSpaceUnit08 = 0x2d3, - MapSpaceUnit09 = 0x2d4, - MapSpaceUnit10 = 0x2d5, - MapSpaceUnit11 = 0x2d6, - MapSpaceUnit12 = 0x2d7, - MapSpaceUnit13 = 0x2d8, - MapSpaceUnit14 = 0x2d9, - MapSpaceUnit15 = 0x2da, - MapSpaceUnit16 = 0x2db, - MapSpaceUnit17 = 0x2dc, - MapSpaceUnit18 = 0x2dd, - MapSpaceUnit19 = 0x2de, - MapBuildPad00 = 0x2df, - MapBuildPad01 = 0x2e0, - MapBuildPad02 = 0x2e1, - MapBuildPad03 = 0x2e2, - MapBuildPad04 = 0x2e3, - MapBuildPad05 = 0x2e4, - MapBuildPad06 = 0x2e5, - MapBuildPad07 = 0x2e6, - MapBuildPad08 = 0x2e7, - MapBuildPad09 = 0x2e8, - MapBuildPad10 = 0x2e9, - MapBuildPad11 = 0x2ea, - MapBuildPad12 = 0x2eb, - MapBuildPad13 = 0x2ec, - MapBuildPad14 = 0x2ed, - MapBuildPad15 = 0x2ee, - MapBuildPad16 = 0x2ef, - MapBuildPad17 = 0x2f0, - MapBuildPad18 = 0x2f1, - MapBuildPad19 = 0x2f2, - MapBuildPad20 = 0x2f3, - MapBuildPad21 = 0x2f4, - MapBuildPad22 = 0x2f5, - MapBuildPad23 = 0x2f6, - MapBuildPad24 = 0x2f7, - MapBuildPad25 = 0x2f8, - MapBuildPad26 = 0x2f9, - MapBuildPad27 = 0x2fa, - MapBuildPad28 = 0x2fb, - MapBuildPad29 = 0x2fc, - MapBuildPad30 = 0x2fd, - MapBuildPad31 = 0x2fe, - MapBuildPad32 = 0x2ff, - MapReinforcePad00 = 0x300, - MapReinforcePad01 = 0x301, - MapReinforcePad02 = 0x302, - MapReinforcePad03 = 0x303, - MapReinforcePad04 = 0x304, - MapReinforcePad05 = 0x305, - MapReinforcePad06 = 0x306, - MapBunkerAndBuildPad00 = 0x307, - MapBunkerAndBuildPad01 = 0x308, - MapBunkerAndBuildPad02 = 0x309, - MapBunkerAndBuildPad03 = 0x30a, - MapBunkerAndBuildPad04 = 0x30b, - MapBunkerAndBuildPad05 = 0x30c, - MapBunkerAndBuildPad06 = 0x30d, - MapBunkerAndBuildPad07 = 0x30e, - MapBunkerAndBuildPad08 = 0x30f, - MapBunkerAndBuildPad09 = 0x310, - MapBunkerAndBuildPad10 = 0x311, - MapBunkerAndBuildPad11 = 0x312, - MapBunkerAndBuildPad12 = 0x313, - MapBunkerAndBuildPad13 = 0x314, - MapBunkerAndBuildPad14 = 0x315, - MapBunkerAndBuildPad15 = 0x316, - MapBunkerAndBuildPad16 = 0x317, - MapBunkerAndBuildPad17 = 0x318, - MapBunkerAndBuildPad18 = 0x319, - MapBunkerAndBuildPad19 = 0x31a, - MapBunkerAndBuildPad20 = 0x31b, - MapBunkerAndBuildPad21 = 0x31c, - MapBunkerAndBuildPad22 = 0x31d, - MapBunkerAndBuildPad23 = 0x31e, - MapBunkerAndBuildPad24 = 0x31f, - MapBunkerAndBuildPad25 = 0x320, - MapBunkerAndBuildPad26 = 0x321, - MapBunkerAndBuildPad27 = 0x322, - MapBunkerAndBuildPad28 = 0x323, - MapBunkerAndBuildPad29 = 0x324, - MapBunkerAndBuildPad30 = 0x325, - MapBunkerAndBuildPad31 = 0x326, - MapBunkerAndBuildPad32 = 0x327, - MapBunkerAndBuildPad33 = 0x328, - MapBunkerAndBuildPad34 = 0x329, - MapBunkerAndBuildPad35 = 0x32a, - MapBunkerAndBuildPad36 = 0x32b, - MapBunkerAndBuildPad37 = 0x32c, - MapBunkerAndBuildPad38 = 0x32d, - MapBunkerAndBuildPad39 = 0x32e, - MapUnitPad00 = 0x32f, - MapUnitPad01 = 0x330, - MapUnitPad02 = 0x331, - MapUnitPad03 = 0x332, - MapUnitPad04 = 0x333, - MapUnitPad05 = 0x334, - MapUnitPad06 = 0x335, - MapUnitPad07 = 0x336, - MapUnitPad08 = 0x337, - MapUnitPad09 = 0x338, - BribeDisplay = 0x339, - BlackMarketShell = 0x33a, - BlackMarketTitle = 0x33b, - BlackMarketPlanet = 0x33c, - BlackMarketTechLevelHeader = 0x33d, - BlackMarketSlotHeader = 0x33e, - BlackMarketCancel = 0x33f, - BlackMarketEncyclopedia = 0x340, - BlackMarketTechLevel00 = 0x341, - BlackMarketTechLevel01 = 0x342, - BlackMarketTechLevel02 = 0x343, - BlackMarketTechLevel03 = 0x344, - BlackMarketTechLevel04 = 0x345, - BlackMarketSlot00 = 0x346, - BlackMarketSlot01 = 0x347, - BlackMarketSlot02 = 0x348, - BlackMarketSlot03 = 0x349, - BlackMarketSlot04 = 0x34a, - BlackMarketSlotDescription00 = 0x34b, - BlackMarketSlotDescription01 = 0x34c, - BlackMarketSlotDescription02 = 0x34d, - BlackMarketSlotDescription03 = 0x34e, - BlackMarketSlotDescription04 = 0x34f, - BlackMarketSlotPrice00 = 0x350, - BlackMarketSlotPrice01 = 0x351, - BlackMarketSlotPrice02 = 0x352, - BlackMarketSlotPrice03 = 0x353, - BlackMarketSlotPrice04 = 0x354, - SabotageShell = 0x355, - SabotageTitle = 0x356, - SabotageTitle2 = 0x357, - SabotageTechLevel = 0x358, - SabotageCancel = 0x359, - SabotageDescription = 0x35a, - SabotageSlot00 = 0x35b, - SabotageSlot01 = 0x35c, - SabotageSlot02 = 0x35d, - SabotageSlot03 = 0x35e, - SabotageSlot04 = 0x35f, - SabotageSlot05 = 0x360, - SabotageSlot06 = 0x361, - SabotageSlot07 = 0x362, - SabotageSlot08 = 0x363, - SabotageSlot09 = 0x364, - SabotagePrice00 = 0x365, - SabotagePrice01 = 0x366, - SabotagePrice02 = 0x367, - SabotagePrice03 = 0x368, - SabotagePrice04 = 0x369, - SabotagePrice05 = 0x36a, - SabotagePrice06 = 0x36b, - SabotagePrice07 = 0x36c, - SabotagePrice08 = 0x36d, - SabotagePrice09 = 0x36e, - SabotageName00 = 0x36f, - SabotageName01 = 0x370, - SabotageName02 = 0x371, - SabotageName03 = 0x372, - SabotageName04 = 0x373, - SabotageName05 = 0x374, - SabotageName06 = 0x375, - SabotageName07 = 0x376, - SabotageName08 = 0x377, - SabotageName09 = 0x378, - PlanetaryBombardment = 0x379, - PlanetaryBombardmentRecharge = 0x37a, - SuperLaser = 0x37b, - SuperLaserRecharge = 0x37c, - GenericFlytext = 0x37d, - BribedIcon = 0x37e, - SurfaceModIcon = 0x37f, - RemoteBombIcon = 0x380, - CorruptionShell = 0x381, - CorruptionTitle = 0x382, - CorruptionPlanetName = 0x383, - CorruptionEncyclopedia = 0x384, - CorruptionClose = 0x385, - CorruptionPlanetModel = 0x386, - CorruptionInfo00 = 0x387, - CorruptionInfo01 = 0x388, - CorruptionChoiceIcon0 = 0x389, - CorruptionChoiceIcon1 = 0x38a, - CorruptionChoiceIcon2 = 0x38b, - CorruptionChoiceCost0 = 0x38c, - CorruptionChoiceCost1 = 0x38d, - CorruptionChoiceCost2 = 0x38e, - CorruptionChoiceTitle0 = 0x38f, - CorruptionChoiceTitle1 = 0x390, - CorruptionChoiceTitle2 = 0x391, - CorruptionChoiceBenefitText00 = 0x392, - CorruptionChoiceBenefitText01 = 0x393, - CorruptionChoiceBenefitText02 = 0x394, - CorruptionChoiceBenefitText03 = 0x395, - CorruptionChoiceBenefitText10 = 0x396, - CorruptionChoiceBenefitText11 = 0x397, - CorruptionChoiceBenefitText12 = 0x398, - CorruptionChoiceBenefitText13 = 0x399, - CorruptionChoiceBenefitText20 = 0x39a, - CorruptionChoiceBenefitText21 = 0x39b, - CorruptionChoiceBenefitText22 = 0x39c, - CorruptionChoiceBenefitText23 = 0x39d, - CorruptionChoiceRequirement0 = 0x39e, - CorruptionChoiceRequirement1 = 0x39f, - CorruptionChoiceRequirement2 = 0x3a0, - CorruptionChoiceRequirementIcon0000 = 0x3a1, - CorruptionChoiceRequirementIcon0001 = 0x3a2, - CorruptionChoiceRequirementIcon0002 = 0x3a3, - CorruptionChoiceRequirementIcon0003 = 0x3a4, - CorruptionChoiceRequirementIcon0100 = 0x3a5, - CorruptionChoiceRequirementIcon0101 = 0x3a6, - CorruptionChoiceRequirementIcon0102 = 0x3a7, - CorruptionChoiceRequirementIcon0103 = 0x3a8, - CorruptionChoiceRequirementIcon1000 = 0x3a9, - CorruptionChoiceRequirementIcon1001 = 0x3aa, - CorruptionChoiceRequirementIcon1002 = 0x3ab, - CorruptionChoiceRequirementIcon1003 = 0x3ac, - CorruptionChoiceRequirementIcon1100 = 0x3ad, - CorruptionChoiceRequirementIcon1101 = 0x3ae, - CorruptionChoiceRequirementIcon1102 = 0x3af, - CorruptionChoiceRequirementIcon1103 = 0x3b0, - CorruptionChoiceRequirementIcon2000 = 0x3b1, - CorruptionChoiceRequirementIcon2001 = 0x3b2, - CorruptionChoiceRequirementIcon2002 = 0x3b3, - CorruptionChoiceRequirementIcon2003 = 0x3b4, - CorruptionChoiceRequirementIcon2100 = 0x3b5, - CorruptionChoiceRequirementIcon2101 = 0x3b6, - CorruptionChoiceRequirementIcon2102 = 0x3b7, - CorruptionChoiceRequirementIcon2103 = 0x3b8, - HackSuperWeaponShell = 0x3b9, - HackSuperWeaponTitle = 0x3ba, - HackSuperWeaponText00 = 0x3bb, - HackSuperWeaponText01 = 0x3bc, - HackSuperWeaponText02 = 0x3bd, - HackSuperWeaponText03 = 0x3be, - HackSuperWeaponText04 = 0x3bf, - HackSuperWeaponText05 = 0x3c0, - HackSuperWeaponText06 = 0x3c1, - HackSuperWeaponCost = 0x3c2, - HackSuperWeaponAccept = 0x3c3, - HackSuperWeaponCancel = 0x3c4, - Count = 0x3c5, - None = 0x3c6 -}; \ No newline at end of file + public static readonly CommandBarComponentId None = new(GameEngineType.Eaw, -1); + + public GameEngineType TargetEngine { get; } + + public int Value { get; } + + internal CommandBarComponentId(GameEngineType engine, int value) + { + TargetEngine = engine; + Value = value; + } + + public override string ToString() + { + if (Value == -1) + return "NONE (-1)"; + + var nameLookup = SupportedCommandBarComponentData.GetComponentIdsForEngine(TargetEngine); + return $"'{nameLookup[this]}' ({Value})"; + } + + public bool Equals(CommandBarComponentId other) + { + return TargetEngine == other.TargetEngine && Value == other.Value; + } + + public override bool Equals(object? obj) + { + return obj is CommandBarComponentId other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine((int)TargetEngine, Value); + } + + public static bool operator ==(CommandBarComponentId left, CommandBarComponentId right) + { + return left.Equals(right); + } + + public static bool operator !=(CommandBarComponentId left, CommandBarComponentId right) + { + return !(left == right); + } +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 8f1dbd9..75de497 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -6,8 +6,12 @@ using PG.StarWarsGame.Engine.CommandBar.Xml; using PG.StarWarsGame.Engine.Database; 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.Engine.Xml.Parsers; +using PG.StarWarsGame.Files.Binary; using PG.StarWarsGame.Files.MTD.Files; using PG.StarWarsGame.Files.MTD.Services; using System; @@ -15,10 +19,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using PG.StarWarsGame.Engine.GameConstants; -using PG.StarWarsGame.Engine.Rendering; -using PG.StarWarsGame.Engine.Rendering.Font; -using PG.StarWarsGame.Files.Binary; namespace PG.StarWarsGame.Engine.CommandBar; @@ -119,14 +119,13 @@ await Task.Run(() => contentParser.ParseEntriesFromFileListXml( private void LinkComponentsWithActions() { - var ids = (CommandBarComponentId[])Enum.GetValues(typeof(CommandBarComponentId)); - foreach (var id in ids) + var nameLookup = SupportedCommandBarComponentData.GetComponentIdsForEngine(GameRepository.EngineType); + + foreach (var idPair in nameLookup) { - if (!SupportedCommandBarComponentData.SupportedComponents.TryGetValue(id, out var name)) - continue; - var crc = _hashingService.GetCrc32(name, PGConstants.DefaultPGEncoding); + var crc = _hashingService.GetCrc32(idPair.Value, PGConstants.DefaultPGEncoding); if (NamedEntries.TryGetFirstValue(crc, out var component)) - component.Id = id; + component.Id = idPair.Key; } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/EawCommandBarComponentIds.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/EawCommandBarComponentIds.cs new file mode 100644 index 0000000..ee47d05 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/EawCommandBarComponentIds.cs @@ -0,0 +1,665 @@ +namespace PG.StarWarsGame.Engine.CommandBar; + +public static class EawCommandBarComponentIds +{ + public static readonly CommandBarComponentId MainShell = new(GameEngineType.Eaw, 0x0); + public static readonly CommandBarComponentId ProductionOptions = new(GameEngineType.Eaw, 0x1); + public static readonly CommandBarComponentId GalacticCamera = new(GameEngineType.Eaw, 0x2); + public static readonly CommandBarComponentId PlanetName = new(GameEngineType.Eaw, 0x3); + public static readonly CommandBarComponentId PlanetValue = new(GameEngineType.Eaw, 0x4); + public static readonly CommandBarComponentId PlanetAffiliation = new(GameEngineType.Eaw, 0x5); + public static readonly CommandBarComponentId PlayerCredits = new(GameEngineType.Eaw, 0x6); + public static readonly CommandBarComponentId ReinforcementCap = new(GameEngineType.Eaw, 0x7); + public static readonly CommandBarComponentId PlanetNameTactical = new(GameEngineType.Eaw, 0x8); + public static readonly CommandBarComponentId PlanetInfo = new(GameEngineType.Eaw, 0x9); + public static readonly CommandBarComponentId Production0 = new(GameEngineType.Eaw, 0xa); + public static readonly CommandBarComponentId Production1 = new(GameEngineType.Eaw, 0xb); + public static readonly CommandBarComponentId Production2 = new(GameEngineType.Eaw, 0xc); + public static readonly CommandBarComponentId Production3 = new(GameEngineType.Eaw, 0xd); + public static readonly CommandBarComponentId Production4 = new(GameEngineType.Eaw, 0xe); + public static readonly CommandBarComponentId Production5 = new(GameEngineType.Eaw, 0xf); + public static readonly CommandBarComponentId Production6 = new(GameEngineType.Eaw, 0x10); + public static readonly CommandBarComponentId Production7 = new(GameEngineType.Eaw, 0x11); + public static readonly CommandBarComponentId Production8 = new(GameEngineType.Eaw, 0x12); + public static readonly CommandBarComponentId Production9 = new(GameEngineType.Eaw, 0x13); + public static readonly CommandBarComponentId Production10 = new(GameEngineType.Eaw, 0x14); + public static readonly CommandBarComponentId Production11 = new(GameEngineType.Eaw, 0x15); + public static readonly CommandBarComponentId Production12 = new(GameEngineType.Eaw, 0x16); + public static readonly CommandBarComponentId Production13 = new(GameEngineType.Eaw, 0x17); + public static readonly CommandBarComponentId Production14 = new(GameEngineType.Eaw, 0x18); + public static readonly CommandBarComponentId Production15 = new(GameEngineType.Eaw, 0x19); + public static readonly CommandBarComponentId Production16 = new(GameEngineType.Eaw, 0x1a); + public static readonly CommandBarComponentId Production17 = new(GameEngineType.Eaw, 0x1b); + public static readonly CommandBarComponentId Production18 = new(GameEngineType.Eaw, 0x1c); + public static readonly CommandBarComponentId Production19 = new(GameEngineType.Eaw, 0x1d); + public static readonly CommandBarComponentId Production20 = new(GameEngineType.Eaw, 0x1e); + public static readonly CommandBarComponentId Production21 = new(GameEngineType.Eaw, 0x1f); + public static readonly CommandBarComponentId Production22 = new(GameEngineType.Eaw, 0x20); + public static readonly CommandBarComponentId Production23 = new(GameEngineType.Eaw, 0x21); + public static readonly CommandBarComponentId Production24 = new(GameEngineType.Eaw, 0x22); + public static readonly CommandBarComponentId Production25 = new(GameEngineType.Eaw, 0x23); + public static readonly CommandBarComponentId Production26 = new(GameEngineType.Eaw, 0x24); + public static readonly CommandBarComponentId Production27 = new(GameEngineType.Eaw, 0x25); + public static readonly CommandBarComponentId Production28 = new(GameEngineType.Eaw, 0x26); + public static readonly CommandBarComponentId Production29 = new(GameEngineType.Eaw, 0x27); + public static readonly CommandBarComponentId Production30 = new(GameEngineType.Eaw, 0x28); + public static readonly CommandBarComponentId Production31 = new(GameEngineType.Eaw, 0x29); + public static readonly CommandBarComponentId Production32 = new(GameEngineType.Eaw, 0x2a); + public static readonly CommandBarComponentId Production33 = new(GameEngineType.Eaw, 0x2b); + public static readonly CommandBarComponentId Production34 = new(GameEngineType.Eaw, 0x2c); + public static readonly CommandBarComponentId Production35 = new(GameEngineType.Eaw, 0x2d); + public static readonly CommandBarComponentId Production36 = new(GameEngineType.Eaw, 0x2e); + public static readonly CommandBarComponentId Production37 = new(GameEngineType.Eaw, 0x2f); + public static readonly CommandBarComponentId Production38 = new(GameEngineType.Eaw, 0x30); + public static readonly CommandBarComponentId Production39 = new(GameEngineType.Eaw, 0x31); + public static readonly CommandBarComponentId Production40 = new(GameEngineType.Eaw, 0x32); + public static readonly CommandBarComponentId Production41 = new(GameEngineType.Eaw, 0x33); + public static readonly CommandBarComponentId Production42 = new(GameEngineType.Eaw, 0x34); + public static readonly CommandBarComponentId Production43 = new(GameEngineType.Eaw, 0x35); + public static readonly CommandBarComponentId Production44 = new(GameEngineType.Eaw, 0x36); + public static readonly CommandBarComponentId Production45 = new(GameEngineType.Eaw, 0x37); + public static readonly CommandBarComponentId Production46 = new(GameEngineType.Eaw, 0x38); + public static readonly CommandBarComponentId Production47 = new(GameEngineType.Eaw, 0x39); + public static readonly CommandBarComponentId DroidHelp = new(GameEngineType.Eaw, 0x3a); + public static readonly CommandBarComponentId DroidHelpTactical = new(GameEngineType.Eaw, 0x3b); + public static readonly CommandBarComponentId CurrentDay = new(GameEngineType.Eaw, 0x3c); + public static readonly CommandBarComponentId DayCredits = new(GameEngineType.Eaw, 0x3d); + public static readonly CommandBarComponentId PopulationCap = new(GameEngineType.Eaw, 0x3e); + public static readonly CommandBarComponentId Filter0 = new(GameEngineType.Eaw, 0x3f); + public static readonly CommandBarComponentId Filter1 = new(GameEngineType.Eaw, 0x40); + public static readonly CommandBarComponentId Filter2 = new(GameEngineType.Eaw, 0x41); + public static readonly CommandBarComponentId Filter3 = new(GameEngineType.Eaw, 0x42); + public static readonly CommandBarComponentId StoryArcButton = new(GameEngineType.Eaw, 0x43); + public static readonly CommandBarComponentId PlanetSummaryButton = new(GameEngineType.Eaw, 0x44); + public static readonly CommandBarComponentId SpaceTab = new(GameEngineType.Eaw, 0x45); + public static readonly CommandBarComponentId LandTab = new(GameEngineType.Eaw, 0x46); + public static readonly CommandBarComponentId Dial = new(GameEngineType.Eaw, 0x47); + public static readonly CommandBarComponentId ScrollRight = new(GameEngineType.Eaw, 0x48); + public static readonly CommandBarComponentId ScrollLeft = new(GameEngineType.Eaw, 0x49); + public static readonly CommandBarComponentId ZoomView = new(GameEngineType.Eaw, 0x4a); + public static readonly CommandBarComponentId PrevPlanet = new(GameEngineType.Eaw, 0x4b); + public static readonly CommandBarComponentId NextPlanet = new(GameEngineType.Eaw, 0x4c); + public static readonly CommandBarComponentId RadarGalactic = new(GameEngineType.Eaw, 0x4d); + public static readonly CommandBarComponentId TechLevel = new(GameEngineType.Eaw, 0x4e); + public static readonly CommandBarComponentId BalancePip = new(GameEngineType.Eaw, 0x4f); + public static readonly CommandBarComponentId BuildQueue00 = new(GameEngineType.Eaw, 0x50); + public static readonly CommandBarComponentId BuildQueue01 = new(GameEngineType.Eaw, 0x51); + public static readonly CommandBarComponentId BuildQueue02 = new(GameEngineType.Eaw, 0x52); + public static readonly CommandBarComponentId BuildQueue03 = new(GameEngineType.Eaw, 0x53); + public static readonly CommandBarComponentId BuildQueue04 = new(GameEngineType.Eaw, 0x54); + public static readonly CommandBarComponentId BuildQueue05 = new(GameEngineType.Eaw, 0x55); + public static readonly CommandBarComponentId BuildQueue06 = new(GameEngineType.Eaw, 0x56); + public static readonly CommandBarComponentId BuildQueue07 = new(GameEngineType.Eaw, 0x57); + public static readonly CommandBarComponentId BuildQueue08 = new(GameEngineType.Eaw, 0x58); + public static readonly CommandBarComponentId BuildQueue09 = new(GameEngineType.Eaw, 0x59); + public static readonly CommandBarComponentId OrganizationShell = new(GameEngineType.Eaw, 0x5a); + public static readonly CommandBarComponentId OrganizationCollision = new(GameEngineType.Eaw, 0x5b); + public static readonly CommandBarComponentId SmugglerBox = new(GameEngineType.Eaw, 0x5c); + public static readonly CommandBarComponentId SpaceStationUpgrade01 = new(GameEngineType.Eaw, 0x5d); + public static readonly CommandBarComponentId SpaceStationUpgrade02 = new(GameEngineType.Eaw, 0x5e); + public static readonly CommandBarComponentId HeroAbilitySlot = new(GameEngineType.Eaw, 0x5f); + public static readonly CommandBarComponentId PlanetOrganize0 = new(GameEngineType.Eaw, 0x60); + public static readonly CommandBarComponentId PlanetOrganize1 = new(GameEngineType.Eaw, 0x61); + public static readonly CommandBarComponentId PlanetOrganize2 = new(GameEngineType.Eaw, 0x62); + public static readonly CommandBarComponentId PlanetOrganize3 = new(GameEngineType.Eaw, 0x63); + public static readonly CommandBarComponentId PlanetOrganize4 = new(GameEngineType.Eaw, 0x64); + public static readonly CommandBarComponentId PlanetOrganize5 = new(GameEngineType.Eaw, 0x65); + public static readonly CommandBarComponentId PlanetOrganize6 = new(GameEngineType.Eaw, 0x66); + public static readonly CommandBarComponentId PlanetOrganize7 = new(GameEngineType.Eaw, 0x67); + public static readonly CommandBarComponentId PlanetOrganize8 = new(GameEngineType.Eaw, 0x68); + public static readonly CommandBarComponentId PlanetOrganize9 = new(GameEngineType.Eaw, 0x69); + public static readonly CommandBarComponentId SpecialStructureLand0 = new(GameEngineType.Eaw, 0x6a); + public static readonly CommandBarComponentId SpecialStructureLand1 = new(GameEngineType.Eaw, 0x6b); + public static readonly CommandBarComponentId SpecialStructureLand2 = new(GameEngineType.Eaw, 0x6c); + public static readonly CommandBarComponentId SpecialStructureLand3 = new(GameEngineType.Eaw, 0x6d); + public static readonly CommandBarComponentId SpecialStructureLand4 = new(GameEngineType.Eaw, 0x6e); + public static readonly CommandBarComponentId SpecialStructureLand5 = new(GameEngineType.Eaw, 0x6f); + public static readonly CommandBarComponentId SpecialStructureLand6 = new(GameEngineType.Eaw, 0x70); + public static readonly CommandBarComponentId SpecialStructureLand7 = new(GameEngineType.Eaw, 0x71); + public static readonly CommandBarComponentId SpecialStructureLand8 = new(GameEngineType.Eaw, 0x72); + public static readonly CommandBarComponentId SpecialStructureLandSell = new(GameEngineType.Eaw, 0x73); + public static readonly CommandBarComponentId BigFleet0 = new(GameEngineType.Eaw, 0x74); + public static readonly CommandBarComponentId BigFleet1 = new(GameEngineType.Eaw, 0x75); + public static readonly CommandBarComponentId BigFleet2 = new(GameEngineType.Eaw, 0x76); + public static readonly CommandBarComponentId BigFleet3 = new(GameEngineType.Eaw, 0x77); + public static readonly CommandBarComponentId Fleet0Slot0 = new(GameEngineType.Eaw, 0x78); + public static readonly CommandBarComponentId Fleet0Slot1 = new(GameEngineType.Eaw, 0x79); + public static readonly CommandBarComponentId Fleet0Slot2 = new(GameEngineType.Eaw, 0x7a); + public static readonly CommandBarComponentId Fleet0Slot3 = new(GameEngineType.Eaw, 0x7b); + public static readonly CommandBarComponentId Fleet0Slot4 = new(GameEngineType.Eaw, 0x7c); + public static readonly CommandBarComponentId Fleet0Slot5 = new(GameEngineType.Eaw, 0x7d); + public static readonly CommandBarComponentId Fleet0Slot6 = new(GameEngineType.Eaw, 0x7e); + public static readonly CommandBarComponentId Fleet0Slot7 = new(GameEngineType.Eaw, 0x7f); + public static readonly CommandBarComponentId Fleet0Slot8 = new(GameEngineType.Eaw, 0x80); + public static readonly CommandBarComponentId Fleet0Slot9 = new(GameEngineType.Eaw, 0x81); + public static readonly CommandBarComponentId Fleet0Slot10 = new(GameEngineType.Eaw, 0x82); + public static readonly CommandBarComponentId Fleet0Slot11 = new(GameEngineType.Eaw, 0x83); + public static readonly CommandBarComponentId Fleet0Slot12 = new(GameEngineType.Eaw, 0x84); + public static readonly CommandBarComponentId Fleet0Slot13 = new(GameEngineType.Eaw, 0x85); + public static readonly CommandBarComponentId Fleet0Slot14 = new(GameEngineType.Eaw, 0x86); + public static readonly CommandBarComponentId Fleet0Slot15 = new(GameEngineType.Eaw, 0x87); + public static readonly CommandBarComponentId Fleet0Slot16 = new(GameEngineType.Eaw, 0x88); + public static readonly CommandBarComponentId Fleet0Slot17 = new(GameEngineType.Eaw, 0x89); + public static readonly CommandBarComponentId Fleet0Slot18 = new(GameEngineType.Eaw, 0x8a); + public static readonly CommandBarComponentId Fleet0Slot19 = new(GameEngineType.Eaw, 0x8b); + public static readonly CommandBarComponentId Fleet0Slot20 = new(GameEngineType.Eaw, 0x8c); + public static readonly CommandBarComponentId Fleet0Slot21 = new(GameEngineType.Eaw, 0x8d); + public static readonly CommandBarComponentId Fleet0Slot22 = new(GameEngineType.Eaw, 0x8e); + public static readonly CommandBarComponentId Fleet0Slot23 = new(GameEngineType.Eaw, 0x8f); + public static readonly CommandBarComponentId Fleet0Slot24 = new(GameEngineType.Eaw, 0x90); + public static readonly CommandBarComponentId Fleet0Slot25 = new(GameEngineType.Eaw, 0x91); + public static readonly CommandBarComponentId Fleet0Slot26 = new(GameEngineType.Eaw, 0x92); + public static readonly CommandBarComponentId Fleet0Slot27 = new(GameEngineType.Eaw, 0x93); + public static readonly CommandBarComponentId Fleet0Slot28 = new(GameEngineType.Eaw, 0x94); + public static readonly CommandBarComponentId Fleet0Slot29 = new(GameEngineType.Eaw, 0x95); + public static readonly CommandBarComponentId Fleet0Slot30 = new(GameEngineType.Eaw, 0x96); + public static readonly CommandBarComponentId Fleet0Slot31 = new(GameEngineType.Eaw, 0x97); + public static readonly CommandBarComponentId Fleet0Slot32 = new(GameEngineType.Eaw, 0x98); + public static readonly CommandBarComponentId Fleet0Slot33 = new(GameEngineType.Eaw, 0x99); + public static readonly CommandBarComponentId Fleet0Slot34 = new(GameEngineType.Eaw, 0x9a); + public static readonly CommandBarComponentId Fleet1Slot0 = new(GameEngineType.Eaw, 0x9b); + public static readonly CommandBarComponentId Fleet1Slot1 = new(GameEngineType.Eaw, 0x9c); + public static readonly CommandBarComponentId Fleet1Slot2 = new(GameEngineType.Eaw, 0x9d); + public static readonly CommandBarComponentId Fleet1Slot3 = new(GameEngineType.Eaw, 0x9e); + public static readonly CommandBarComponentId Fleet1Slot4 = new(GameEngineType.Eaw, 0x9f); + public static readonly CommandBarComponentId Fleet1Slot5 = new(GameEngineType.Eaw, 0xa0); + public static readonly CommandBarComponentId Fleet1Slot6 = new(GameEngineType.Eaw, 0xa1); + public static readonly CommandBarComponentId Fleet1Slot7 = new(GameEngineType.Eaw, 0xa2); + public static readonly CommandBarComponentId Fleet1Slot8 = new(GameEngineType.Eaw, 0xa3); + public static readonly CommandBarComponentId Fleet1Slot9 = new(GameEngineType.Eaw, 0xa4); + public static readonly CommandBarComponentId Fleet1Slot10 = new(GameEngineType.Eaw, 0xa5); + public static readonly CommandBarComponentId Fleet1Slot11 = new(GameEngineType.Eaw, 0xa6); + public static readonly CommandBarComponentId Fleet1Slot12 = new(GameEngineType.Eaw, 0xa7); + public static readonly CommandBarComponentId Fleet1Slot13 = new(GameEngineType.Eaw, 0xa8); + public static readonly CommandBarComponentId Fleet1Slot14 = new(GameEngineType.Eaw, 0xa9); + public static readonly CommandBarComponentId Fleet1Slot15 = new(GameEngineType.Eaw, 0xaa); + public static readonly CommandBarComponentId Fleet1Slot16 = new(GameEngineType.Eaw, 0xab); + public static readonly CommandBarComponentId Fleet1Slot17 = new(GameEngineType.Eaw, 0xac); + public static readonly CommandBarComponentId Fleet1Slot18 = new(GameEngineType.Eaw, 0xad); + public static readonly CommandBarComponentId Fleet1Slot19 = new(GameEngineType.Eaw, 0xae); + public static readonly CommandBarComponentId Fleet1Slot20 = new(GameEngineType.Eaw, 0xaf); + public static readonly CommandBarComponentId Fleet1Slot21 = new(GameEngineType.Eaw, 0xb0); + public static readonly CommandBarComponentId Fleet1Slot22 = new(GameEngineType.Eaw, 0xb1); + public static readonly CommandBarComponentId Fleet1Slot23 = new(GameEngineType.Eaw, 0xb2); + public static readonly CommandBarComponentId Fleet1Slot24 = new(GameEngineType.Eaw, 0xb3); + public static readonly CommandBarComponentId Fleet1Slot25 = new(GameEngineType.Eaw, 0xb4); + public static readonly CommandBarComponentId Fleet1Slot26 = new(GameEngineType.Eaw, 0xb5); + public static readonly CommandBarComponentId Fleet1Slot27 = new(GameEngineType.Eaw, 0xb6); + public static readonly CommandBarComponentId Fleet1Slot28 = new(GameEngineType.Eaw, 0xb7); + public static readonly CommandBarComponentId Fleet1Slot29 = new(GameEngineType.Eaw, 0xb8); + public static readonly CommandBarComponentId Fleet1Slot30 = new(GameEngineType.Eaw, 0xb9); + public static readonly CommandBarComponentId Fleet1Slot31 = new(GameEngineType.Eaw, 0xba); + public static readonly CommandBarComponentId Fleet1Slot32 = new(GameEngineType.Eaw, 0xbb); + public static readonly CommandBarComponentId Fleet1Slot33 = new(GameEngineType.Eaw, 0xbc); + public static readonly CommandBarComponentId Fleet1Slot34 = new(GameEngineType.Eaw, 0xbd); + public static readonly CommandBarComponentId Fleet2Slot0 = new(GameEngineType.Eaw, 0xbe); + public static readonly CommandBarComponentId Fleet2Slot1 = new(GameEngineType.Eaw, 0xbf); + public static readonly CommandBarComponentId Fleet2Slot2 = new(GameEngineType.Eaw, 0xc0); + public static readonly CommandBarComponentId Fleet2Slot3 = new(GameEngineType.Eaw, 0xc1); + public static readonly CommandBarComponentId Fleet2Slot4 = new(GameEngineType.Eaw, 0xc2); + public static readonly CommandBarComponentId Fleet2Slot5 = new(GameEngineType.Eaw, 0xc3); + public static readonly CommandBarComponentId Fleet2Slot6 = new(GameEngineType.Eaw, 0xc4); + public static readonly CommandBarComponentId Fleet2Slot7 = new(GameEngineType.Eaw, 0xc5); + public static readonly CommandBarComponentId Fleet2Slot8 = new(GameEngineType.Eaw, 0xc6); + public static readonly CommandBarComponentId Fleet2Slot9 = new(GameEngineType.Eaw, 0xc7); + public static readonly CommandBarComponentId Fleet2Slot10 = new(GameEngineType.Eaw, 0xc8); + public static readonly CommandBarComponentId Fleet2Slot11 = new(GameEngineType.Eaw, 0xc9); + public static readonly CommandBarComponentId Fleet2Slot12 = new(GameEngineType.Eaw, 0xca); + public static readonly CommandBarComponentId Fleet2Slot13 = new(GameEngineType.Eaw, 0xcb); + public static readonly CommandBarComponentId Fleet2Slot14 = new(GameEngineType.Eaw, 0xcc); + public static readonly CommandBarComponentId Fleet2Slot15 = new(GameEngineType.Eaw, 0xcd); + public static readonly CommandBarComponentId Fleet2Slot16 = new(GameEngineType.Eaw, 0xce); + public static readonly CommandBarComponentId Fleet2Slot17 = new(GameEngineType.Eaw, 0xcf); + public static readonly CommandBarComponentId Fleet2Slot18 = new(GameEngineType.Eaw, 0xd0); + public static readonly CommandBarComponentId Fleet2Slot19 = new(GameEngineType.Eaw, 0xd1); + public static readonly CommandBarComponentId Fleet2Slot20 = new(GameEngineType.Eaw, 0xd2); + public static readonly CommandBarComponentId Fleet2Slot21 = new(GameEngineType.Eaw, 0xd3); + public static readonly CommandBarComponentId Fleet2Slot22 = new(GameEngineType.Eaw, 0xd4); + public static readonly CommandBarComponentId Fleet2Slot23 = new(GameEngineType.Eaw, 0xd5); + public static readonly CommandBarComponentId Fleet2Slot24 = new(GameEngineType.Eaw, 0xd6); + public static readonly CommandBarComponentId Fleet2Slot25 = new(GameEngineType.Eaw, 0xd7); + public static readonly CommandBarComponentId Fleet2Slot26 = new(GameEngineType.Eaw, 0xd8); + public static readonly CommandBarComponentId Fleet2Slot27 = new(GameEngineType.Eaw, 0xd9); + public static readonly CommandBarComponentId Fleet2Slot28 = new(GameEngineType.Eaw, 0xda); + public static readonly CommandBarComponentId Fleet2Slot29 = new(GameEngineType.Eaw, 0xdb); + public static readonly CommandBarComponentId Fleet2Slot30 = new(GameEngineType.Eaw, 0xdc); + public static readonly CommandBarComponentId Fleet2Slot31 = new(GameEngineType.Eaw, 0xdd); + public static readonly CommandBarComponentId Fleet2Slot32 = new(GameEngineType.Eaw, 0xde); + public static readonly CommandBarComponentId Fleet2Slot33 = new(GameEngineType.Eaw, 0xdf); + public static readonly CommandBarComponentId Fleet2Slot34 = new(GameEngineType.Eaw, 0xe0); + public static readonly CommandBarComponentId TacticalMain = new(GameEngineType.Eaw, 0xe1); + public static readonly CommandBarComponentId TacticalOptions = new(GameEngineType.Eaw, 0xe2); + public static readonly CommandBarComponentId TacticalCamera = new(GameEngineType.Eaw, 0xe3); + public static readonly CommandBarComponentId TacticalBeacon = new(GameEngineType.Eaw, 0xe4); + public static readonly CommandBarComponentId TacticalWeapon0 = new(GameEngineType.Eaw, 0xe5); + public static readonly CommandBarComponentId TacticalWeapon1 = new(GameEngineType.Eaw, 0xe6); + public static readonly CommandBarComponentId TacticalReinforce = new(GameEngineType.Eaw, 0xe7); + public static readonly CommandBarComponentId TacticalRetreat = new(GameEngineType.Eaw, 0xe8); + public static readonly CommandBarComponentId TacticalStoryArc = new(GameEngineType.Eaw, 0xe9); + public static readonly CommandBarComponentId TacticalTechLevel = new(GameEngineType.Eaw, 0xea); + public static readonly CommandBarComponentId TacticalAttack = new(GameEngineType.Eaw, 0xeb); + public static readonly CommandBarComponentId TacticalAttackMove = new(GameEngineType.Eaw, 0xec); + public static readonly CommandBarComponentId TacticalMove = new(GameEngineType.Eaw, 0xed); + public static readonly CommandBarComponentId TacticalWaypoint = new(GameEngineType.Eaw, 0xee); + public static readonly CommandBarComponentId TacticalStop = new(GameEngineType.Eaw, 0xef); + public static readonly CommandBarComponentId TacticalGuard = new(GameEngineType.Eaw, 0xf0); + public static readonly CommandBarComponentId SpaceTacticalRadar = new(GameEngineType.Eaw, 0xf1); + public static readonly CommandBarComponentId SpaceTacticalCredits = new(GameEngineType.Eaw, 0xf2); + public static readonly CommandBarComponentId SpaceTacticalGrabBar = new(GameEngineType.Eaw, 0xf3); + public static readonly CommandBarComponentId SpaceTacticalHealthBar = new(GameEngineType.Eaw, 0xf4); + public static readonly CommandBarComponentId SpaceTacticalBracketSmall = new(GameEngineType.Eaw, 0xf5); + public static readonly CommandBarComponentId SpaceTacticalBracketMedium = new(GameEngineType.Eaw, 0xf6); + public static readonly CommandBarComponentId SpaceTacticalBracketLarge = new(GameEngineType.Eaw, 0xf7); + public static readonly CommandBarComponentId SpaceTacticalHeroIcon = new(GameEngineType.Eaw, 0xf8); + public static readonly CommandBarComponentId SpaceTacticalHeroHealth = new(GameEngineType.Eaw, 0xf9); + public static readonly CommandBarComponentId SpaceTacticalHealth = new(GameEngineType.Eaw, 0xfa); + public static readonly CommandBarComponentId SpaceTacticalHealthMedium = new(GameEngineType.Eaw, 0xfb); + public static readonly CommandBarComponentId SpaceTacticalHealthLarge = new(GameEngineType.Eaw, 0xfc); + public static readonly CommandBarComponentId SpaceTacticalShields = new(GameEngineType.Eaw, 0xfd); + public static readonly CommandBarComponentId SpaceTacticalShieldsMedium = new(GameEngineType.Eaw, 0xfe); + public static readonly CommandBarComponentId SpaceTacticalShieldsLarge = new(GameEngineType.Eaw, 0xff); + public static readonly CommandBarComponentId SpaceTacticalPower = new(GameEngineType.Eaw, 0x100); + public static readonly CommandBarComponentId SpaceTacticalControlGroup = new(GameEngineType.Eaw, 0x101); + public static readonly CommandBarComponentId SpaceTacticalAbilityIcon = new(GameEngineType.Eaw, 0x102); + public static readonly CommandBarComponentId SpaceTacticalGarrisonIcon = new(GameEngineType.Eaw, 0x103); + public static readonly CommandBarComponentId LandTacticalWeatherIcon = new(GameEngineType.Eaw, 0x104); + public static readonly CommandBarComponentId DsFireShell = new(GameEngineType.Eaw, 0x105); + public static readonly CommandBarComponentId DsFire = new(GameEngineType.Eaw, 0x106); + public static readonly CommandBarComponentId DsCountdownTimer = new(GameEngineType.Eaw, 0x107); + public static readonly CommandBarComponentId TacticalSelect00 = new(GameEngineType.Eaw, 0x108); + public static readonly CommandBarComponentId TacticalSelect01 = new(GameEngineType.Eaw, 0x109); + public static readonly CommandBarComponentId TacticalSelect02 = new(GameEngineType.Eaw, 0x10a); + public static readonly CommandBarComponentId TacticalSelect03 = new(GameEngineType.Eaw, 0x10b); + public static readonly CommandBarComponentId TacticalSelect04 = new(GameEngineType.Eaw, 0x10c); + public static readonly CommandBarComponentId TacticalSelect05 = new(GameEngineType.Eaw, 0x10d); + public static readonly CommandBarComponentId TacticalSelect06 = new(GameEngineType.Eaw, 0x10e); + public static readonly CommandBarComponentId TacticalSelect07 = new(GameEngineType.Eaw, 0x10f); + public static readonly CommandBarComponentId TacticalSelect08 = new(GameEngineType.Eaw, 0x110); + public static readonly CommandBarComponentId TacticalSelect09 = new(GameEngineType.Eaw, 0x111); + public static readonly CommandBarComponentId TacticalSelect10 = new(GameEngineType.Eaw, 0x112); + public static readonly CommandBarComponentId TacticalSelect11 = new(GameEngineType.Eaw, 0x113); + public static readonly CommandBarComponentId TacticalSelect12 = new(GameEngineType.Eaw, 0x114); + public static readonly CommandBarComponentId TacticalSelect13 = new(GameEngineType.Eaw, 0x115); + public static readonly CommandBarComponentId TacticalSelect14 = new(GameEngineType.Eaw, 0x116); + public static readonly CommandBarComponentId TacticalSelect15 = new(GameEngineType.Eaw, 0x117); + public static readonly CommandBarComponentId TacticalSelect16 = new(GameEngineType.Eaw, 0x118); + public static readonly CommandBarComponentId TacticalSelect17 = new(GameEngineType.Eaw, 0x119); + public static readonly CommandBarComponentId TacticalSelect18 = new(GameEngineType.Eaw, 0x11a); + public static readonly CommandBarComponentId TacticalSelect19 = new(GameEngineType.Eaw, 0x11b); + public static readonly CommandBarComponentId TacticalSelect20 = new(GameEngineType.Eaw, 0x11c); + public static readonly CommandBarComponentId TacticalSelect21 = new(GameEngineType.Eaw, 0x11d); + public static readonly CommandBarComponentId TacticalSelect22 = new(GameEngineType.Eaw, 0x11e); + public static readonly CommandBarComponentId TacticalSelect23 = new(GameEngineType.Eaw, 0x11f); + public static readonly CommandBarComponentId TacticalSelect24 = new(GameEngineType.Eaw, 0x120); + public static readonly CommandBarComponentId TacticalSelect25 = new(GameEngineType.Eaw, 0x121); + public static readonly CommandBarComponentId TacticalSelect26 = new(GameEngineType.Eaw, 0x122); + public static readonly CommandBarComponentId TacticalSelect27 = new(GameEngineType.Eaw, 0x123); + public static readonly CommandBarComponentId TacticalSelect28 = new(GameEngineType.Eaw, 0x124); + public static readonly CommandBarComponentId TacticalSelect29 = new(GameEngineType.Eaw, 0x125); + public static readonly CommandBarComponentId TacticalSelect30 = new(GameEngineType.Eaw, 0x126); + public static readonly CommandBarComponentId TacticalSelect31 = new(GameEngineType.Eaw, 0x127); + public static readonly CommandBarComponentId TacticalSelect32 = new(GameEngineType.Eaw, 0x128); + public static readonly CommandBarComponentId TacticalSelect33 = new(GameEngineType.Eaw, 0x129); + public static readonly CommandBarComponentId TacticalSelect34 = new(GameEngineType.Eaw, 0x12a); + public static readonly CommandBarComponentId TacticalSelect35 = new(GameEngineType.Eaw, 0x12b); + public static readonly CommandBarComponentId TacticalSelect36 = new(GameEngineType.Eaw, 0x12c); + public static readonly CommandBarComponentId TacticalSelect37 = new(GameEngineType.Eaw, 0x12d); + public static readonly CommandBarComponentId TacticalSelect38 = new(GameEngineType.Eaw, 0x12e); + public static readonly CommandBarComponentId TacticalSelect39 = new(GameEngineType.Eaw, 0x12f); + public static readonly CommandBarComponentId TacticalSelect40 = new(GameEngineType.Eaw, 0x130); + public static readonly CommandBarComponentId TacticalSelect41 = new(GameEngineType.Eaw, 0x131); + public static readonly CommandBarComponentId TacticalSelect42 = new(GameEngineType.Eaw, 0x132); + public static readonly CommandBarComponentId TacticalSelect43 = new(GameEngineType.Eaw, 0x133); + public static readonly CommandBarComponentId TacticalSelect44 = new(GameEngineType.Eaw, 0x134); + public static readonly CommandBarComponentId TacticalSelect45 = new(GameEngineType.Eaw, 0x135); + public static readonly CommandBarComponentId TacticalSelect46 = new(GameEngineType.Eaw, 0x136); + public static readonly CommandBarComponentId TacticalSelect47 = new(GameEngineType.Eaw, 0x137); + public static readonly CommandBarComponentId TacticalSelectHealth00 = new(GameEngineType.Eaw, 0x138); + public static readonly CommandBarComponentId TacticalSelectHealth01 = new(GameEngineType.Eaw, 0x139); + public static readonly CommandBarComponentId TacticalSelectHealth02 = new(GameEngineType.Eaw, 0x13a); + public static readonly CommandBarComponentId TacticalSelectHealth03 = new(GameEngineType.Eaw, 0x13b); + public static readonly CommandBarComponentId TacticalSelectHealth04 = new(GameEngineType.Eaw, 0x13c); + public static readonly CommandBarComponentId TacticalSelectHealth05 = new(GameEngineType.Eaw, 0x13d); + public static readonly CommandBarComponentId TacticalSelectHealth06 = new(GameEngineType.Eaw, 0x13e); + public static readonly CommandBarComponentId TacticalSelectHealth07 = new(GameEngineType.Eaw, 0x13f); + public static readonly CommandBarComponentId TacticalSelectHealth08 = new(GameEngineType.Eaw, 0x140); + public static readonly CommandBarComponentId TacticalSelectHealth09 = new(GameEngineType.Eaw, 0x141); + public static readonly CommandBarComponentId TacticalSelectHealth10 = new(GameEngineType.Eaw, 0x142); + public static readonly CommandBarComponentId TacticalSelectHealth11 = new(GameEngineType.Eaw, 0x143); + public static readonly CommandBarComponentId TacticalSelectHealth12 = new(GameEngineType.Eaw, 0x144); + public static readonly CommandBarComponentId TacticalSelectHealth13 = new(GameEngineType.Eaw, 0x145); + public static readonly CommandBarComponentId TacticalSelectHealth14 = new(GameEngineType.Eaw, 0x146); + public static readonly CommandBarComponentId TacticalSelectHealth15 = new(GameEngineType.Eaw, 0x147); + public static readonly CommandBarComponentId TacticalSelectHealth16 = new(GameEngineType.Eaw, 0x148); + public static readonly CommandBarComponentId TacticalSelectHealth17 = new(GameEngineType.Eaw, 0x149); + public static readonly CommandBarComponentId TacticalSelectHealth18 = new(GameEngineType.Eaw, 0x14a); + public static readonly CommandBarComponentId TacticalSelectHealth19 = new(GameEngineType.Eaw, 0x14b); + public static readonly CommandBarComponentId TacticalSelectHealth20 = new(GameEngineType.Eaw, 0x14c); + public static readonly CommandBarComponentId TacticalSelectHealth21 = new(GameEngineType.Eaw, 0x14d); + public static readonly CommandBarComponentId TacticalSelectHealth22 = new(GameEngineType.Eaw, 0x14e); + public static readonly CommandBarComponentId TacticalSelectHealth23 = new(GameEngineType.Eaw, 0x14f); + public static readonly CommandBarComponentId TacticalSelectHealth24 = new(GameEngineType.Eaw, 0x150); + public static readonly CommandBarComponentId TacticalSelectHealth25 = new(GameEngineType.Eaw, 0x151); + public static readonly CommandBarComponentId TacticalSelectHealth26 = new(GameEngineType.Eaw, 0x152); + public static readonly CommandBarComponentId TacticalSelectHealth27 = new(GameEngineType.Eaw, 0x153); + public static readonly CommandBarComponentId TacticalSelectHealth28 = new(GameEngineType.Eaw, 0x154); + public static readonly CommandBarComponentId TacticalSelectHealth29 = new(GameEngineType.Eaw, 0x155); + public static readonly CommandBarComponentId TacticalSelectHealth30 = new(GameEngineType.Eaw, 0x156); + public static readonly CommandBarComponentId TacticalSelectHealth31 = new(GameEngineType.Eaw, 0x157); + public static readonly CommandBarComponentId TacticalSelectHealth32 = new(GameEngineType.Eaw, 0x158); + public static readonly CommandBarComponentId TacticalSelectHealth33 = new(GameEngineType.Eaw, 0x159); + public static readonly CommandBarComponentId TacticalSelectHealth34 = new(GameEngineType.Eaw, 0x15a); + public static readonly CommandBarComponentId TacticalSelectHealth35 = new(GameEngineType.Eaw, 0x15b); + public static readonly CommandBarComponentId TacticalSelectHealth36 = new(GameEngineType.Eaw, 0x15c); + public static readonly CommandBarComponentId TacticalSelectHealth37 = new(GameEngineType.Eaw, 0x15d); + public static readonly CommandBarComponentId TacticalSelectHealth38 = new(GameEngineType.Eaw, 0x15e); + public static readonly CommandBarComponentId TacticalSelectHealth39 = new(GameEngineType.Eaw, 0x15f); + public static readonly CommandBarComponentId TacticalSelectHealth40 = new(GameEngineType.Eaw, 0x160); + public static readonly CommandBarComponentId TacticalSelectHealth41 = new(GameEngineType.Eaw, 0x161); + public static readonly CommandBarComponentId TacticalSelectHealth42 = new(GameEngineType.Eaw, 0x162); + public static readonly CommandBarComponentId TacticalSelectHealth43 = new(GameEngineType.Eaw, 0x163); + public static readonly CommandBarComponentId TacticalSelectHealth44 = new(GameEngineType.Eaw, 0x164); + public static readonly CommandBarComponentId TacticalSelectHealth45 = new(GameEngineType.Eaw, 0x165); + public static readonly CommandBarComponentId TacticalSelectHealth46 = new(GameEngineType.Eaw, 0x166); + public static readonly CommandBarComponentId TacticalSelectHealth47 = new(GameEngineType.Eaw, 0x167); + public static readonly CommandBarComponentId TacticalSelectShield00 = new(GameEngineType.Eaw, 0x168); + public static readonly CommandBarComponentId TacticalSelectShield01 = new(GameEngineType.Eaw, 0x169); + public static readonly CommandBarComponentId TacticalSelectShield02 = new(GameEngineType.Eaw, 0x16a); + public static readonly CommandBarComponentId TacticalSelectShield03 = new(GameEngineType.Eaw, 0x16b); + public static readonly CommandBarComponentId TacticalSelectShield04 = new(GameEngineType.Eaw, 0x16c); + public static readonly CommandBarComponentId TacticalSelectShield05 = new(GameEngineType.Eaw, 0x16d); + public static readonly CommandBarComponentId TacticalSelectShield06 = new(GameEngineType.Eaw, 0x16e); + public static readonly CommandBarComponentId TacticalSelectShield07 = new(GameEngineType.Eaw, 0x16f); + public static readonly CommandBarComponentId TacticalSelectShield08 = new(GameEngineType.Eaw, 0x170); + public static readonly CommandBarComponentId TacticalSelectShield09 = new(GameEngineType.Eaw, 0x171); + public static readonly CommandBarComponentId TacticalSelectShield10 = new(GameEngineType.Eaw, 0x172); + public static readonly CommandBarComponentId TacticalSelectShield11 = new(GameEngineType.Eaw, 0x173); + public static readonly CommandBarComponentId TacticalSelectShield12 = new(GameEngineType.Eaw, 0x174); + public static readonly CommandBarComponentId TacticalSelectShield13 = new(GameEngineType.Eaw, 0x175); + public static readonly CommandBarComponentId TacticalSelectShield14 = new(GameEngineType.Eaw, 0x176); + public static readonly CommandBarComponentId TacticalSelectShield15 = new(GameEngineType.Eaw, 0x177); + public static readonly CommandBarComponentId TacticalSelectShield16 = new(GameEngineType.Eaw, 0x178); + public static readonly CommandBarComponentId TacticalSelectShield17 = new(GameEngineType.Eaw, 0x179); + public static readonly CommandBarComponentId TacticalSelectShield18 = new(GameEngineType.Eaw, 0x17a); + public static readonly CommandBarComponentId TacticalSelectShield19 = new(GameEngineType.Eaw, 0x17b); + public static readonly CommandBarComponentId TacticalSelectShield20 = new(GameEngineType.Eaw, 0x17c); + public static readonly CommandBarComponentId TacticalSelectShield21 = new(GameEngineType.Eaw, 0x17d); + public static readonly CommandBarComponentId TacticalSelectShield22 = new(GameEngineType.Eaw, 0x17e); + public static readonly CommandBarComponentId TacticalSelectShield23 = new(GameEngineType.Eaw, 0x17f); + public static readonly CommandBarComponentId TacticalSelectShield24 = new(GameEngineType.Eaw, 0x180); + public static readonly CommandBarComponentId TacticalSelectShield25 = new(GameEngineType.Eaw, 0x181); + public static readonly CommandBarComponentId TacticalSelectShield26 = new(GameEngineType.Eaw, 0x182); + public static readonly CommandBarComponentId TacticalSelectShield27 = new(GameEngineType.Eaw, 0x183); + public static readonly CommandBarComponentId TacticalSelectShield28 = new(GameEngineType.Eaw, 0x184); + public static readonly CommandBarComponentId TacticalSelectShield29 = new(GameEngineType.Eaw, 0x185); + public static readonly CommandBarComponentId TacticalSelectShield30 = new(GameEngineType.Eaw, 0x186); + public static readonly CommandBarComponentId TacticalSelectShield31 = new(GameEngineType.Eaw, 0x187); + public static readonly CommandBarComponentId TacticalSelectShield32 = new(GameEngineType.Eaw, 0x188); + public static readonly CommandBarComponentId TacticalSelectShield33 = new(GameEngineType.Eaw, 0x189); + public static readonly CommandBarComponentId TacticalSelectShield34 = new(GameEngineType.Eaw, 0x18a); + public static readonly CommandBarComponentId TacticalSelectShield35 = new(GameEngineType.Eaw, 0x18b); + public static readonly CommandBarComponentId TacticalSelectShield36 = new(GameEngineType.Eaw, 0x18c); + public static readonly CommandBarComponentId TacticalSelectShield37 = new(GameEngineType.Eaw, 0x18d); + public static readonly CommandBarComponentId TacticalSelectShield38 = new(GameEngineType.Eaw, 0x18e); + public static readonly CommandBarComponentId TacticalSelectShield39 = new(GameEngineType.Eaw, 0x18f); + public static readonly CommandBarComponentId TacticalSelectShield40 = new(GameEngineType.Eaw, 0x190); + public static readonly CommandBarComponentId TacticalSelectShield41 = new(GameEngineType.Eaw, 0x191); + public static readonly CommandBarComponentId TacticalSelectShield42 = new(GameEngineType.Eaw, 0x192); + public static readonly CommandBarComponentId TacticalSelectShield43 = new(GameEngineType.Eaw, 0x193); + public static readonly CommandBarComponentId TacticalSelectShield44 = new(GameEngineType.Eaw, 0x194); + public static readonly CommandBarComponentId TacticalSelectShield45 = new(GameEngineType.Eaw, 0x195); + public static readonly CommandBarComponentId TacticalSelectShield46 = new(GameEngineType.Eaw, 0x196); + public static readonly CommandBarComponentId TacticalSelectShield47 = new(GameEngineType.Eaw, 0x197); + public static readonly CommandBarComponentId TacticalBorder00 = new(GameEngineType.Eaw, 0x198); + public static readonly CommandBarComponentId TacticalBorder01 = new(GameEngineType.Eaw, 0x199); + public static readonly CommandBarComponentId TacticalBorder02 = new(GameEngineType.Eaw, 0x19a); + public static readonly CommandBarComponentId TacticalBorder03 = new(GameEngineType.Eaw, 0x19b); + public static readonly CommandBarComponentId TacticalBorder04 = new(GameEngineType.Eaw, 0x19c); + public static readonly CommandBarComponentId TacticalBorder05 = new(GameEngineType.Eaw, 0x19d); + public static readonly CommandBarComponentId TacticalBorder06 = new(GameEngineType.Eaw, 0x19e); + public static readonly CommandBarComponentId TacticalBorder07 = new(GameEngineType.Eaw, 0x19f); + public static readonly CommandBarComponentId TacticalBorder08 = new(GameEngineType.Eaw, 0x1a0); + public static readonly CommandBarComponentId TacticalBorder09 = new(GameEngineType.Eaw, 0x1a1); + public static readonly CommandBarComponentId TacticalBorder10 = new(GameEngineType.Eaw, 0x1a2); + public static readonly CommandBarComponentId TacticalSelectButton00 = new(GameEngineType.Eaw, 0x1a3); + public static readonly CommandBarComponentId TacticalSelectButton01 = new(GameEngineType.Eaw, 0x1a4); + public static readonly CommandBarComponentId TacticalSelectButton02 = new(GameEngineType.Eaw, 0x1a5); + public static readonly CommandBarComponentId TacticalSelectButton03 = new(GameEngineType.Eaw, 0x1a6); + public static readonly CommandBarComponentId TacticalSelectButton04 = new(GameEngineType.Eaw, 0x1a7); + public static readonly CommandBarComponentId TacticalSelectButton05 = new(GameEngineType.Eaw, 0x1a8); + public static readonly CommandBarComponentId TacticalSelectButton06 = new(GameEngineType.Eaw, 0x1a9); + public static readonly CommandBarComponentId TacticalSelectButton07 = new(GameEngineType.Eaw, 0x1aa); + public static readonly CommandBarComponentId TacticalSelectButton08 = new(GameEngineType.Eaw, 0x1ab); + public static readonly CommandBarComponentId TacticalSelectButton09 = new(GameEngineType.Eaw, 0x1ac); + public static readonly CommandBarComponentId TacticalSelectButton10 = new(GameEngineType.Eaw, 0x1ad); + public static readonly CommandBarComponentId TacticalSelectButton11 = new(GameEngineType.Eaw, 0x1ae); + public static readonly CommandBarComponentId TacticalSelectButton12 = new(GameEngineType.Eaw, 0x1af); + public static readonly CommandBarComponentId TacticalSelectButton13 = new(GameEngineType.Eaw, 0x1b0); + public static readonly CommandBarComponentId TacticalSelectButton14 = new(GameEngineType.Eaw, 0x1b1); + public static readonly CommandBarComponentId TacticalSelectButton15 = new(GameEngineType.Eaw, 0x1b2); + public static readonly CommandBarComponentId TacticalSelectButton16 = new(GameEngineType.Eaw, 0x1b3); + public static readonly CommandBarComponentId TacticalSelectButton17 = new(GameEngineType.Eaw, 0x1b4); + public static readonly CommandBarComponentId TacticalSelectButton18 = new(GameEngineType.Eaw, 0x1b5); + public static readonly CommandBarComponentId TacticalSelectButton19 = new(GameEngineType.Eaw, 0x1b6); + public static readonly CommandBarComponentId TacticalSelectButton20 = new(GameEngineType.Eaw, 0x1b7); + public static readonly CommandBarComponentId TacticalSelectButton21 = new(GameEngineType.Eaw, 0x1b8); + public static readonly CommandBarComponentId TacticalSelectButton22 = new(GameEngineType.Eaw, 0x1b9); + public static readonly CommandBarComponentId TacticalSelectButton23 = new(GameEngineType.Eaw, 0x1ba); + public static readonly CommandBarComponentId TacticalSelectButton24 = new(GameEngineType.Eaw, 0x1bb); + public static readonly CommandBarComponentId TacticalSelectButton25 = new(GameEngineType.Eaw, 0x1bc); + public static readonly CommandBarComponentId TacticalSelectButton26 = new(GameEngineType.Eaw, 0x1bd); + public static readonly CommandBarComponentId TacticalSelectButton27 = new(GameEngineType.Eaw, 0x1be); + public static readonly CommandBarComponentId TacticalSelectButton28 = new(GameEngineType.Eaw, 0x1bf); + public static readonly CommandBarComponentId TacticalSelectButton29 = new(GameEngineType.Eaw, 0x1c0); + public static readonly CommandBarComponentId TacticalSelectButton30 = new(GameEngineType.Eaw, 0x1c1); + public static readonly CommandBarComponentId TacticalSelectButton31 = new(GameEngineType.Eaw, 0x1c2); + public static readonly CommandBarComponentId TacticalSelectButton32 = new(GameEngineType.Eaw, 0x1c3); + public static readonly CommandBarComponentId TacticalSelectButton33 = new(GameEngineType.Eaw, 0x1c4); + public static readonly CommandBarComponentId TacticalSelectButton34 = new(GameEngineType.Eaw, 0x1c5); + public static readonly CommandBarComponentId TacticalSelectButton35 = new(GameEngineType.Eaw, 0x1c6); + public static readonly CommandBarComponentId TacticalSelectButton36 = new(GameEngineType.Eaw, 0x1c7); + public static readonly CommandBarComponentId TacticalSelectButton37 = new(GameEngineType.Eaw, 0x1c8); + public static readonly CommandBarComponentId TacticalSelectButton38 = new(GameEngineType.Eaw, 0x1c9); + public static readonly CommandBarComponentId TacticalSelectButton39 = new(GameEngineType.Eaw, 0x1ca); + public static readonly CommandBarComponentId TacticalSelectButton40 = new(GameEngineType.Eaw, 0x1cb); + public static readonly CommandBarComponentId TacticalSelectButton41 = new(GameEngineType.Eaw, 0x1cc); + public static readonly CommandBarComponentId TacticalSelectButton42 = new(GameEngineType.Eaw, 0x1cd); + public static readonly CommandBarComponentId TacticalSelectButton43 = new(GameEngineType.Eaw, 0x1ce); + public static readonly CommandBarComponentId TacticalSelectButton44 = new(GameEngineType.Eaw, 0x1cf); + public static readonly CommandBarComponentId TacticalSelectButton45 = new(GameEngineType.Eaw, 0x1d0); + public static readonly CommandBarComponentId TacticalSelectButton46 = new(GameEngineType.Eaw, 0x1d1); + public static readonly CommandBarComponentId TacticalSelectButton47 = new(GameEngineType.Eaw, 0x1d2); + public static readonly CommandBarComponentId TacticalBuildQueue00 = new(GameEngineType.Eaw, 0x1d3); + public static readonly CommandBarComponentId TacticalBuildQueue01 = new(GameEngineType.Eaw, 0x1d4); + public static readonly CommandBarComponentId TacticalBuildQueue02 = new(GameEngineType.Eaw, 0x1d5); + public static readonly CommandBarComponentId TacticalBuildQueue03 = new(GameEngineType.Eaw, 0x1d6); + public static readonly CommandBarComponentId TacticalBuildQueue04 = new(GameEngineType.Eaw, 0x1d7); + public static readonly CommandBarComponentId TacticalBuildQueue05 = new(GameEngineType.Eaw, 0x1d8); + public static readonly CommandBarComponentId TacticalBuildQueue06 = new(GameEngineType.Eaw, 0x1d9); + public static readonly CommandBarComponentId TacticalBuildQueue07 = new(GameEngineType.Eaw, 0x1da); + public static readonly CommandBarComponentId TacticalBuildQueue08 = new(GameEngineType.Eaw, 0x1db); + public static readonly CommandBarComponentId TacticalBuildQueue09 = new(GameEngineType.Eaw, 0x1dc); + public static readonly CommandBarComponentId TooltipBack = new(GameEngineType.Eaw, 0x1dd); + public static readonly CommandBarComponentId TooltipName = new(GameEngineType.Eaw, 0x1de); + public static readonly CommandBarComponentId TooltipPrice = new(GameEngineType.Eaw, 0x1df); + public static readonly CommandBarComponentId TooltipIcon = new(GameEngineType.Eaw, 0x1e0); + public static readonly CommandBarComponentId TooltipIconLand = new(GameEngineType.Eaw, 0x1e1); + public static readonly CommandBarComponentId TooltipLeftJustified = new(GameEngineType.Eaw, 0x1e2); + public static readonly CommandBarComponentId EncyclopediaBack = new(GameEngineType.Eaw, 0x1e3); + public static readonly CommandBarComponentId EncyclopediaHeaderText = new(GameEngineType.Eaw, 0x1e4); + public static readonly CommandBarComponentId EncyclopediaText = new(GameEngineType.Eaw, 0x1e5); + public static readonly CommandBarComponentId EncyclopediaRightText = new(GameEngineType.Eaw, 0x1e6); + public static readonly CommandBarComponentId EncyclopediaCenterText = new(GameEngineType.Eaw, 0x1e7); + public static readonly CommandBarComponentId EncyclopediaIcon = new(GameEngineType.Eaw, 0x1e8); + public static readonly CommandBarComponentId EncyclopediaCostText = new(GameEngineType.Eaw, 0x1e9); + public static readonly CommandBarComponentId ZoomedBack = new(GameEngineType.Eaw, 0x1ea); + public static readonly CommandBarComponentId ZoomedHeaderText = new(GameEngineType.Eaw, 0x1eb); + public static readonly CommandBarComponentId ZoomedText = new(GameEngineType.Eaw, 0x1ec); + public static readonly CommandBarComponentId ZoomedRightText = new(GameEngineType.Eaw, 0x1ed); + public static readonly CommandBarComponentId ZoomedCenterText = new(GameEngineType.Eaw, 0x1ee); + public static readonly CommandBarComponentId ZoomedCostText = new(GameEngineType.Eaw, 0x1ef); + public static readonly CommandBarComponentId GPlanetFleet = new(GameEngineType.Eaw, 0x1f0); + public static readonly CommandBarComponentId GPlanetName = new(GameEngineType.Eaw, 0x1f1); + public static readonly CommandBarComponentId GPlanetValue = new(GameEngineType.Eaw, 0x1f2); + public static readonly CommandBarComponentId GPoliticalControl = new(GameEngineType.Eaw, 0x1f3); + public static readonly CommandBarComponentId GSpaceLevel = new(GameEngineType.Eaw, 0x1f4); + public static readonly CommandBarComponentId GSpaceIcon = new(GameEngineType.Eaw, 0x1f5); + public static readonly CommandBarComponentId GSpaceLevelPips = new(GameEngineType.Eaw, 0x1f6); + public static readonly CommandBarComponentId GGroundLevel = new(GameEngineType.Eaw, 0x1f7); + public static readonly CommandBarComponentId GGroundIcon = new(GameEngineType.Eaw, 0x1f8); + public static readonly CommandBarComponentId GGroundLevelPips = new(GameEngineType.Eaw, 0x1f9); + public static readonly CommandBarComponentId GConflict = new(GameEngineType.Eaw, 0x1fa); + public static readonly CommandBarComponentId GHero = new(GameEngineType.Eaw, 0x1fb); + public static readonly CommandBarComponentId GEnemyHero = new(GameEngineType.Eaw, 0x1fc); + public static readonly CommandBarComponentId GBuild = new(GameEngineType.Eaw, 0x1fd); + public static readonly CommandBarComponentId GSmuggler = new(GameEngineType.Eaw, 0x2fe); + public static readonly CommandBarComponentId GBountyHunter = new(GameEngineType.Eaw, 0x2ff); + public static readonly CommandBarComponentId GPlanetLandForces = new(GameEngineType.Eaw, 0x200); + public static readonly CommandBarComponentId GGalacticRadarBlip = new(GameEngineType.Eaw, 0x201); + public static readonly CommandBarComponentId GGalacticRadarView = new(GameEngineType.Eaw, 0x202); + public static readonly CommandBarComponentId GSmuggled = new(GameEngineType.Eaw, 0x203); + public static readonly CommandBarComponentId GSpecialAbility = new(GameEngineType.Eaw, 0x204); + public static readonly CommandBarComponentId GHeroIcon = new(GameEngineType.Eaw, 0x205); + public static readonly CommandBarComponentId GPlanetRing = new(GameEngineType.Eaw, 0x206); + public static readonly CommandBarComponentId GWeather = new(GameEngineType.Eaw, 0x207); + public static readonly CommandBarComponentId GPlanetAbility = new(GameEngineType.Eaw, 0x208); + public static readonly CommandBarComponentId TutorialText = new(GameEngineType.Eaw, 0x209); + public static readonly CommandBarComponentId TutorialTextBack = new(GameEngineType.Eaw, 0x20a); + public static readonly CommandBarComponentId RadarBlip = new(GameEngineType.Eaw, 0x20b); + public static readonly CommandBarComponentId TacticalBuildButtonShell = new(GameEngineType.Eaw, 0x20c); + public static readonly CommandBarComponentId TacticalBuildButton0 = new(GameEngineType.Eaw, 0x20d); + public static readonly CommandBarComponentId TacticalBuildButton1 = new(GameEngineType.Eaw, 0x20e); + public static readonly CommandBarComponentId TacticalBuildButton2 = new(GameEngineType.Eaw, 0x20f); + public static readonly CommandBarComponentId TacticalBuildButton3 = new(GameEngineType.Eaw, 0x210); + public static readonly CommandBarComponentId TacticalBuildButton4 = new(GameEngineType.Eaw, 0x211); + public static readonly CommandBarComponentId TacticalBuildButton5 = new(GameEngineType.Eaw, 0x212); + public static readonly CommandBarComponentId TacticalSellButton = new(GameEngineType.Eaw, 0x213); + public static readonly CommandBarComponentId ReinforcementShell = new(GameEngineType.Eaw, 0x214); + public static readonly CommandBarComponentId ReinforcementCancel = new(GameEngineType.Eaw, 0x215); + public static readonly CommandBarComponentId ReinforcementSlot00 = new(GameEngineType.Eaw, 0x216); + public static readonly CommandBarComponentId ReinforcementSlot01 = new(GameEngineType.Eaw, 0x217); + public static readonly CommandBarComponentId ReinforcementSlot02 = new(GameEngineType.Eaw, 0x218); + public static readonly CommandBarComponentId ReinforcementSlot03 = new(GameEngineType.Eaw, 0x219); + public static readonly CommandBarComponentId ReinforcementSlot04 = new(GameEngineType.Eaw, 0x21a); + public static readonly CommandBarComponentId ReinforcementSlot05 = new(GameEngineType.Eaw, 0x21b); + public static readonly CommandBarComponentId ReinforcementSlot06 = new(GameEngineType.Eaw, 0x21c); + public static readonly CommandBarComponentId ReinforcementSlot07 = new(GameEngineType.Eaw, 0x21d); + public static readonly CommandBarComponentId ReinforcementSlot08 = new(GameEngineType.Eaw, 0x21e); + public static readonly CommandBarComponentId ReinforcementSlot09 = new(GameEngineType.Eaw, 0x21f); + public static readonly CommandBarComponentId ReinforcementSlot10 = new(GameEngineType.Eaw, 0x220); + public static readonly CommandBarComponentId ReinforcementSlot11 = new(GameEngineType.Eaw, 0x221); + public static readonly CommandBarComponentId ReinforcementSlot12 = new(GameEngineType.Eaw, 0x222); + public static readonly CommandBarComponentId ReinforcementSlot13 = new(GameEngineType.Eaw, 0x223); + public static readonly CommandBarComponentId ReinforcementSlot14 = new(GameEngineType.Eaw, 0x224); + public static readonly CommandBarComponentId ReinforcementCap2 = new(GameEngineType.Eaw, 0x225); + public static readonly CommandBarComponentId ReinforcementCap2Text = new(GameEngineType.Eaw, 0x226); + public static readonly CommandBarComponentId ReinforcementCounter = new(GameEngineType.Eaw, 0x227); + public static readonly CommandBarComponentId GarrisonRespawnCounter = new(GameEngineType.Eaw, 0x228); + public static readonly CommandBarComponentId SkirmishUpgrade = new(GameEngineType.Eaw, 0x229); + public static readonly CommandBarComponentId PendingBattleShell = new(GameEngineType.Eaw, 0x22a); + public static readonly CommandBarComponentId PendingBattleText = new(GameEngineType.Eaw, 0x22b); + public static readonly CommandBarComponentId PendingBattleButton = new(GameEngineType.Eaw, 0x22c); + public static readonly CommandBarComponentId PendingBattleAutoresolve = new(GameEngineType.Eaw, 0x22d); + public static readonly CommandBarComponentId PendingBattleGraphLeft = new(GameEngineType.Eaw, 0x22e); + public static readonly CommandBarComponentId PendingBattleGraphRight = new(GameEngineType.Eaw, 0x22f); + public static readonly CommandBarComponentId TatcicalAutoresolveShell = new(GameEngineType.Eaw, 0x230); + public static readonly CommandBarComponentId TacticalAutoresolveButton = new(GameEngineType.Eaw, 0x231); + public static readonly CommandBarComponentId TacticalAutoresolveGraphLeft = new(GameEngineType.Eaw, 0x232); + public static readonly CommandBarComponentId TacticalAutoresolveGraphRight = new(GameEngineType.Eaw, 0x233); + public static readonly CommandBarComponentId ObjectiveBack = new(GameEngineType.Eaw, 0x234); + public static readonly CommandBarComponentId ObjectiveHeaderText = new(GameEngineType.Eaw, 0x235); + public static readonly CommandBarComponentId ObjectiveText = new(GameEngineType.Eaw, 0x236); + public static readonly CommandBarComponentId ObjectiveIcon = new(GameEngineType.Eaw, 0x237); + public static readonly CommandBarComponentId GuiDialogTooltip = new(GameEngineType.Eaw, 0x238); + public static readonly CommandBarComponentId TargetUnitTypeShell = new(GameEngineType.Eaw, 0x239); + public static readonly CommandBarComponentId TargetUnitTypeTitle = new(GameEngineType.Eaw, 0x23a); + public static readonly CommandBarComponentId TargetUnitTypeCancel = new(GameEngineType.Eaw, 0x23b); + public static readonly CommandBarComponentId TargetUnitTypeDescription = new(GameEngineType.Eaw, 0x23c); + public static readonly CommandBarComponentId TargetUnitTypeSlot00 = new(GameEngineType.Eaw, 0x23d); + public static readonly CommandBarComponentId TargetUnitTypeSlot01 = new(GameEngineType.Eaw, 0x23e); + public static readonly CommandBarComponentId TargetUnitTypeSlot02 = new(GameEngineType.Eaw, 0x23f); + public static readonly CommandBarComponentId TargetUnitTypeSlot03 = new(GameEngineType.Eaw, 0x240); + public static readonly CommandBarComponentId TargetUnitTypeSlot04 = new(GameEngineType.Eaw, 0x241); + public static readonly CommandBarComponentId TargetUnitTypeSlot05 = new(GameEngineType.Eaw, 0x242); + public static readonly CommandBarComponentId TargetUnitTypeSlot06 = new(GameEngineType.Eaw, 0x243); + public static readonly CommandBarComponentId TargetUnitTypeSlot07 = new(GameEngineType.Eaw, 0x244); + public static readonly CommandBarComponentId TargetUnitTypeSlot08 = new(GameEngineType.Eaw, 0x245); + public static readonly CommandBarComponentId TargetUnitTypeSlot09 = new(GameEngineType.Eaw, 0x246); + public static readonly CommandBarComponentId TargetUnitTypeSlot10 = new(GameEngineType.Eaw, 0x247); + public static readonly CommandBarComponentId TargetUnitTypeSlot11 = new(GameEngineType.Eaw, 0x248); + public static readonly CommandBarComponentId TargetUnitTypeSlot12 = new(GameEngineType.Eaw, 0x249); + public static readonly CommandBarComponentId TargetUnitTypeSlot13 = new(GameEngineType.Eaw, 0x24a); + public static readonly CommandBarComponentId TargetUnitTypeSlot14 = new(GameEngineType.Eaw, 0x24b); + public static readonly CommandBarComponentId TargetUnitTypeSlot15 = new(GameEngineType.Eaw, 0x24c); + public static readonly CommandBarComponentId VcrButtonPlayPause = new(GameEngineType.Eaw, 0x24d); + public static readonly CommandBarComponentId VcrButtonFastForward = new(GameEngineType.Eaw, 0x24e); + public static readonly CommandBarComponentId VcrButtonFastForwardTactical = new(GameEngineType.Eaw, 0x24f); + public static readonly CommandBarComponentId VcrButtonPlayPauseTactical = new(GameEngineType.Eaw, 0x250); + public static readonly CommandBarComponentId AdvisorHintPopupGalactic = new(GameEngineType.Eaw, 0x251); + public static readonly CommandBarComponentId AdvisorHintPopupTactical = new(GameEngineType.Eaw, 0x252); + public static readonly CommandBarComponentId AdvisorHintBack = new(GameEngineType.Eaw, 0x253); + public static readonly CommandBarComponentId TextOrganizeFleet00 = new(GameEngineType.Eaw, 0x254); + public static readonly CommandBarComponentId TextOrganizeFleet01 = new(GameEngineType.Eaw, 0x255); + public static readonly CommandBarComponentId TextOrganizeFleet02 = new(GameEngineType.Eaw, 0x256); + public static readonly CommandBarComponentId IconOrganizeFleet00 = new(GameEngineType.Eaw, 0x257); + public static readonly CommandBarComponentId IconOrganizeFleet01 = new(GameEngineType.Eaw, 0x258); + public static readonly CommandBarComponentId IconOrganizeFleet02 = new(GameEngineType.Eaw, 0x259); + public static readonly CommandBarComponentId TextOrganizeLandFleet = new(GameEngineType.Eaw, 0x25a); + public static readonly CommandBarComponentId CsAbilityButton = new(GameEngineType.Eaw, 0x25b); + public static readonly CommandBarComponentId CsAbilityText = new(GameEngineType.Eaw, 0x25c); + public static readonly CommandBarComponentId MovieBoneGalactic = new(GameEngineType.Eaw, 0x25d); + public static readonly CommandBarComponentId MovieBoneTactical = new(GameEngineType.Eaw, 0x25e); + public static readonly CommandBarComponentId GenericCollision = new(GameEngineType.Eaw, 0x25f); + public static readonly CommandBarComponentId GoodHeroShell = new(GameEngineType.Eaw, 0x260); + public static readonly CommandBarComponentId GoodHeroSlot00 = new(GameEngineType.Eaw, 0x261); + public static readonly CommandBarComponentId GoodHeroSlot01 = new(GameEngineType.Eaw, 0x262); + public static readonly CommandBarComponentId GoodHeroSlot02 = new(GameEngineType.Eaw, 0x263); + public static readonly CommandBarComponentId GoodHeroSlot03 = new(GameEngineType.Eaw, 0x264); + public static readonly CommandBarComponentId GoodHeroSlot04 = new(GameEngineType.Eaw, 0x265); + public static readonly CommandBarComponentId GoodHeroSlot05 = new(GameEngineType.Eaw, 0x266); + public static readonly CommandBarComponentId GoodHeroSlot06 = new(GameEngineType.Eaw, 0x267); + public static readonly CommandBarComponentId GoodHeroSlot07 = new(GameEngineType.Eaw, 0x268); + public static readonly CommandBarComponentId GoodHeroSlot08 = new(GameEngineType.Eaw, 0x269); + public static readonly CommandBarComponentId GoodHeroSlot09 = new(GameEngineType.Eaw, 0x26a); + public static readonly CommandBarComponentId GoodHeroSlot10 = new(GameEngineType.Eaw, 0x26b); + public static readonly CommandBarComponentId GoodHeroHealth00 = new(GameEngineType.Eaw, 0x26c); + public static readonly CommandBarComponentId GoodHeroHealth01 = new(GameEngineType.Eaw, 0x26d); + public static readonly CommandBarComponentId GoodHeroHealth02 = new(GameEngineType.Eaw, 0x26e); + public static readonly CommandBarComponentId GoodHeroHealth03 = new(GameEngineType.Eaw, 0x26f); + public static readonly CommandBarComponentId GoodHeroHealth04 = new(GameEngineType.Eaw, 0x270); + public static readonly CommandBarComponentId GoodHeroHealth05 = new(GameEngineType.Eaw, 0x271); + public static readonly CommandBarComponentId GoodHeroHealth06 = new(GameEngineType.Eaw, 0x272); + public static readonly CommandBarComponentId GoodHeroHealth07 = new(GameEngineType.Eaw, 0x273); + public static readonly CommandBarComponentId GoodHeroHealth08 = new(GameEngineType.Eaw, 0x274); + public static readonly CommandBarComponentId GoodHeroHealth09 = new(GameEngineType.Eaw, 0x275); + public static readonly CommandBarComponentId GoodHeroHealth10 = new(GameEngineType.Eaw, 0x276); + public static readonly CommandBarComponentId EvilHeroShell = new(GameEngineType.Eaw, 0x277); + public static readonly CommandBarComponentId EvilHeroSlot00 = new(GameEngineType.Eaw, 0x278); + public static readonly CommandBarComponentId EvilHeroSlot01 = new(GameEngineType.Eaw, 0x279); + public static readonly CommandBarComponentId EvilHeroSlot02 = new(GameEngineType.Eaw, 0x27a); + public static readonly CommandBarComponentId EvilHeroSlot03 = new(GameEngineType.Eaw, 0x27b); + public static readonly CommandBarComponentId EvilHeroSlot04 = new(GameEngineType.Eaw, 0x27c); + public static readonly CommandBarComponentId EvilHeroSlot05 = new(GameEngineType.Eaw, 0x27d); + public static readonly CommandBarComponentId EvilHeroSlot06 = new(GameEngineType.Eaw, 0x27e); + public static readonly CommandBarComponentId EvilHeroSlot07 = new(GameEngineType.Eaw, 0x27f); + public static readonly CommandBarComponentId EvilHeroSlot08 = new(GameEngineType.Eaw, 0x280); + public static readonly CommandBarComponentId EvilHeroSlot09 = new(GameEngineType.Eaw, 0x281); + public static readonly CommandBarComponentId EvilHeroSlot10 = new(GameEngineType.Eaw, 0x282); + public static readonly CommandBarComponentId EvilHeroHealth00 = new(GameEngineType.Eaw, 0x283); + public static readonly CommandBarComponentId EvilHeroHealth01 = new(GameEngineType.Eaw, 0x284); + public static readonly CommandBarComponentId EvilHeroHealth02 = new(GameEngineType.Eaw, 0x285); + public static readonly CommandBarComponentId EvilHeroHealth03 = new(GameEngineType.Eaw, 0x286); + public static readonly CommandBarComponentId EvilHeroHealth04 = new(GameEngineType.Eaw, 0x287); + public static readonly CommandBarComponentId EvilHeroHealth05 = new(GameEngineType.Eaw, 0x288); + public static readonly CommandBarComponentId EvilHeroHealth06 = new(GameEngineType.Eaw, 0x289); + public static readonly CommandBarComponentId EvilHeroHealth07 = new(GameEngineType.Eaw, 0x28a); + public static readonly CommandBarComponentId EvilHeroHealth08 = new(GameEngineType.Eaw, 0x28b); + public static readonly CommandBarComponentId EvilHeroHealth09 = new(GameEngineType.Eaw, 0x28c); + public static readonly CommandBarComponentId EvilHeroHealth10 = new(GameEngineType.Eaw, 0x28d); + public static readonly CommandBarComponentId PauseShell = new(GameEngineType.Eaw, 0x28e); + public static readonly CommandBarComponentId PauseText = new(GameEngineType.Eaw, 0x28f); + public static readonly CommandBarComponentId PauseButton = new(GameEngineType.Eaw, 0x290); + public static readonly CommandBarComponentId StoryCampaignPendingBattleShell = new(GameEngineType.Eaw, 0x291); + public static readonly CommandBarComponentId StoryCampaignPendingBattleText = new(GameEngineType.Eaw, 0x292); + public static readonly CommandBarComponentId StoryCampaignPendingBattleButton = new(GameEngineType.Eaw, 0x293); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/FocCommandBarComponentIds.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/FocCommandBarComponentIds.cs new file mode 100644 index 0000000..b174d8e --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/FocCommandBarComponentIds.cs @@ -0,0 +1,970 @@ +namespace PG.StarWarsGame.Engine.CommandBar; + +public static class FocCommandBarComponentIds +{ + public static readonly CommandBarComponentId MainShell = new(GameEngineType.Foc, 0x0); + public static readonly CommandBarComponentId ProductionOptions = new(GameEngineType.Foc, 0x1); + public static readonly CommandBarComponentId GalacticCamera = new(GameEngineType.Foc, 0x2); + public static readonly CommandBarComponentId PlanetName = new(GameEngineType.Foc, 0x3); + public static readonly CommandBarComponentId PlanetValue = new(GameEngineType.Foc, 0x4); + public static readonly CommandBarComponentId PlanetAffiliation = new(GameEngineType.Foc, 0x5); + public static readonly CommandBarComponentId PlayerCredits = new(GameEngineType.Foc, 0x6); + public static readonly CommandBarComponentId ReinforcementCap = new(GameEngineType.Foc, 0x7); + public static readonly CommandBarComponentId PlanetNameTactical = new(GameEngineType.Foc, 0x8); + public static readonly CommandBarComponentId PlanetInfo = new(GameEngineType.Foc, 0x9); + public static readonly CommandBarComponentId Production0 = new(GameEngineType.Foc, 0xa); + public static readonly CommandBarComponentId Production1 = new(GameEngineType.Foc, 0xb); + public static readonly CommandBarComponentId Production2 = new(GameEngineType.Foc, 0xc); + public static readonly CommandBarComponentId Production3 = new(GameEngineType.Foc, 0xd); + public static readonly CommandBarComponentId Production4 = new(GameEngineType.Foc, 0xe); + public static readonly CommandBarComponentId Production5 = new(GameEngineType.Foc, 0xf); + public static readonly CommandBarComponentId Production6 = new(GameEngineType.Foc, 0x10); + public static readonly CommandBarComponentId Production7 = new(GameEngineType.Foc, 0x11); + public static readonly CommandBarComponentId Production8 = new(GameEngineType.Foc, 0x12); + public static readonly CommandBarComponentId Production9 = new(GameEngineType.Foc, 0x13); + public static readonly CommandBarComponentId Production10 = new(GameEngineType.Foc, 0x14); + public static readonly CommandBarComponentId Production11 = new(GameEngineType.Foc, 0x15); + public static readonly CommandBarComponentId Production12 = new(GameEngineType.Foc, 0x16); + public static readonly CommandBarComponentId Production13 = new(GameEngineType.Foc, 0x17); + public static readonly CommandBarComponentId Production14 = new(GameEngineType.Foc, 0x18); + public static readonly CommandBarComponentId Production15 = new(GameEngineType.Foc, 0x19); + public static readonly CommandBarComponentId Production16 = new(GameEngineType.Foc, 0x1a); + public static readonly CommandBarComponentId Production17 = new(GameEngineType.Foc, 0x1b); + public static readonly CommandBarComponentId Production18 = new(GameEngineType.Foc, 0x1c); + public static readonly CommandBarComponentId Production19 = new(GameEngineType.Foc, 0x1d); + public static readonly CommandBarComponentId Production20 = new(GameEngineType.Foc, 0x1e); + public static readonly CommandBarComponentId Production21 = new(GameEngineType.Foc, 0x1f); + public static readonly CommandBarComponentId Production22 = new(GameEngineType.Foc, 0x20); + public static readonly CommandBarComponentId Production23 = new(GameEngineType.Foc, 0x21); + public static readonly CommandBarComponentId Production24 = new(GameEngineType.Foc, 0x22); + public static readonly CommandBarComponentId Production25 = new(GameEngineType.Foc, 0x23); + public static readonly CommandBarComponentId Production26 = new(GameEngineType.Foc, 0x24); + public static readonly CommandBarComponentId Production27 = new(GameEngineType.Foc, 0x25); + public static readonly CommandBarComponentId Production28 = new(GameEngineType.Foc, 0x26); + public static readonly CommandBarComponentId Production29 = new(GameEngineType.Foc, 0x27); + public static readonly CommandBarComponentId Production30 = new(GameEngineType.Foc, 0x28); + public static readonly CommandBarComponentId Production31 = new(GameEngineType.Foc, 0x29); + public static readonly CommandBarComponentId Production32 = new(GameEngineType.Foc, 0x2a); + public static readonly CommandBarComponentId Production33 = new(GameEngineType.Foc, 0x2b); + public static readonly CommandBarComponentId Production34 = new(GameEngineType.Foc, 0x2c); + public static readonly CommandBarComponentId Production35 = new(GameEngineType.Foc, 0x2d); + public static readonly CommandBarComponentId Production36 = new(GameEngineType.Foc, 0x2e); + public static readonly CommandBarComponentId Production37 = new(GameEngineType.Foc, 0x2f); + public static readonly CommandBarComponentId Production38 = new(GameEngineType.Foc, 0x30); + public static readonly CommandBarComponentId Production39 = new(GameEngineType.Foc, 0x31); + public static readonly CommandBarComponentId Production40 = new(GameEngineType.Foc, 0x32); + public static readonly CommandBarComponentId Production41 = new(GameEngineType.Foc, 0x33); + public static readonly CommandBarComponentId Production42 = new(GameEngineType.Foc, 0x34); + public static readonly CommandBarComponentId Production43 = new(GameEngineType.Foc, 0x35); + public static readonly CommandBarComponentId Production44 = new(GameEngineType.Foc, 0x36); + public static readonly CommandBarComponentId Production45 = new(GameEngineType.Foc, 0x37); + public static readonly CommandBarComponentId Production46 = new(GameEngineType.Foc, 0x38); + public static readonly CommandBarComponentId Production47 = new(GameEngineType.Foc, 0x39); + public static readonly CommandBarComponentId DroidHelp = new(GameEngineType.Foc, 0x3a); + public static readonly CommandBarComponentId DroidHelpTactical = new(GameEngineType.Foc, 0x3b); + public static readonly CommandBarComponentId CurrentDay = new(GameEngineType.Foc, 0x3c); + public static readonly CommandBarComponentId DayCredits = new(GameEngineType.Foc, 0x3d); + public static readonly CommandBarComponentId PopulationCap = new(GameEngineType.Foc, 0x3e); + public static readonly CommandBarComponentId Filter0 = new(GameEngineType.Foc, 0x3f); + public static readonly CommandBarComponentId Filter1 = new(GameEngineType.Foc, 0x40); + public static readonly CommandBarComponentId Filter2 = new(GameEngineType.Foc, 0x41); + public static readonly CommandBarComponentId Filter3 = new(GameEngineType.Foc, 0x42); + public static readonly CommandBarComponentId StoryArcButton = new(GameEngineType.Foc, 0x43); + public static readonly CommandBarComponentId PlanetSummaryButton = new(GameEngineType.Foc, 0x44); + public static readonly CommandBarComponentId SpaceTab = new(GameEngineType.Foc, 0x45); + public static readonly CommandBarComponentId LandTab = new(GameEngineType.Foc, 0x46); + public static readonly CommandBarComponentId Dial = new(GameEngineType.Foc, 0x47); + public static readonly CommandBarComponentId ScrollRight = new(GameEngineType.Foc, 0x48); + public static readonly CommandBarComponentId ScrollLeft = new(GameEngineType.Foc, 0x49); + public static readonly CommandBarComponentId ZoomView = new(GameEngineType.Foc, 0x4a); + public static readonly CommandBarComponentId PrevPlanet = new(GameEngineType.Foc, 0x4b); + public static readonly CommandBarComponentId NextPlanet = new(GameEngineType.Foc, 0x4c); + public static readonly CommandBarComponentId RadarGalactic = new(GameEngineType.Foc, 0x4d); + public static readonly CommandBarComponentId TechLevel = new(GameEngineType.Foc, 0x4e); + public static readonly CommandBarComponentId BalancePip = new(GameEngineType.Foc, 0x4f); + public static readonly CommandBarComponentId BuildQueue00 = new(GameEngineType.Foc, 0x50); + public static readonly CommandBarComponentId BuildQueue01 = new(GameEngineType.Foc, 0x51); + public static readonly CommandBarComponentId BuildQueue02 = new(GameEngineType.Foc, 0x52); + public static readonly CommandBarComponentId BuildQueue03 = new(GameEngineType.Foc, 0x53); + public static readonly CommandBarComponentId BuildQueue04 = new(GameEngineType.Foc, 0x54); + public static readonly CommandBarComponentId BuildQueue05 = new(GameEngineType.Foc, 0x55); + public static readonly CommandBarComponentId BuildQueue06 = new(GameEngineType.Foc, 0x56); + public static readonly CommandBarComponentId BuildQueue07 = new(GameEngineType.Foc, 0x57); + public static readonly CommandBarComponentId BuildQueue08 = new(GameEngineType.Foc, 0x58); + public static readonly CommandBarComponentId BuildQueue09 = new(GameEngineType.Foc, 0x59); + public static readonly CommandBarComponentId OrganizationShell = new(GameEngineType.Foc, 0x5a); + public static readonly CommandBarComponentId OrganizationCollision = new(GameEngineType.Foc, 0x5b); + public static readonly CommandBarComponentId SmugglerBox = new(GameEngineType.Foc, 0x5c); + public static readonly CommandBarComponentId SpaceStationUpgrade01 = new(GameEngineType.Foc, 0x5d); + public static readonly CommandBarComponentId SpaceStationUpgrade02 = new(GameEngineType.Foc, 0x5e); + public static readonly CommandBarComponentId HeroAbilitySlot = new(GameEngineType.Foc, 0x5f); + public static readonly CommandBarComponentId PlanetOrganize0 = new(GameEngineType.Foc, 0x60); + public static readonly CommandBarComponentId PlanetOrganize1 = new(GameEngineType.Foc, 0x61); + public static readonly CommandBarComponentId PlanetOrganize2 = new(GameEngineType.Foc, 0x62); + public static readonly CommandBarComponentId PlanetOrganize3 = new(GameEngineType.Foc, 0x63); + public static readonly CommandBarComponentId PlanetOrganize4 = new(GameEngineType.Foc, 0x64); + public static readonly CommandBarComponentId PlanetOrganize5 = new(GameEngineType.Foc, 0x65); + public static readonly CommandBarComponentId PlanetOrganize6 = new(GameEngineType.Foc, 0x66); + public static readonly CommandBarComponentId PlanetOrganize7 = new(GameEngineType.Foc, 0x67); + public static readonly CommandBarComponentId PlanetOrganize8 = new(GameEngineType.Foc, 0x68); + public static readonly CommandBarComponentId PlanetOrganize9 = new(GameEngineType.Foc, 0x69); + public static readonly CommandBarComponentId SpecialStructureLand0 = new(GameEngineType.Foc, 0x6a); + public static readonly CommandBarComponentId SpecialStructureLand1 = new(GameEngineType.Foc, 0x6b); + public static readonly CommandBarComponentId SpecialStructureLand2 = new(GameEngineType.Foc, 0x6c); + public static readonly CommandBarComponentId SpecialStructureLand3 = new(GameEngineType.Foc, 0x6d); + public static readonly CommandBarComponentId SpecialStructureLand4 = new(GameEngineType.Foc, 0x6e); + public static readonly CommandBarComponentId SpecialStructureLand5 = new(GameEngineType.Foc, 0x6f); + public static readonly CommandBarComponentId SpecialStructureLand6 = new(GameEngineType.Foc, 0x70); + public static readonly CommandBarComponentId SpecialStructureLand7 = new(GameEngineType.Foc, 0x71); + public static readonly CommandBarComponentId SpecialStructureLand8 = new(GameEngineType.Foc, 0x72); + public static readonly CommandBarComponentId SpecialStructureLandSell = new(GameEngineType.Foc, 0x73); + public static readonly CommandBarComponentId BigFleet0 = new(GameEngineType.Foc, 0x74); + public static readonly CommandBarComponentId BigFleet1 = new(GameEngineType.Foc, 0x75); + public static readonly CommandBarComponentId BigFleet2 = new(GameEngineType.Foc, 0x76); + public static readonly CommandBarComponentId BigFleet3 = new(GameEngineType.Foc, 0x77); + public static readonly CommandBarComponentId Fleet0Slot0 = new(GameEngineType.Foc, 0x78); + public static readonly CommandBarComponentId Fleet0Slot1 = new(GameEngineType.Foc, 0x79); + public static readonly CommandBarComponentId Fleet0Slot2 = new(GameEngineType.Foc, 0x7a); + public static readonly CommandBarComponentId Fleet0Slot3 = new(GameEngineType.Foc, 0x7b); + public static readonly CommandBarComponentId Fleet0Slot4 = new(GameEngineType.Foc, 0x7c); + public static readonly CommandBarComponentId Fleet0Slot5 = new(GameEngineType.Foc, 0x7d); + public static readonly CommandBarComponentId Fleet0Slot6 = new(GameEngineType.Foc, 0x7e); + public static readonly CommandBarComponentId Fleet0Slot7 = new(GameEngineType.Foc, 0x7f); + public static readonly CommandBarComponentId Fleet0Slot8 = new(GameEngineType.Foc, 0x80); + public static readonly CommandBarComponentId Fleet0Slot9 = new(GameEngineType.Foc, 0x81); + public static readonly CommandBarComponentId Fleet0Slot10 = new(GameEngineType.Foc, 0x82); + public static readonly CommandBarComponentId Fleet0Slot11 = new(GameEngineType.Foc, 0x83); + public static readonly CommandBarComponentId Fleet0Slot12 = new(GameEngineType.Foc, 0x84); + public static readonly CommandBarComponentId Fleet0Slot13 = new(GameEngineType.Foc, 0x85); + public static readonly CommandBarComponentId Fleet0Slot14 = new(GameEngineType.Foc, 0x86); + public static readonly CommandBarComponentId Fleet0Slot15 = new(GameEngineType.Foc, 0x87); + public static readonly CommandBarComponentId Fleet0Slot16 = new(GameEngineType.Foc, 0x88); + public static readonly CommandBarComponentId Fleet0Slot17 = new(GameEngineType.Foc, 0x89); + public static readonly CommandBarComponentId Fleet0Slot18 = new(GameEngineType.Foc, 0x8a); + public static readonly CommandBarComponentId Fleet0Slot19 = new(GameEngineType.Foc, 0x8b); + public static readonly CommandBarComponentId Fleet0Slot20 = new(GameEngineType.Foc, 0x8c); + public static readonly CommandBarComponentId Fleet0Slot21 = new(GameEngineType.Foc, 0x8d); + public static readonly CommandBarComponentId Fleet0Slot22 = new(GameEngineType.Foc, 0x8e); + public static readonly CommandBarComponentId Fleet0Slot23 = new(GameEngineType.Foc, 0x8f); + public static readonly CommandBarComponentId Fleet0Slot24 = new(GameEngineType.Foc, 0x90); + public static readonly CommandBarComponentId Fleet0Slot25 = new(GameEngineType.Foc, 0x91); + public static readonly CommandBarComponentId Fleet0Slot26 = new(GameEngineType.Foc, 0x92); + public static readonly CommandBarComponentId Fleet0Slot27 = new(GameEngineType.Foc, 0x93); + public static readonly CommandBarComponentId Fleet0Slot28 = new(GameEngineType.Foc, 0x94); + public static readonly CommandBarComponentId Fleet0Slot29 = new(GameEngineType.Foc, 0x95); + public static readonly CommandBarComponentId Fleet0Slot30 = new(GameEngineType.Foc, 0x96); + public static readonly CommandBarComponentId Fleet0Slot31 = new(GameEngineType.Foc, 0x97); + public static readonly CommandBarComponentId Fleet0Slot32 = new(GameEngineType.Foc, 0x98); + public static readonly CommandBarComponentId Fleet0Slot33 = new(GameEngineType.Foc, 0x99); + public static readonly CommandBarComponentId Fleet0Slot34 = new(GameEngineType.Foc, 0x9a); + public static readonly CommandBarComponentId Fleet1Slot0 = new(GameEngineType.Foc, 0x9b); + public static readonly CommandBarComponentId Fleet1Slot1 = new(GameEngineType.Foc, 0x9c); + public static readonly CommandBarComponentId Fleet1Slot2 = new(GameEngineType.Foc, 0x9d); + public static readonly CommandBarComponentId Fleet1Slot3 = new(GameEngineType.Foc, 0x9e); + public static readonly CommandBarComponentId Fleet1Slot4 = new(GameEngineType.Foc, 0x9f); + public static readonly CommandBarComponentId Fleet1Slot5 = new(GameEngineType.Foc, 0xa0); + public static readonly CommandBarComponentId Fleet1Slot6 = new(GameEngineType.Foc, 0xa1); + public static readonly CommandBarComponentId Fleet1Slot7 = new(GameEngineType.Foc, 0xa2); + public static readonly CommandBarComponentId Fleet1Slot8 = new(GameEngineType.Foc, 0xa3); + public static readonly CommandBarComponentId Fleet1Slot9 = new(GameEngineType.Foc, 0xa4); + public static readonly CommandBarComponentId Fleet1Slot10 = new(GameEngineType.Foc, 0xa5); + public static readonly CommandBarComponentId Fleet1Slot11 = new(GameEngineType.Foc, 0xa6); + public static readonly CommandBarComponentId Fleet1Slot12 = new(GameEngineType.Foc, 0xa7); + public static readonly CommandBarComponentId Fleet1Slot13 = new(GameEngineType.Foc, 0xa8); + public static readonly CommandBarComponentId Fleet1Slot14 = new(GameEngineType.Foc, 0xa9); + public static readonly CommandBarComponentId Fleet1Slot15 = new(GameEngineType.Foc, 0xaa); + public static readonly CommandBarComponentId Fleet1Slot16 = new(GameEngineType.Foc, 0xab); + public static readonly CommandBarComponentId Fleet1Slot17 = new(GameEngineType.Foc, 0xac); + public static readonly CommandBarComponentId Fleet1Slot18 = new(GameEngineType.Foc, 0xad); + public static readonly CommandBarComponentId Fleet1Slot19 = new(GameEngineType.Foc, 0xae); + public static readonly CommandBarComponentId Fleet1Slot20 = new(GameEngineType.Foc, 0xaf); + public static readonly CommandBarComponentId Fleet1Slot21 = new(GameEngineType.Foc, 0xb0); + public static readonly CommandBarComponentId Fleet1Slot22 = new(GameEngineType.Foc, 0xb1); + public static readonly CommandBarComponentId Fleet1Slot23 = new(GameEngineType.Foc, 0xb2); + public static readonly CommandBarComponentId Fleet1Slot24 = new(GameEngineType.Foc, 0xb3); + public static readonly CommandBarComponentId Fleet1Slot25 = new(GameEngineType.Foc, 0xb4); + public static readonly CommandBarComponentId Fleet1Slot26 = new(GameEngineType.Foc, 0xb5); + public static readonly CommandBarComponentId Fleet1Slot27 = new(GameEngineType.Foc, 0xb6); + public static readonly CommandBarComponentId Fleet1Slot28 = new(GameEngineType.Foc, 0xb7); + public static readonly CommandBarComponentId Fleet1Slot29 = new(GameEngineType.Foc, 0xb8); + public static readonly CommandBarComponentId Fleet1Slot30 = new(GameEngineType.Foc, 0xb9); + public static readonly CommandBarComponentId Fleet1Slot31 = new(GameEngineType.Foc, 0xba); + public static readonly CommandBarComponentId Fleet1Slot32 = new(GameEngineType.Foc, 0xbb); + public static readonly CommandBarComponentId Fleet1Slot33 = new(GameEngineType.Foc, 0xbc); + public static readonly CommandBarComponentId Fleet1Slot34 = new(GameEngineType.Foc, 0xbd); + public static readonly CommandBarComponentId Fleet2Slot0 = new(GameEngineType.Foc, 0xbe); + public static readonly CommandBarComponentId Fleet2Slot1 = new(GameEngineType.Foc, 0xbf); + public static readonly CommandBarComponentId Fleet2Slot2 = new(GameEngineType.Foc, 0xc0); + public static readonly CommandBarComponentId Fleet2Slot3 = new(GameEngineType.Foc, 0xc1); + public static readonly CommandBarComponentId Fleet2Slot4 = new(GameEngineType.Foc, 0xc2); + public static readonly CommandBarComponentId Fleet2Slot5 = new(GameEngineType.Foc, 0xc3); + public static readonly CommandBarComponentId Fleet2Slot6 = new(GameEngineType.Foc, 0xc4); + public static readonly CommandBarComponentId Fleet2Slot7 = new(GameEngineType.Foc, 0xc5); + public static readonly CommandBarComponentId Fleet2Slot8 = new(GameEngineType.Foc, 0xc6); + public static readonly CommandBarComponentId Fleet2Slot9 = new(GameEngineType.Foc, 0xc7); + public static readonly CommandBarComponentId Fleet2Slot10 = new(GameEngineType.Foc, 0xc8); + public static readonly CommandBarComponentId Fleet2Slot11 = new(GameEngineType.Foc, 0xc9); + public static readonly CommandBarComponentId Fleet2Slot12 = new(GameEngineType.Foc, 0xca); + public static readonly CommandBarComponentId Fleet2Slot13 = new(GameEngineType.Foc, 0xcb); + public static readonly CommandBarComponentId Fleet2Slot14 = new(GameEngineType.Foc, 0xcc); + public static readonly CommandBarComponentId Fleet2Slot15 = new(GameEngineType.Foc, 0xcd); + public static readonly CommandBarComponentId Fleet2Slot16 = new(GameEngineType.Foc, 0xce); + public static readonly CommandBarComponentId Fleet2Slot17 = new(GameEngineType.Foc, 0xcf); + public static readonly CommandBarComponentId Fleet2Slot18 = new(GameEngineType.Foc, 0xd0); + public static readonly CommandBarComponentId Fleet2Slot19 = new(GameEngineType.Foc, 0xd1); + public static readonly CommandBarComponentId Fleet2Slot20 = new(GameEngineType.Foc, 0xd2); + public static readonly CommandBarComponentId Fleet2Slot21 = new(GameEngineType.Foc, 0xd3); + public static readonly CommandBarComponentId Fleet2Slot22 = new(GameEngineType.Foc, 0xd4); + public static readonly CommandBarComponentId Fleet2Slot23 = new(GameEngineType.Foc, 0xd5); + public static readonly CommandBarComponentId Fleet2Slot24 = new(GameEngineType.Foc, 0xd6); + public static readonly CommandBarComponentId Fleet2Slot25 = new(GameEngineType.Foc, 0xd7); + public static readonly CommandBarComponentId Fleet2Slot26 = new(GameEngineType.Foc, 0xd8); + public static readonly CommandBarComponentId Fleet2Slot27 = new(GameEngineType.Foc, 0xd9); + public static readonly CommandBarComponentId Fleet2Slot28 = new(GameEngineType.Foc, 0xda); + public static readonly CommandBarComponentId Fleet2Slot29 = new(GameEngineType.Foc, 0xdb); + public static readonly CommandBarComponentId Fleet2Slot30 = new(GameEngineType.Foc, 0xdc); + public static readonly CommandBarComponentId Fleet2Slot31 = new(GameEngineType.Foc, 0xdd); + public static readonly CommandBarComponentId Fleet2Slot32 = new(GameEngineType.Foc, 0xde); + public static readonly CommandBarComponentId Fleet2Slot33 = new(GameEngineType.Foc, 0xdf); + public static readonly CommandBarComponentId Fleet2Slot34 = new(GameEngineType.Foc, 0xe0); + public static readonly CommandBarComponentId TacticalMain = new(GameEngineType.Foc, 0xe1); + public static readonly CommandBarComponentId TacticalOptions = new(GameEngineType.Foc, 0xe2); + public static readonly CommandBarComponentId TacticalCamera = new(GameEngineType.Foc, 0xe3); + public static readonly CommandBarComponentId TacticalBeacon = new(GameEngineType.Foc, 0xe4); + public static readonly CommandBarComponentId TacticalWeapon0 = new(GameEngineType.Foc, 0xe5); + public static readonly CommandBarComponentId TacticalWeapon1 = new(GameEngineType.Foc, 0xe6); + public static readonly CommandBarComponentId TacticalReinforce = new(GameEngineType.Foc, 0xe7); + public static readonly CommandBarComponentId TacticalRetreat = new(GameEngineType.Foc, 0xe8); + public static readonly CommandBarComponentId TacticalStoryArc = new(GameEngineType.Foc, 0xe9); + public static readonly CommandBarComponentId TacticalTechLevel = new(GameEngineType.Foc, 0xea); + public static readonly CommandBarComponentId TacticalAttack = new(GameEngineType.Foc, 0xeb); + public static readonly CommandBarComponentId TacticalAttackMove = new(GameEngineType.Foc, 0xec); + public static readonly CommandBarComponentId TacticalMove = new(GameEngineType.Foc, 0xed); + public static readonly CommandBarComponentId TacticalWaypoint = new(GameEngineType.Foc, 0xee); + public static readonly CommandBarComponentId TacticalStop = new(GameEngineType.Foc, 0xef); + public static readonly CommandBarComponentId TacticalGuard = new(GameEngineType.Foc, 0xf0); + public static readonly CommandBarComponentId SpaceTacticalRadar = new(GameEngineType.Foc, 0xf1); + public static readonly CommandBarComponentId SpaceTacticalCredits = new(GameEngineType.Foc, 0xf2); + public static readonly CommandBarComponentId SpaceTacticalGrabBar = new(GameEngineType.Foc, 0xf3); + public static readonly CommandBarComponentId SpaceTacticalHealthBar = new(GameEngineType.Foc, 0xf4); + public static readonly CommandBarComponentId SpaceTacticalBracketSmall = new(GameEngineType.Foc, 0xf5); + public static readonly CommandBarComponentId SpaceTacticalBracketMedium = new(GameEngineType.Foc, 0xf6); + public static readonly CommandBarComponentId SpaceTacticalBracketLarge = new(GameEngineType.Foc, 0xf7); + public static readonly CommandBarComponentId SpaceTacticalHeroIcon = new(GameEngineType.Foc, 0xf8); + public static readonly CommandBarComponentId SpaceTacticalHeroHealth = new(GameEngineType.Foc, 0xf9); + public static readonly CommandBarComponentId SpaceTacticalHealth = new(GameEngineType.Foc, 0xfa); + public static readonly CommandBarComponentId SpaceTacticalHealthMedium = new(GameEngineType.Foc, 0xfb); + public static readonly CommandBarComponentId SpaceTacticalHealthLarge = new(GameEngineType.Foc, 0xfc); + public static readonly CommandBarComponentId SpaceTacticalShields = new(GameEngineType.Foc, 0xfd); + public static readonly CommandBarComponentId SpaceTacticalShieldsMedium = new(GameEngineType.Foc, 0xfe); + public static readonly CommandBarComponentId SpaceTacticalShieldsLarge = new(GameEngineType.Foc, 0xff); + public static readonly CommandBarComponentId SpaceTacticalPower = new(GameEngineType.Foc, 0x100); + public static readonly CommandBarComponentId SpaceTacticalControlGroup = new(GameEngineType.Foc, 0x101); + public static readonly CommandBarComponentId SpaceTacticalAbilityIcon = new(GameEngineType.Foc, 0x102); + public static readonly CommandBarComponentId SpaceTacticalGarrisonIcon = new(GameEngineType.Foc, 0x103); + public static readonly CommandBarComponentId LandTacticalWeatherIcon = new(GameEngineType.Foc, 0x104); + public static readonly CommandBarComponentId GarrisonSlotIcon = new(GameEngineType.Foc, 0x105); + public static readonly CommandBarComponentId DsFireShell = new(GameEngineType.Foc, 0x106); + public static readonly CommandBarComponentId DsFire = new(GameEngineType.Foc, 0x107); + public static readonly CommandBarComponentId DsCountdownTimer = new(GameEngineType.Foc, 0x108); + public static readonly CommandBarComponentId TacticalSelect00 = new(GameEngineType.Foc, 0x109); + public static readonly CommandBarComponentId TacticalSelect01 = new(GameEngineType.Foc, 0x10a); + public static readonly CommandBarComponentId TacticalSelect02 = new(GameEngineType.Foc, 0x10b); + public static readonly CommandBarComponentId TacticalSelect03 = new(GameEngineType.Foc, 0x10c); + public static readonly CommandBarComponentId TacticalSelect04 = new(GameEngineType.Foc, 0x10d); + public static readonly CommandBarComponentId TacticalSelect05 = new(GameEngineType.Foc, 0x10e); + public static readonly CommandBarComponentId TacticalSelect06 = new(GameEngineType.Foc, 0x10f); + public static readonly CommandBarComponentId TacticalSelect07 = new(GameEngineType.Foc, 0x110); + public static readonly CommandBarComponentId TacticalSelect08 = new(GameEngineType.Foc, 0x111); + public static readonly CommandBarComponentId TacticalSelect09 = new(GameEngineType.Foc, 0x112); + public static readonly CommandBarComponentId TacticalSelect10 = new(GameEngineType.Foc, 0x113); + public static readonly CommandBarComponentId TacticalSelect11 = new(GameEngineType.Foc, 0x114); + public static readonly CommandBarComponentId TacticalSelect12 = new(GameEngineType.Foc, 0x115); + public static readonly CommandBarComponentId TacticalSelect13 = new(GameEngineType.Foc, 0x116); + public static readonly CommandBarComponentId TacticalSelect14 = new(GameEngineType.Foc, 0x117); + public static readonly CommandBarComponentId TacticalSelect15 = new(GameEngineType.Foc, 0x118); + public static readonly CommandBarComponentId TacticalSelect16 = new(GameEngineType.Foc, 0x119); + public static readonly CommandBarComponentId TacticalSelect17 = new(GameEngineType.Foc, 0x11a); + public static readonly CommandBarComponentId TacticalSelect18 = new(GameEngineType.Foc, 0x11b); + public static readonly CommandBarComponentId TacticalSelect19 = new(GameEngineType.Foc, 0x11c); + public static readonly CommandBarComponentId TacticalSelect20 = new(GameEngineType.Foc, 0x11d); + public static readonly CommandBarComponentId TacticalSelect21 = new(GameEngineType.Foc, 0x11e); + public static readonly CommandBarComponentId TacticalSelect22 = new(GameEngineType.Foc, 0x11f); + public static readonly CommandBarComponentId TacticalSelect23 = new(GameEngineType.Foc, 0x120); + public static readonly CommandBarComponentId TacticalSelect24 = new(GameEngineType.Foc, 0x121); + public static readonly CommandBarComponentId TacticalSelect25 = new(GameEngineType.Foc, 0x122); + public static readonly CommandBarComponentId TacticalSelect26 = new(GameEngineType.Foc, 0x123); + public static readonly CommandBarComponentId TacticalSelect27 = new(GameEngineType.Foc, 0x124); + public static readonly CommandBarComponentId TacticalSelect28 = new(GameEngineType.Foc, 0x125); + public static readonly CommandBarComponentId TacticalSelect29 = new(GameEngineType.Foc, 0x126); + public static readonly CommandBarComponentId TacticalSelect30 = new(GameEngineType.Foc, 0x127); + public static readonly CommandBarComponentId TacticalSelect31 = new(GameEngineType.Foc, 0x128); + public static readonly CommandBarComponentId TacticalSelect32 = new(GameEngineType.Foc, 0x129); + public static readonly CommandBarComponentId TacticalSelect33 = new(GameEngineType.Foc, 0x12a); + public static readonly CommandBarComponentId TacticalSelect34 = new(GameEngineType.Foc, 0x12b); + public static readonly CommandBarComponentId TacticalSelect35 = new(GameEngineType.Foc, 0x12c); + public static readonly CommandBarComponentId TacticalSelect36 = new(GameEngineType.Foc, 0x12d); + public static readonly CommandBarComponentId TacticalSelect37 = new(GameEngineType.Foc, 0x12e); + public static readonly CommandBarComponentId TacticalSelect38 = new(GameEngineType.Foc, 0x12f); + public static readonly CommandBarComponentId TacticalSelect39 = new(GameEngineType.Foc, 0x130); + public static readonly CommandBarComponentId TacticalSelect40 = new(GameEngineType.Foc, 0x131); + public static readonly CommandBarComponentId TacticalSelect41 = new(GameEngineType.Foc, 0x132); + public static readonly CommandBarComponentId TacticalSelect42 = new(GameEngineType.Foc, 0x133); + public static readonly CommandBarComponentId TacticalSelect43 = new(GameEngineType.Foc, 0x134); + public static readonly CommandBarComponentId TacticalSelect44 = new(GameEngineType.Foc, 0x135); + public static readonly CommandBarComponentId TacticalSelect45 = new(GameEngineType.Foc, 0x136); + public static readonly CommandBarComponentId TacticalSelect46 = new(GameEngineType.Foc, 0x137); + public static readonly CommandBarComponentId TacticalSelect47 = new(GameEngineType.Foc, 0x138); + public static readonly CommandBarComponentId TacticalSelectHealth00 = new(GameEngineType.Foc, 0x139); + public static readonly CommandBarComponentId TacticalSelectHealth01 = new(GameEngineType.Foc, 0x13a); + public static readonly CommandBarComponentId TacticalSelectHealth02 = new(GameEngineType.Foc, 0x13b); + public static readonly CommandBarComponentId TacticalSelectHealth03 = new(GameEngineType.Foc, 0x13c); + public static readonly CommandBarComponentId TacticalSelectHealth04 = new(GameEngineType.Foc, 0x13d); + public static readonly CommandBarComponentId TacticalSelectHealth05 = new(GameEngineType.Foc, 0x13e); + public static readonly CommandBarComponentId TacticalSelectHealth06 = new(GameEngineType.Foc, 0x13f); + public static readonly CommandBarComponentId TacticalSelectHealth07 = new(GameEngineType.Foc, 0x140); + public static readonly CommandBarComponentId TacticalSelectHealth08 = new(GameEngineType.Foc, 0x141); + public static readonly CommandBarComponentId TacticalSelectHealth09 = new(GameEngineType.Foc, 0x142); + public static readonly CommandBarComponentId TacticalSelectHealth10 = new(GameEngineType.Foc, 0x143); + public static readonly CommandBarComponentId TacticalSelectHealth11 = new(GameEngineType.Foc, 0x144); + public static readonly CommandBarComponentId TacticalSelectHealth12 = new(GameEngineType.Foc, 0x145); + public static readonly CommandBarComponentId TacticalSelectHealth13 = new(GameEngineType.Foc, 0x146); + public static readonly CommandBarComponentId TacticalSelectHealth14 = new(GameEngineType.Foc, 0x147); + public static readonly CommandBarComponentId TacticalSelectHealth15 = new(GameEngineType.Foc, 0x148); + public static readonly CommandBarComponentId TacticalSelectHealth16 = new(GameEngineType.Foc, 0x149); + public static readonly CommandBarComponentId TacticalSelectHealth17 = new(GameEngineType.Foc, 0x14a); + public static readonly CommandBarComponentId TacticalSelectHealth18 = new(GameEngineType.Foc, 0x14b); + public static readonly CommandBarComponentId TacticalSelectHealth19 = new(GameEngineType.Foc, 0x14c); + public static readonly CommandBarComponentId TacticalSelectHealth20 = new(GameEngineType.Foc, 0x14d); + public static readonly CommandBarComponentId TacticalSelectHealth21 = new(GameEngineType.Foc, 0x14e); + public static readonly CommandBarComponentId TacticalSelectHealth22 = new(GameEngineType.Foc, 0x14f); + public static readonly CommandBarComponentId TacticalSelectHealth23 = new(GameEngineType.Foc, 0x150); + public static readonly CommandBarComponentId TacticalSelectHealth24 = new(GameEngineType.Foc, 0x151); + public static readonly CommandBarComponentId TacticalSelectHealth25 = new(GameEngineType.Foc, 0x152); + public static readonly CommandBarComponentId TacticalSelectHealth26 = new(GameEngineType.Foc, 0x153); + public static readonly CommandBarComponentId TacticalSelectHealth27 = new(GameEngineType.Foc, 0x154); + public static readonly CommandBarComponentId TacticalSelectHealth28 = new(GameEngineType.Foc, 0x155); + public static readonly CommandBarComponentId TacticalSelectHealth29 = new(GameEngineType.Foc, 0x156); + public static readonly CommandBarComponentId TacticalSelectHealth30 = new(GameEngineType.Foc, 0x157); + public static readonly CommandBarComponentId TacticalSelectHealth31 = new(GameEngineType.Foc, 0x158); + public static readonly CommandBarComponentId TacticalSelectHealth32 = new(GameEngineType.Foc, 0x159); + public static readonly CommandBarComponentId TacticalSelectHealth33 = new(GameEngineType.Foc, 0x15a); + public static readonly CommandBarComponentId TacticalSelectHealth34 = new(GameEngineType.Foc, 0x15b); + public static readonly CommandBarComponentId TacticalSelectHealth35 = new(GameEngineType.Foc, 0x15c); + public static readonly CommandBarComponentId TacticalSelectHealth36 = new(GameEngineType.Foc, 0x15d); + public static readonly CommandBarComponentId TacticalSelectHealth37 = new(GameEngineType.Foc, 0x15e); + public static readonly CommandBarComponentId TacticalSelectHealth38 = new(GameEngineType.Foc, 0x15f); + public static readonly CommandBarComponentId TacticalSelectHealth39 = new(GameEngineType.Foc, 0x160); + public static readonly CommandBarComponentId TacticalSelectHealth40 = new(GameEngineType.Foc, 0x161); + public static readonly CommandBarComponentId TacticalSelectHealth41 = new(GameEngineType.Foc, 0x162); + public static readonly CommandBarComponentId TacticalSelectHealth42 = new(GameEngineType.Foc, 0x163); + public static readonly CommandBarComponentId TacticalSelectHealth43 = new(GameEngineType.Foc, 0x164); + public static readonly CommandBarComponentId TacticalSelectHealth44 = new(GameEngineType.Foc, 0x165); + public static readonly CommandBarComponentId TacticalSelectHealth45 = new(GameEngineType.Foc, 0x166); + public static readonly CommandBarComponentId TacticalSelectHealth46 = new(GameEngineType.Foc, 0x167); + public static readonly CommandBarComponentId TacticalSelectHealth47 = new(GameEngineType.Foc, 0x168); + public static readonly CommandBarComponentId TacticalSelectShield00 = new(GameEngineType.Foc, 0x169); + public static readonly CommandBarComponentId TacticalSelectShield01 = new(GameEngineType.Foc, 0x16a); + public static readonly CommandBarComponentId TacticalSelectShield02 = new(GameEngineType.Foc, 0x16b); + public static readonly CommandBarComponentId TacticalSelectShield03 = new(GameEngineType.Foc, 0x16c); + public static readonly CommandBarComponentId TacticalSelectShield04 = new(GameEngineType.Foc, 0x16d); + public static readonly CommandBarComponentId TacticalSelectShield05 = new(GameEngineType.Foc, 0x16e); + public static readonly CommandBarComponentId TacticalSelectShield06 = new(GameEngineType.Foc, 0x16f); + public static readonly CommandBarComponentId TacticalSelectShield07 = new(GameEngineType.Foc, 0x170); + public static readonly CommandBarComponentId TacticalSelectShield08 = new(GameEngineType.Foc, 0x171); + public static readonly CommandBarComponentId TacticalSelectShield09 = new(GameEngineType.Foc, 0x172); + public static readonly CommandBarComponentId TacticalSelectShield10 = new(GameEngineType.Foc, 0x173); + public static readonly CommandBarComponentId TacticalSelectShield11 = new(GameEngineType.Foc, 0x174); + public static readonly CommandBarComponentId TacticalSelectShield12 = new(GameEngineType.Foc, 0x175); + public static readonly CommandBarComponentId TacticalSelectShield13 = new(GameEngineType.Foc, 0x176); + public static readonly CommandBarComponentId TacticalSelectShield14 = new(GameEngineType.Foc, 0x177); + public static readonly CommandBarComponentId TacticalSelectShield15 = new(GameEngineType.Foc, 0x178); + public static readonly CommandBarComponentId TacticalSelectShield16 = new(GameEngineType.Foc, 0x179); + public static readonly CommandBarComponentId TacticalSelectShield17 = new(GameEngineType.Foc, 0x17a); + public static readonly CommandBarComponentId TacticalSelectShield18 = new(GameEngineType.Foc, 0x17b); + public static readonly CommandBarComponentId TacticalSelectShield19 = new(GameEngineType.Foc, 0x17c); + public static readonly CommandBarComponentId TacticalSelectShield20 = new(GameEngineType.Foc, 0x17d); + public static readonly CommandBarComponentId TacticalSelectShield21 = new(GameEngineType.Foc, 0x17e); + public static readonly CommandBarComponentId TacticalSelectShield22 = new(GameEngineType.Foc, 0x17f); + public static readonly CommandBarComponentId TacticalSelectShield23 = new(GameEngineType.Foc, 0x180); + public static readonly CommandBarComponentId TacticalSelectShield24 = new(GameEngineType.Foc, 0x181); + public static readonly CommandBarComponentId TacticalSelectShield25 = new(GameEngineType.Foc, 0x182); + public static readonly CommandBarComponentId TacticalSelectShield26 = new(GameEngineType.Foc, 0x183); + public static readonly CommandBarComponentId TacticalSelectShield27 = new(GameEngineType.Foc, 0x184); + public static readonly CommandBarComponentId TacticalSelectShield28 = new(GameEngineType.Foc, 0x185); + public static readonly CommandBarComponentId TacticalSelectShield29 = new(GameEngineType.Foc, 0x186); + public static readonly CommandBarComponentId TacticalSelectShield30 = new(GameEngineType.Foc, 0x187); + public static readonly CommandBarComponentId TacticalSelectShield31 = new(GameEngineType.Foc, 0x188); + public static readonly CommandBarComponentId TacticalSelectShield32 = new(GameEngineType.Foc, 0x189); + public static readonly CommandBarComponentId TacticalSelectShield33 = new(GameEngineType.Foc, 0x18a); + public static readonly CommandBarComponentId TacticalSelectShield34 = new(GameEngineType.Foc, 0x18b); + public static readonly CommandBarComponentId TacticalSelectShield35 = new(GameEngineType.Foc, 0x18c); + public static readonly CommandBarComponentId TacticalSelectShield36 = new(GameEngineType.Foc, 0x18d); + public static readonly CommandBarComponentId TacticalSelectShield37 = new(GameEngineType.Foc, 0x18e); + public static readonly CommandBarComponentId TacticalSelectShield38 = new(GameEngineType.Foc, 0x18f); + public static readonly CommandBarComponentId TacticalSelectShield39 = new(GameEngineType.Foc, 0x190); + public static readonly CommandBarComponentId TacticalSelectShield40 = new(GameEngineType.Foc, 0x191); + public static readonly CommandBarComponentId TacticalSelectShield41 = new(GameEngineType.Foc, 0x192); + public static readonly CommandBarComponentId TacticalSelectShield42 = new(GameEngineType.Foc, 0x193); + public static readonly CommandBarComponentId TacticalSelectShield43 = new(GameEngineType.Foc, 0x194); + public static readonly CommandBarComponentId TacticalSelectShield44 = new(GameEngineType.Foc, 0x195); + public static readonly CommandBarComponentId TacticalSelectShield45 = new(GameEngineType.Foc, 0x196); + public static readonly CommandBarComponentId TacticalSelectShield46 = new(GameEngineType.Foc, 0x197); + public static readonly CommandBarComponentId TacticalSelectShield47 = new(GameEngineType.Foc, 0x198); + public static readonly CommandBarComponentId TacticalBorder00 = new(GameEngineType.Foc, 0x199); + public static readonly CommandBarComponentId TacticalBorder01 = new(GameEngineType.Foc, 0x19a); + public static readonly CommandBarComponentId TacticalBorder02 = new(GameEngineType.Foc, 0x19b); + public static readonly CommandBarComponentId TacticalBorder03 = new(GameEngineType.Foc, 0x19c); + public static readonly CommandBarComponentId TacticalBorder04 = new(GameEngineType.Foc, 0x19d); + public static readonly CommandBarComponentId TacticalBorder05 = new(GameEngineType.Foc, 0x19e); + public static readonly CommandBarComponentId TacticalBorder06 = new(GameEngineType.Foc, 0x19f); + public static readonly CommandBarComponentId TacticalBorder07 = new(GameEngineType.Foc, 0x1a0); + public static readonly CommandBarComponentId TacticalBorder08 = new(GameEngineType.Foc, 0x1a1); + public static readonly CommandBarComponentId TacticalBorder09 = new(GameEngineType.Foc, 0x1a2); + public static readonly CommandBarComponentId TacticalBorder10 = new(GameEngineType.Foc, 0x1a3); + public static readonly CommandBarComponentId TacticalBorder11 = new(GameEngineType.Foc, 0x1a4); + public static readonly CommandBarComponentId TacticalSelectButton00 = new(GameEngineType.Foc, 0x1a5); + public static readonly CommandBarComponentId TacticalSelectButton01 = new(GameEngineType.Foc, 0x1a6); + public static readonly CommandBarComponentId TacticalSelectButton02 = new(GameEngineType.Foc, 0x1a7); + public static readonly CommandBarComponentId TacticalSelectButton03 = new(GameEngineType.Foc, 0x1a8); + public static readonly CommandBarComponentId TacticalSelectButton04 = new(GameEngineType.Foc, 0x1a9); + public static readonly CommandBarComponentId TacticalSelectButton05 = new(GameEngineType.Foc, 0x1aa); + public static readonly CommandBarComponentId TacticalSelectButton06 = new(GameEngineType.Foc, 0x1ab); + public static readonly CommandBarComponentId TacticalSelectButton07 = new(GameEngineType.Foc, 0x1ac); + public static readonly CommandBarComponentId TacticalSelectButton08 = new(GameEngineType.Foc, 0x1ad); + public static readonly CommandBarComponentId TacticalSelectButton09 = new(GameEngineType.Foc, 0x1ae); + public static readonly CommandBarComponentId TacticalSelectButton10 = new(GameEngineType.Foc, 0x1af); + public static readonly CommandBarComponentId TacticalSelectButton11 = new(GameEngineType.Foc, 0x1b0); + public static readonly CommandBarComponentId TacticalSelectButton12 = new(GameEngineType.Foc, 0x1b1); + public static readonly CommandBarComponentId TacticalSelectButton13 = new(GameEngineType.Foc, 0x1b2); + public static readonly CommandBarComponentId TacticalSelectButton14 = new(GameEngineType.Foc, 0x1b3); + public static readonly CommandBarComponentId TacticalSelectButton15 = new(GameEngineType.Foc, 0x1b4); + public static readonly CommandBarComponentId TacticalSelectButton16 = new(GameEngineType.Foc, 0x1b5); + public static readonly CommandBarComponentId TacticalSelectButton17 = new(GameEngineType.Foc, 0x1b6); + public static readonly CommandBarComponentId TacticalSelectButton18 = new(GameEngineType.Foc, 0x1b7); + public static readonly CommandBarComponentId TacticalSelectButton19 = new(GameEngineType.Foc, 0x1b8); + public static readonly CommandBarComponentId TacticalSelectButton20 = new(GameEngineType.Foc, 0x1b9); + public static readonly CommandBarComponentId TacticalSelectButton21 = new(GameEngineType.Foc, 0x1ba); + public static readonly CommandBarComponentId TacticalSelectButton22 = new(GameEngineType.Foc, 0x1bb); + public static readonly CommandBarComponentId TacticalSelectButton23 = new(GameEngineType.Foc, 0x1bc); + public static readonly CommandBarComponentId TacticalSelectButton24 = new(GameEngineType.Foc, 0x1bd); + public static readonly CommandBarComponentId TacticalSelectButton25 = new(GameEngineType.Foc, 0x1be); + public static readonly CommandBarComponentId TacticalSelectButton26 = new(GameEngineType.Foc, 0x1bf); + public static readonly CommandBarComponentId TacticalSelectButton27 = new(GameEngineType.Foc, 0x1c0); + public static readonly CommandBarComponentId TacticalSelectButton28 = new(GameEngineType.Foc, 0x1c1); + public static readonly CommandBarComponentId TacticalSelectButton29 = new(GameEngineType.Foc, 0x1c2); + public static readonly CommandBarComponentId TacticalSelectButton30 = new(GameEngineType.Foc, 0x1c3); + public static readonly CommandBarComponentId TacticalSelectButton31 = new(GameEngineType.Foc, 0x1c4); + public static readonly CommandBarComponentId TacticalSelectButton32 = new(GameEngineType.Foc, 0x1c5); + public static readonly CommandBarComponentId TacticalSelectButton33 = new(GameEngineType.Foc, 0x1c6); + public static readonly CommandBarComponentId TacticalSelectButton34 = new(GameEngineType.Foc, 0x1c7); + public static readonly CommandBarComponentId TacticalSelectButton35 = new(GameEngineType.Foc, 0x1c8); + public static readonly CommandBarComponentId TacticalSelectButton36 = new(GameEngineType.Foc, 0x1c9); + public static readonly CommandBarComponentId TacticalSelectButton37 = new(GameEngineType.Foc, 0x1ca); + public static readonly CommandBarComponentId TacticalSelectButton38 = new(GameEngineType.Foc, 0x1cb); + public static readonly CommandBarComponentId TacticalSelectButton39 = new(GameEngineType.Foc, 0x1cc); + public static readonly CommandBarComponentId TacticalSelectButton40 = new(GameEngineType.Foc, 0x1cd); + public static readonly CommandBarComponentId TacticalSelectButton41 = new(GameEngineType.Foc, 0x1ce); + public static readonly CommandBarComponentId TacticalSelectButton42 = new(GameEngineType.Foc, 0x1cf); + public static readonly CommandBarComponentId TacticalSelectButton43 = new(GameEngineType.Foc, 0x1d0); + public static readonly CommandBarComponentId TacticalSelectButton44 = new(GameEngineType.Foc, 0x1d1); + public static readonly CommandBarComponentId TacticalSelectButton45 = new(GameEngineType.Foc, 0x1d2); + public static readonly CommandBarComponentId TacticalSelectButton46 = new(GameEngineType.Foc, 0x1d3); + public static readonly CommandBarComponentId TacticalSelectButton47 = new(GameEngineType.Foc, 0x1d4); + public static readonly CommandBarComponentId TacticalBuildQueue00 = new(GameEngineType.Foc, 0x1d5); + public static readonly CommandBarComponentId TacticalBuildQueue01 = new(GameEngineType.Foc, 0x1d6); + public static readonly CommandBarComponentId TacticalBuildQueue02 = new(GameEngineType.Foc, 0x1d7); + public static readonly CommandBarComponentId TacticalBuildQueue03 = new(GameEngineType.Foc, 0x1d8); + public static readonly CommandBarComponentId TacticalBuildQueue04 = new(GameEngineType.Foc, 0x1d9); + public static readonly CommandBarComponentId TacticalBuildQueue05 = new(GameEngineType.Foc, 0x1da); + public static readonly CommandBarComponentId TacticalBuildQueue06 = new(GameEngineType.Foc, 0x1db); + public static readonly CommandBarComponentId TacticalBuildQueue07 = new(GameEngineType.Foc, 0x1dc); + public static readonly CommandBarComponentId TacticalBuildQueue08 = new(GameEngineType.Foc, 0x1dd); + public static readonly CommandBarComponentId TacticalBuildQueue09 = new(GameEngineType.Foc, 0x1de); + public static readonly CommandBarComponentId TooltipBack = new(GameEngineType.Foc, 0x1df); + public static readonly CommandBarComponentId TooltipName = new(GameEngineType.Foc, 0x1e0); + public static readonly CommandBarComponentId TooltipPrice = new(GameEngineType.Foc, 0x1e1); + public static readonly CommandBarComponentId TooltipIcon = new(GameEngineType.Foc, 0x1e2); + public static readonly CommandBarComponentId TooltipIconLand = new(GameEngineType.Foc, 0x1e3); + public static readonly CommandBarComponentId TooltipLeftJustified = new(GameEngineType.Foc, 0x1e4); + public static readonly CommandBarComponentId EncyclopediaBack = new(GameEngineType.Foc, 0x1e5); + public static readonly CommandBarComponentId EncyclopediaHeaderText = new(GameEngineType.Foc, 0x1e6); + public static readonly CommandBarComponentId EncyclopediaText = new(GameEngineType.Foc, 0x1e7); + public static readonly CommandBarComponentId EncyclopediaRightText = new(GameEngineType.Foc, 0x1e8); + public static readonly CommandBarComponentId EncyclopediaCenterText = new(GameEngineType.Foc, 0x1e9); + public static readonly CommandBarComponentId EncyclopediaIcon = new(GameEngineType.Foc, 0x1ea); + public static readonly CommandBarComponentId EncyclopediaCostText = new(GameEngineType.Foc, 0x1eb); + public static readonly CommandBarComponentId ZoomedBack = new(GameEngineType.Foc, 0x1ec); + public static readonly CommandBarComponentId ZoomedHeaderText = new(GameEngineType.Foc, 0x1ed); + public static readonly CommandBarComponentId ZoomedText = new(GameEngineType.Foc, 0x1ee); + public static readonly CommandBarComponentId ZoomedRightText = new(GameEngineType.Foc, 0x1ef); + public static readonly CommandBarComponentId ZoomedCenterText = new(GameEngineType.Foc, 0x1f0); + public static readonly CommandBarComponentId ZoomedCostText = new(GameEngineType.Foc, 0x1f1); + public static readonly CommandBarComponentId GPlanetFleet = new(GameEngineType.Foc, 0x1f2); + public static readonly CommandBarComponentId GPlanetName = new(GameEngineType.Foc, 0x1f3); + public static readonly CommandBarComponentId GPlanetValue = new(GameEngineType.Foc, 0x1f4); + public static readonly CommandBarComponentId GPoliticalControl = new(GameEngineType.Foc, 0x1f5); + public static readonly CommandBarComponentId GSpaceLevel = new(GameEngineType.Foc, 0x1f6); + public static readonly CommandBarComponentId GSpaceIcon = new(GameEngineType.Foc, 0x1f7); + public static readonly CommandBarComponentId GSpaceLevelPips = new(GameEngineType.Foc, 0x1f8); + public static readonly CommandBarComponentId GGroundLevel = new(GameEngineType.Foc, 0x1f9); + public static readonly CommandBarComponentId GGroundIcon = new(GameEngineType.Foc, 0x1fa); + public static readonly CommandBarComponentId GGroundLevelPips = new(GameEngineType.Foc, 0x1fb); + public static readonly CommandBarComponentId GConflict = new(GameEngineType.Foc, 0x1fc); + public static readonly CommandBarComponentId GHero = new(GameEngineType.Foc, 0x1fd); + public static readonly CommandBarComponentId GEnemyHero = new(GameEngineType.Foc, 0x1fe); + public static readonly CommandBarComponentId GBuild = new(GameEngineType.Foc, 0x1ff); + public static readonly CommandBarComponentId GSmuggler = new(GameEngineType.Foc, 0x200); + public static readonly CommandBarComponentId GBountyHunter = new(GameEngineType.Foc, 0x201); + public static readonly CommandBarComponentId GPlanetLandForces = new(GameEngineType.Foc, 0x202); + public static readonly CommandBarComponentId GGalacticRadarBlip = new(GameEngineType.Foc, 0x203); + public static readonly CommandBarComponentId GGalacticRadarView = new(GameEngineType.Foc, 0x204); + public static readonly CommandBarComponentId GSmuggled = new(GameEngineType.Foc, 0x205); + public static readonly CommandBarComponentId GSpecialAbility = new(GameEngineType.Foc, 0x206); + public static readonly CommandBarComponentId GHeroIcon = new(GameEngineType.Foc, 0x207); + public static readonly CommandBarComponentId GPlanetRing = new(GameEngineType.Foc, 0x208); + public static readonly CommandBarComponentId GWeather = new(GameEngineType.Foc, 0x209); + public static readonly CommandBarComponentId GPlanetAbility = new(GameEngineType.Foc, 0x20a); + public static readonly CommandBarComponentId GCorruptionText = new(GameEngineType.Foc, 0x20b); + public static readonly CommandBarComponentId GCorruptionIcon = new(GameEngineType.Foc, 0x20c); + public static readonly CommandBarComponentId TutorialText = new(GameEngineType.Foc, 0x20d); + public static readonly CommandBarComponentId TutorialTextBack = new(GameEngineType.Foc, 0x20e); + public static readonly CommandBarComponentId RadarBlip = new(GameEngineType.Foc, 0x20f); + public static readonly CommandBarComponentId TacticalBuildButtonShell = new(GameEngineType.Foc, 0x210); + public static readonly CommandBarComponentId TacticalBuildButton0 = new(GameEngineType.Foc, 0x211); + public static readonly CommandBarComponentId TacticalBuildButton1 = new(GameEngineType.Foc, 0x212); + public static readonly CommandBarComponentId TacticalBuildButton2 = new(GameEngineType.Foc, 0x213); + public static readonly CommandBarComponentId TacticalBuildButton3 = new(GameEngineType.Foc, 0x214); + public static readonly CommandBarComponentId TacticalBuildButton4 = new(GameEngineType.Foc, 0x215); + public static readonly CommandBarComponentId TacticalBuildButton5 = new(GameEngineType.Foc, 0x216); + public static readonly CommandBarComponentId TacticalSellButton = new(GameEngineType.Foc, 0x217); + public static readonly CommandBarComponentId ReinforcementShell = new(GameEngineType.Foc, 0x218); + public static readonly CommandBarComponentId ReinforcementCancel = new(GameEngineType.Foc, 0x219); + public static readonly CommandBarComponentId ReinforcementSlot00 = new(GameEngineType.Foc, 0x21a); + public static readonly CommandBarComponentId ReinforcementSlot01 = new(GameEngineType.Foc, 0x21b); + public static readonly CommandBarComponentId ReinforcementSlot02 = new(GameEngineType.Foc, 0x21c); + public static readonly CommandBarComponentId ReinforcementSlot03 = new(GameEngineType.Foc, 0x21d); + public static readonly CommandBarComponentId ReinforcementSlot04 = new(GameEngineType.Foc, 0x21e); + public static readonly CommandBarComponentId ReinforcementSlot05 = new(GameEngineType.Foc, 0x21f); + public static readonly CommandBarComponentId ReinforcementSlot06 = new(GameEngineType.Foc, 0x220); + public static readonly CommandBarComponentId ReinforcementSlot07 = new(GameEngineType.Foc, 0x221); + public static readonly CommandBarComponentId ReinforcementSlot08 = new(GameEngineType.Foc, 0x222); + public static readonly CommandBarComponentId ReinforcementSlot09 = new(GameEngineType.Foc, 0x223); + public static readonly CommandBarComponentId ReinforcementSlot10 = new(GameEngineType.Foc, 0x224); + public static readonly CommandBarComponentId ReinforcementSlot11 = new(GameEngineType.Foc, 0x225); + public static readonly CommandBarComponentId ReinforcementSlot12 = new(GameEngineType.Foc, 0x226); + public static readonly CommandBarComponentId ReinforcementSlot13 = new(GameEngineType.Foc, 0x227); + public static readonly CommandBarComponentId ReinforcementSlot14 = new(GameEngineType.Foc, 0x228); + public static readonly CommandBarComponentId ReinforcementSlot15 = new(GameEngineType.Foc, 0x229); + public static readonly CommandBarComponentId ReinforcementSlot16 = new(GameEngineType.Foc, 0x22a); + public static readonly CommandBarComponentId ReinforcementSlot17 = new(GameEngineType.Foc, 0x22b); + public static readonly CommandBarComponentId ReinforcementSlot18 = new(GameEngineType.Foc, 0x22c); + public static readonly CommandBarComponentId ReinforcementSlot19 = new(GameEngineType.Foc, 0x22d); + public static readonly CommandBarComponentId ReinforcementCap2 = new(GameEngineType.Foc, 0x22e); + public static readonly CommandBarComponentId ReinforcementCap2Text = new(GameEngineType.Foc, 0x22f); + public static readonly CommandBarComponentId ReinforcementCounter = new(GameEngineType.Foc, 0x230); + public static readonly CommandBarComponentId GarrisonRespawnCounter = new(GameEngineType.Foc, 0x231); + public static readonly CommandBarComponentId SkirmishUpgrade = new(GameEngineType.Foc, 0x232); + public static readonly CommandBarComponentId PendingBattleShell = new(GameEngineType.Foc, 0x233); + public static readonly CommandBarComponentId PendingBattleText = new(GameEngineType.Foc, 0x234); + public static readonly CommandBarComponentId PendingBattleButton = new(GameEngineType.Foc, 0x235); + public static readonly CommandBarComponentId PendingBattleAutoresolve = new(GameEngineType.Foc, 0x236); + public static readonly CommandBarComponentId PendingBattleGraphLeft = new(GameEngineType.Foc, 0x237); + public static readonly CommandBarComponentId PendingBattleGraphRight = new(GameEngineType.Foc, 0x238); + public static readonly CommandBarComponentId TatcicalAutoresolveShell = new(GameEngineType.Foc, 0x239); + public static readonly CommandBarComponentId TacticalAutoresolveButton = new(GameEngineType.Foc, 0x23a); + public static readonly CommandBarComponentId TacticalAutoresolveGraphLeft = new(GameEngineType.Foc, 0x23b); + public static readonly CommandBarComponentId TacticalAutoresolveGraphRight = new(GameEngineType.Foc, 0x23c); + public static readonly CommandBarComponentId ObjectiveBack = new(GameEngineType.Foc, 0x23d); + public static readonly CommandBarComponentId ObjectiveHeaderText = new(GameEngineType.Foc, 0x23e); + public static readonly CommandBarComponentId ObjectiveText = new(GameEngineType.Foc, 0x23f); + public static readonly CommandBarComponentId ObjectiveIcon = new(GameEngineType.Foc, 0x240); + public static readonly CommandBarComponentId GuiDialogTooltip = new(GameEngineType.Foc, 0x241); + public static readonly CommandBarComponentId TargetUnitTypeShell = new(GameEngineType.Foc, 0x242); + public static readonly CommandBarComponentId TargetUnitTypeTitle = new(GameEngineType.Foc, 0x243); + public static readonly CommandBarComponentId TargetUnitTypeCancel = new(GameEngineType.Foc, 0x244); + public static readonly CommandBarComponentId TargetUnitTypeDescription = new(GameEngineType.Foc, 0x245); + public static readonly CommandBarComponentId TargetUnitTypeSlot00 = new(GameEngineType.Foc, 0x246); + public static readonly CommandBarComponentId TargetUnitTypeSlot01 = new(GameEngineType.Foc, 0x247); + public static readonly CommandBarComponentId TargetUnitTypeSlot02 = new(GameEngineType.Foc, 0x248); + public static readonly CommandBarComponentId TargetUnitTypeSlot03 = new(GameEngineType.Foc, 0x249); + public static readonly CommandBarComponentId TargetUnitTypeSlot04 = new(GameEngineType.Foc, 0x24a); + public static readonly CommandBarComponentId TargetUnitTypeSlot05 = new(GameEngineType.Foc, 0x24b); + public static readonly CommandBarComponentId TargetUnitTypeSlot06 = new(GameEngineType.Foc, 0x24c); + public static readonly CommandBarComponentId TargetUnitTypeSlot07 = new(GameEngineType.Foc, 0x24d); + public static readonly CommandBarComponentId TargetUnitTypeSlot08 = new(GameEngineType.Foc, 0x24e); + public static readonly CommandBarComponentId TargetUnitTypeSlot09 = new(GameEngineType.Foc, 0x24f); + public static readonly CommandBarComponentId TargetUnitTypeSlot10 = new(GameEngineType.Foc, 0x250); + public static readonly CommandBarComponentId TargetUnitTypeSlot11 = new(GameEngineType.Foc, 0x251); + public static readonly CommandBarComponentId TargetUnitTypeName00 = new(GameEngineType.Foc, 0x252); + public static readonly CommandBarComponentId TargetUnitTypeName01 = new(GameEngineType.Foc, 0x253); + public static readonly CommandBarComponentId TargetUnitTypeName02 = new(GameEngineType.Foc, 0x254); + public static readonly CommandBarComponentId TargetUnitTypeName03 = new(GameEngineType.Foc, 0x255); + public static readonly CommandBarComponentId TargetUnitTypeName04 = new(GameEngineType.Foc, 0x256); + public static readonly CommandBarComponentId TargetUnitTypeName05 = new(GameEngineType.Foc, 0x257); + public static readonly CommandBarComponentId TargetUnitTypeName06 = new(GameEngineType.Foc, 0x258); + public static readonly CommandBarComponentId TargetUnitTypeName07 = new(GameEngineType.Foc, 0x259); + public static readonly CommandBarComponentId TargetUnitTypeName08 = new(GameEngineType.Foc, 0x25a); + public static readonly CommandBarComponentId TargetUnitTypeName09 = new(GameEngineType.Foc, 0x25b); + public static readonly CommandBarComponentId TargetUnitTypeName10 = new(GameEngineType.Foc, 0x25c); + public static readonly CommandBarComponentId TargetUnitTypeName11 = new(GameEngineType.Foc, 0x25d); + public static readonly CommandBarComponentId TargetUnitTypePrice00 = new(GameEngineType.Foc, 0x25e); + public static readonly CommandBarComponentId TargetUnitTypePrice01 = new(GameEngineType.Foc, 0x25f); + public static readonly CommandBarComponentId TargetUnitTypePrice02 = new(GameEngineType.Foc, 0x260); + public static readonly CommandBarComponentId TargetUnitTypePrice03 = new(GameEngineType.Foc, 0x261); + public static readonly CommandBarComponentId TargetUnitTypePrice04 = new(GameEngineType.Foc, 0x262); + public static readonly CommandBarComponentId TargetUnitTypePrice05 = new(GameEngineType.Foc, 0x263); + public static readonly CommandBarComponentId TargetUnitTypePrice06 = new(GameEngineType.Foc, 0x264); + public static readonly CommandBarComponentId TargetUnitTypePrice07 = new(GameEngineType.Foc, 0x265); + public static readonly CommandBarComponentId TargetUnitTypePrice08 = new(GameEngineType.Foc, 0x266); + public static readonly CommandBarComponentId TargetUnitTypePrice09 = new(GameEngineType.Foc, 0x267); + public static readonly CommandBarComponentId TargetUnitTypePrice10 = new(GameEngineType.Foc, 0x268); + public static readonly CommandBarComponentId TargetUnitTypePrice11 = new(GameEngineType.Foc, 0x269); + public static readonly CommandBarComponentId VcrButtonPlayPause = new(GameEngineType.Foc, 0x26a); + public static readonly CommandBarComponentId VcrButtonFastForward = new(GameEngineType.Foc, 0x26b); + public static readonly CommandBarComponentId VcrButtonFastForwardTactical = new(GameEngineType.Foc, 0x26c); + public static readonly CommandBarComponentId VcrButtonPlayPauseTactical = new(GameEngineType.Foc, 0x26d); + public static readonly CommandBarComponentId AdvisorHintPopupGalactic = new(GameEngineType.Foc, 0x26e); + public static readonly CommandBarComponentId AdvisorHintPopupTactical = new(GameEngineType.Foc, 0x26f); + public static readonly CommandBarComponentId AdvisorHintBack = new(GameEngineType.Foc, 0x270); + public static readonly CommandBarComponentId TextOrganizeFleet00 = new(GameEngineType.Foc, 0x271); + public static readonly CommandBarComponentId TextOrganizeFleet01 = new(GameEngineType.Foc, 0x272); + public static readonly CommandBarComponentId TextOrganizeFleet02 = new(GameEngineType.Foc, 0x273); + public static readonly CommandBarComponentId IconOrganizeFleet00 = new(GameEngineType.Foc, 0x274); + public static readonly CommandBarComponentId IconOrganizeFleet01 = new(GameEngineType.Foc, 0x275); + public static readonly CommandBarComponentId IconOrganizeFleet02 = new(GameEngineType.Foc, 0x276); + public static readonly CommandBarComponentId TextOrganizeLandFleet = new(GameEngineType.Foc, 0x277); + public static readonly CommandBarComponentId CsAbilityButton = new(GameEngineType.Foc, 0x278); + public static readonly CommandBarComponentId CsAbilityText = new(GameEngineType.Foc, 0x279); + public static readonly CommandBarComponentId MovieBoneGalactic = new(GameEngineType.Foc, 0x27a); + public static readonly CommandBarComponentId MovieBoneTactical = new(GameEngineType.Foc, 0x27b); + public static readonly CommandBarComponentId GenericCollision = new(GameEngineType.Foc, 0x27c); + public static readonly CommandBarComponentId GoodHeroShell = new(GameEngineType.Foc, 0x27d); + public static readonly CommandBarComponentId GoodHeroSlot00 = new(GameEngineType.Foc, 0x27e); + public static readonly CommandBarComponentId GoodHeroSlot01 = new(GameEngineType.Foc, 0x27f); + public static readonly CommandBarComponentId GoodHeroSlot02 = new(GameEngineType.Foc, 0x280); + public static readonly CommandBarComponentId GoodHeroSlot03 = new(GameEngineType.Foc, 0x281); + public static readonly CommandBarComponentId GoodHeroSlot04 = new(GameEngineType.Foc, 0x282); + public static readonly CommandBarComponentId GoodHeroSlot05 = new(GameEngineType.Foc, 0x283); + public static readonly CommandBarComponentId GoodHeroSlot06 = new(GameEngineType.Foc, 0x284); + public static readonly CommandBarComponentId GoodHeroSlot07 = new(GameEngineType.Foc, 0x285); + public static readonly CommandBarComponentId GoodHeroSlot08 = new(GameEngineType.Foc, 0x286); + public static readonly CommandBarComponentId GoodHeroSlot09 = new(GameEngineType.Foc, 0x287); + public static readonly CommandBarComponentId GoodHeroSlot10 = new(GameEngineType.Foc, 0x288); + public static readonly CommandBarComponentId GoodHeroHealth00 = new(GameEngineType.Foc, 0x289); + public static readonly CommandBarComponentId GoodHeroHealth01 = new(GameEngineType.Foc, 0x28a); + public static readonly CommandBarComponentId GoodHeroHealth02 = new(GameEngineType.Foc, 0x28b); + public static readonly CommandBarComponentId GoodHeroHealth03 = new(GameEngineType.Foc, 0x28c); + public static readonly CommandBarComponentId GoodHeroHealth04 = new(GameEngineType.Foc, 0x28d); + public static readonly CommandBarComponentId GoodHeroHealth05 = new(GameEngineType.Foc, 0x28e); + public static readonly CommandBarComponentId GoodHeroHealth06 = new(GameEngineType.Foc, 0x28f); + public static readonly CommandBarComponentId GoodHeroHealth07 = new(GameEngineType.Foc, 0x290); + public static readonly CommandBarComponentId GoodHeroHealth08 = new(GameEngineType.Foc, 0x291); + public static readonly CommandBarComponentId GoodHeroHealth09 = new(GameEngineType.Foc, 0x292); + public static readonly CommandBarComponentId GoodHeroHealth10 = new(GameEngineType.Foc, 0x293); + public static readonly CommandBarComponentId EvilHeroShell = new(GameEngineType.Foc, 0x294); + public static readonly CommandBarComponentId EvilHeroSlot00 = new(GameEngineType.Foc, 0x295); + public static readonly CommandBarComponentId EvilHeroSlot01 = new(GameEngineType.Foc, 0x296); + public static readonly CommandBarComponentId EvilHeroSlot02 = new(GameEngineType.Foc, 0x297); + public static readonly CommandBarComponentId EvilHeroSlot03 = new(GameEngineType.Foc, 0x298); + public static readonly CommandBarComponentId EvilHeroSlot04 = new(GameEngineType.Foc, 0x299); + public static readonly CommandBarComponentId EvilHeroSlot05 = new(GameEngineType.Foc, 0x29a); + public static readonly CommandBarComponentId EvilHeroSlot06 = new(GameEngineType.Foc, 0x29b); + public static readonly CommandBarComponentId EvilHeroSlot07 = new(GameEngineType.Foc, 0x29c); + public static readonly CommandBarComponentId EvilHeroSlot08 = new(GameEngineType.Foc, 0x29d); + public static readonly CommandBarComponentId EvilHeroSlot09 = new(GameEngineType.Foc, 0x29e); + public static readonly CommandBarComponentId EvilHeroSlot10 = new(GameEngineType.Foc, 0x29f); + public static readonly CommandBarComponentId EvilHeroHealth00 = new(GameEngineType.Foc, 0x2a0); + public static readonly CommandBarComponentId EvilHeroHealth01 = new(GameEngineType.Foc, 0x2a1); + public static readonly CommandBarComponentId EvilHeroHealth02 = new(GameEngineType.Foc, 0x2a2); + public static readonly CommandBarComponentId EvilHeroHealth03 = new(GameEngineType.Foc, 0x2a3); + public static readonly CommandBarComponentId EvilHeroHealth04 = new(GameEngineType.Foc, 0x2a4); + public static readonly CommandBarComponentId EvilHeroHealth05 = new(GameEngineType.Foc, 0x2a5); + public static readonly CommandBarComponentId EvilHeroHealth06 = new(GameEngineType.Foc, 0x2a6); + public static readonly CommandBarComponentId EvilHeroHealth07 = new(GameEngineType.Foc, 0x2a7); + public static readonly CommandBarComponentId EvilHeroHealth08 = new(GameEngineType.Foc, 0x2a8); + public static readonly CommandBarComponentId EvilHeroHealth09 = new(GameEngineType.Foc, 0x2a9); + public static readonly CommandBarComponentId EvilHeroHealth10 = new(GameEngineType.Foc, 0x2aa); + public static readonly CommandBarComponentId PauseShell = new(GameEngineType.Foc, 0x2ab); + public static readonly CommandBarComponentId PauseText = new(GameEngineType.Foc, 0x2ac); + public static readonly CommandBarComponentId PauseButton = new(GameEngineType.Foc, 0x2ad); + public static readonly CommandBarComponentId StoryCampaignPendingBattleShell = new(GameEngineType.Foc, 0x2ae); + public static readonly CommandBarComponentId StoryCampaignPendingBattleText = new(GameEngineType.Foc, 0x2af); + public static readonly CommandBarComponentId StoryCampaignPendingBattleButton = new(GameEngineType.Foc, 0x2b0); + public static readonly CommandBarComponentId MapActivate = new(GameEngineType.Foc, 0x2b1); + public static readonly CommandBarComponentId MapShell = new(GameEngineType.Foc, 0x2b2); + public static readonly CommandBarComponentId MapOverlayShell = new(GameEngineType.Foc, 0x2b3); + public static readonly CommandBarComponentId MapGroundForcesText = new(GameEngineType.Foc, 0x2b4); + public static readonly CommandBarComponentId MapDragDropText = new(GameEngineType.Foc, 0x2b5); + public static readonly CommandBarComponentId MapUnitCapText = new(GameEngineType.Foc, 0x2b6); + public static readonly CommandBarComponentId MapUnitCapNumber = new(GameEngineType.Foc, 0x2b7); + public static readonly CommandBarComponentId MapBuildingCapText = new(GameEngineType.Foc, 0x2b8); + public static readonly CommandBarComponentId MapBuildingCapNumber = new(GameEngineType.Foc, 0x2b9); + public static readonly CommandBarComponentId MapFactionIcon = new(GameEngineType.Foc, 0x2ba); + public static readonly CommandBarComponentId MapPlanetNameText = new(GameEngineType.Foc, 0x2bb); + public static readonly CommandBarComponentId MapPlanetIncomeNumber = new(GameEngineType.Foc, 0x2bc); + public static readonly CommandBarComponentId MapDayCounterText = new(GameEngineType.Foc, 0x2bd); + public static readonly CommandBarComponentId MapDayCounter = new(GameEngineType.Foc, 0x2be); + public static readonly CommandBarComponentId MapBackButton = new(GameEngineType.Foc, 0x2bf); + public static readonly CommandBarComponentId MapWeatherIcon = new(GameEngineType.Foc, 0x2c0); + public static readonly CommandBarComponentId MapPlanetBonusIcon = new(GameEngineType.Foc, 0x2c1); + public static readonly CommandBarComponentId MapRadarMap = new(GameEngineType.Foc, 0x2c2); + public static readonly CommandBarComponentId MapZoomOut = new(GameEngineType.Foc, 0x2c3); + public static readonly CommandBarComponentId MapEncyclopediaPopup = new(GameEngineType.Foc, 0x2c4); + public static readonly CommandBarComponentId AdvancedMapFilter0 = new(GameEngineType.Foc, 0x2c5); + public static readonly CommandBarComponentId AdvancedMapFilter1 = new(GameEngineType.Foc, 0x2c6); + public static readonly CommandBarComponentId AdvancedMapFilter2 = new(GameEngineType.Foc, 0x2c7); + public static readonly CommandBarComponentId AdvancedMapFilter3 = new(GameEngineType.Foc, 0x2c8); + public static readonly CommandBarComponentId AdvancedMapOptions = new(GameEngineType.Foc, 0x2c9); + public static readonly CommandBarComponentId MapMainMap = new(GameEngineType.Foc, 0x2ca); + public static readonly CommandBarComponentId MapSpaceUnit00 = new(GameEngineType.Foc, 0x2cb); + public static readonly CommandBarComponentId MapSpaceUnit01 = new(GameEngineType.Foc, 0x2cc); + public static readonly CommandBarComponentId MapSpaceUnit02 = new(GameEngineType.Foc, 0x2cd); + public static readonly CommandBarComponentId MapSpaceUnit03 = new(GameEngineType.Foc, 0x2ce); + public static readonly CommandBarComponentId MapSpaceUnit04 = new(GameEngineType.Foc, 0x2cf); + public static readonly CommandBarComponentId MapSpaceUnit05 = new(GameEngineType.Foc, 0x2d0); + public static readonly CommandBarComponentId MapSpaceUnit06 = new(GameEngineType.Foc, 0x2d1); + public static readonly CommandBarComponentId MapSpaceUnit07 = new(GameEngineType.Foc, 0x2d2); + public static readonly CommandBarComponentId MapSpaceUnit08 = new(GameEngineType.Foc, 0x2d3); + public static readonly CommandBarComponentId MapSpaceUnit09 = new(GameEngineType.Foc, 0x2d4); + public static readonly CommandBarComponentId MapSpaceUnit10 = new(GameEngineType.Foc, 0x2d5); + public static readonly CommandBarComponentId MapSpaceUnit11 = new(GameEngineType.Foc, 0x2d6); + public static readonly CommandBarComponentId MapSpaceUnit12 = new(GameEngineType.Foc, 0x2d7); + public static readonly CommandBarComponentId MapSpaceUnit13 = new(GameEngineType.Foc, 0x2d8); + public static readonly CommandBarComponentId MapSpaceUnit14 = new(GameEngineType.Foc, 0x2d9); + public static readonly CommandBarComponentId MapSpaceUnit15 = new(GameEngineType.Foc, 0x2da); + public static readonly CommandBarComponentId MapSpaceUnit16 = new(GameEngineType.Foc, 0x2db); + public static readonly CommandBarComponentId MapSpaceUnit17 = new(GameEngineType.Foc, 0x2dc); + public static readonly CommandBarComponentId MapSpaceUnit18 = new(GameEngineType.Foc, 0x2dd); + public static readonly CommandBarComponentId MapSpaceUnit19 = new(GameEngineType.Foc, 0x2de); + public static readonly CommandBarComponentId MapBuildPad00 = new(GameEngineType.Foc, 0x2df); + public static readonly CommandBarComponentId MapBuildPad01 = new(GameEngineType.Foc, 0x2e0); + public static readonly CommandBarComponentId MapBuildPad02 = new(GameEngineType.Foc, 0x2e1); + public static readonly CommandBarComponentId MapBuildPad03 = new(GameEngineType.Foc, 0x2e2); + public static readonly CommandBarComponentId MapBuildPad04 = new(GameEngineType.Foc, 0x2e3); + public static readonly CommandBarComponentId MapBuildPad05 = new(GameEngineType.Foc, 0x2e4); + public static readonly CommandBarComponentId MapBuildPad06 = new(GameEngineType.Foc, 0x2e5); + public static readonly CommandBarComponentId MapBuildPad07 = new(GameEngineType.Foc, 0x2e6); + public static readonly CommandBarComponentId MapBuildPad08 = new(GameEngineType.Foc, 0x2e7); + public static readonly CommandBarComponentId MapBuildPad09 = new(GameEngineType.Foc, 0x2e8); + public static readonly CommandBarComponentId MapBuildPad10 = new(GameEngineType.Foc, 0x2e9); + public static readonly CommandBarComponentId MapBuildPad11 = new(GameEngineType.Foc, 0x2ea); + public static readonly CommandBarComponentId MapBuildPad12 = new(GameEngineType.Foc, 0x2eb); + public static readonly CommandBarComponentId MapBuildPad13 = new(GameEngineType.Foc, 0x2ec); + public static readonly CommandBarComponentId MapBuildPad14 = new(GameEngineType.Foc, 0x2ed); + public static readonly CommandBarComponentId MapBuildPad15 = new(GameEngineType.Foc, 0x2ee); + public static readonly CommandBarComponentId MapBuildPad16 = new(GameEngineType.Foc, 0x2ef); + public static readonly CommandBarComponentId MapBuildPad17 = new(GameEngineType.Foc, 0x2f0); + public static readonly CommandBarComponentId MapBuildPad18 = new(GameEngineType.Foc, 0x2f1); + public static readonly CommandBarComponentId MapBuildPad19 = new(GameEngineType.Foc, 0x2f2); + public static readonly CommandBarComponentId MapBuildPad20 = new(GameEngineType.Foc, 0x2f3); + public static readonly CommandBarComponentId MapBuildPad21 = new(GameEngineType.Foc, 0x2f4); + public static readonly CommandBarComponentId MapBuildPad22 = new(GameEngineType.Foc, 0x2f5); + public static readonly CommandBarComponentId MapBuildPad23 = new(GameEngineType.Foc, 0x2f6); + public static readonly CommandBarComponentId MapBuildPad24 = new(GameEngineType.Foc, 0x2f7); + public static readonly CommandBarComponentId MapBuildPad25 = new(GameEngineType.Foc, 0x2f8); + public static readonly CommandBarComponentId MapBuildPad26 = new(GameEngineType.Foc, 0x2f9); + public static readonly CommandBarComponentId MapBuildPad27 = new(GameEngineType.Foc, 0x2fa); + public static readonly CommandBarComponentId MapBuildPad28 = new(GameEngineType.Foc, 0x2fb); + public static readonly CommandBarComponentId MapBuildPad29 = new(GameEngineType.Foc, 0x2fc); + public static readonly CommandBarComponentId MapBuildPad30 = new(GameEngineType.Foc, 0x2fd); + public static readonly CommandBarComponentId MapBuildPad31 = new(GameEngineType.Foc, 0x2fe); + public static readonly CommandBarComponentId MapBuildPad32 = new(GameEngineType.Foc, 0x2ff); + public static readonly CommandBarComponentId MapReinforcePad00 = new(GameEngineType.Foc, 0x300); + public static readonly CommandBarComponentId MapReinforcePad01 = new(GameEngineType.Foc, 0x301); + public static readonly CommandBarComponentId MapReinforcePad02 = new(GameEngineType.Foc, 0x302); + public static readonly CommandBarComponentId MapReinforcePad03 = new(GameEngineType.Foc, 0x303); + public static readonly CommandBarComponentId MapReinforcePad04 = new(GameEngineType.Foc, 0x304); + public static readonly CommandBarComponentId MapReinforcePad05 = new(GameEngineType.Foc, 0x305); + public static readonly CommandBarComponentId MapReinforcePad06 = new(GameEngineType.Foc, 0x306); + public static readonly CommandBarComponentId MapBunkerAndBuildPad00 = new(GameEngineType.Foc, 0x307); + public static readonly CommandBarComponentId MapBunkerAndBuildPad01 = new(GameEngineType.Foc, 0x308); + public static readonly CommandBarComponentId MapBunkerAndBuildPad02 = new(GameEngineType.Foc, 0x309); + public static readonly CommandBarComponentId MapBunkerAndBuildPad03 = new(GameEngineType.Foc, 0x30a); + public static readonly CommandBarComponentId MapBunkerAndBuildPad04 = new(GameEngineType.Foc, 0x30b); + public static readonly CommandBarComponentId MapBunkerAndBuildPad05 = new(GameEngineType.Foc, 0x30c); + public static readonly CommandBarComponentId MapBunkerAndBuildPad06 = new(GameEngineType.Foc, 0x30d); + public static readonly CommandBarComponentId MapBunkerAndBuildPad07 = new(GameEngineType.Foc, 0x30e); + public static readonly CommandBarComponentId MapBunkerAndBuildPad08 = new(GameEngineType.Foc, 0x30f); + public static readonly CommandBarComponentId MapBunkerAndBuildPad09 = new(GameEngineType.Foc, 0x310); + public static readonly CommandBarComponentId MapBunkerAndBuildPad10 = new(GameEngineType.Foc, 0x311); + public static readonly CommandBarComponentId MapBunkerAndBuildPad11 = new(GameEngineType.Foc, 0x312); + public static readonly CommandBarComponentId MapBunkerAndBuildPad12 = new(GameEngineType.Foc, 0x313); + public static readonly CommandBarComponentId MapBunkerAndBuildPad13 = new(GameEngineType.Foc, 0x314); + public static readonly CommandBarComponentId MapBunkerAndBuildPad14 = new(GameEngineType.Foc, 0x315); + public static readonly CommandBarComponentId MapBunkerAndBuildPad15 = new(GameEngineType.Foc, 0x316); + public static readonly CommandBarComponentId MapBunkerAndBuildPad16 = new(GameEngineType.Foc, 0x317); + public static readonly CommandBarComponentId MapBunkerAndBuildPad17 = new(GameEngineType.Foc, 0x318); + public static readonly CommandBarComponentId MapBunkerAndBuildPad18 = new(GameEngineType.Foc, 0x319); + public static readonly CommandBarComponentId MapBunkerAndBuildPad19 = new(GameEngineType.Foc, 0x31a); + public static readonly CommandBarComponentId MapBunkerAndBuildPad20 = new(GameEngineType.Foc, 0x31b); + public static readonly CommandBarComponentId MapBunkerAndBuildPad21 = new(GameEngineType.Foc, 0x31c); + public static readonly CommandBarComponentId MapBunkerAndBuildPad22 = new(GameEngineType.Foc, 0x31d); + public static readonly CommandBarComponentId MapBunkerAndBuildPad23 = new(GameEngineType.Foc, 0x31e); + public static readonly CommandBarComponentId MapBunkerAndBuildPad24 = new(GameEngineType.Foc, 0x31f); + public static readonly CommandBarComponentId MapBunkerAndBuildPad25 = new(GameEngineType.Foc, 0x320); + public static readonly CommandBarComponentId MapBunkerAndBuildPad26 = new(GameEngineType.Foc, 0x321); + public static readonly CommandBarComponentId MapBunkerAndBuildPad27 = new(GameEngineType.Foc, 0x322); + public static readonly CommandBarComponentId MapBunkerAndBuildPad28 = new(GameEngineType.Foc, 0x323); + public static readonly CommandBarComponentId MapBunkerAndBuildPad29 = new(GameEngineType.Foc, 0x324); + public static readonly CommandBarComponentId MapBunkerAndBuildPad30 = new(GameEngineType.Foc, 0x325); + public static readonly CommandBarComponentId MapBunkerAndBuildPad31 = new(GameEngineType.Foc, 0x326); + public static readonly CommandBarComponentId MapBunkerAndBuildPad32 = new(GameEngineType.Foc, 0x327); + public static readonly CommandBarComponentId MapBunkerAndBuildPad33 = new(GameEngineType.Foc, 0x328); + public static readonly CommandBarComponentId MapBunkerAndBuildPad34 = new(GameEngineType.Foc, 0x329); + public static readonly CommandBarComponentId MapBunkerAndBuildPad35 = new(GameEngineType.Foc, 0x32a); + public static readonly CommandBarComponentId MapBunkerAndBuildPad36 = new(GameEngineType.Foc, 0x32b); + public static readonly CommandBarComponentId MapBunkerAndBuildPad37 = new(GameEngineType.Foc, 0x32c); + public static readonly CommandBarComponentId MapBunkerAndBuildPad38 = new(GameEngineType.Foc, 0x32d); + public static readonly CommandBarComponentId MapBunkerAndBuildPad39 = new(GameEngineType.Foc, 0x32e); + public static readonly CommandBarComponentId MapUnitPad00 = new(GameEngineType.Foc, 0x32f); + public static readonly CommandBarComponentId MapUnitPad01 = new(GameEngineType.Foc, 0x330); + public static readonly CommandBarComponentId MapUnitPad02 = new(GameEngineType.Foc, 0x331); + public static readonly CommandBarComponentId MapUnitPad03 = new(GameEngineType.Foc, 0x332); + public static readonly CommandBarComponentId MapUnitPad04 = new(GameEngineType.Foc, 0x333); + public static readonly CommandBarComponentId MapUnitPad05 = new(GameEngineType.Foc, 0x334); + public static readonly CommandBarComponentId MapUnitPad06 = new(GameEngineType.Foc, 0x335); + public static readonly CommandBarComponentId MapUnitPad07 = new(GameEngineType.Foc, 0x336); + public static readonly CommandBarComponentId MapUnitPad08 = new(GameEngineType.Foc, 0x337); + public static readonly CommandBarComponentId MapUnitPad09 = new(GameEngineType.Foc, 0x338); + public static readonly CommandBarComponentId BribeDisplay = new(GameEngineType.Foc, 0x339); + public static readonly CommandBarComponentId BlackMarketShell = new(GameEngineType.Foc, 0x33a); + public static readonly CommandBarComponentId BlackMarketTitle = new(GameEngineType.Foc, 0x33b); + public static readonly CommandBarComponentId BlackMarketPlanet = new(GameEngineType.Foc, 0x33c); + public static readonly CommandBarComponentId BlackMarketTechLevelHeader = new(GameEngineType.Foc, 0x33d); + public static readonly CommandBarComponentId BlackMarketSlotHeader = new(GameEngineType.Foc, 0x33e); + public static readonly CommandBarComponentId BlackMarketCancel = new(GameEngineType.Foc, 0x33f); + public static readonly CommandBarComponentId BlackMarketEncyclopedia = new(GameEngineType.Foc, 0x340); + public static readonly CommandBarComponentId BlackMarketTechLevel00 = new(GameEngineType.Foc, 0x341); + public static readonly CommandBarComponentId BlackMarketTechLevel01 = new(GameEngineType.Foc, 0x342); + public static readonly CommandBarComponentId BlackMarketTechLevel02 = new(GameEngineType.Foc, 0x343); + public static readonly CommandBarComponentId BlackMarketTechLevel03 = new(GameEngineType.Foc, 0x344); + public static readonly CommandBarComponentId BlackMarketTechLevel04 = new(GameEngineType.Foc, 0x345); + public static readonly CommandBarComponentId BlackMarketSlot00 = new(GameEngineType.Foc, 0x346); + public static readonly CommandBarComponentId BlackMarketSlot01 = new(GameEngineType.Foc, 0x347); + public static readonly CommandBarComponentId BlackMarketSlot02 = new(GameEngineType.Foc, 0x348); + public static readonly CommandBarComponentId BlackMarketSlot03 = new(GameEngineType.Foc, 0x349); + public static readonly CommandBarComponentId BlackMarketSlot04 = new(GameEngineType.Foc, 0x34a); + public static readonly CommandBarComponentId BlackMarketSlotDescription00 = new(GameEngineType.Foc, 0x34b); + public static readonly CommandBarComponentId BlackMarketSlotDescription01 = new(GameEngineType.Foc, 0x34c); + public static readonly CommandBarComponentId BlackMarketSlotDescription02 = new(GameEngineType.Foc, 0x34d); + public static readonly CommandBarComponentId BlackMarketSlotDescription03 = new(GameEngineType.Foc, 0x34e); + public static readonly CommandBarComponentId BlackMarketSlotDescription04 = new(GameEngineType.Foc, 0x34f); + public static readonly CommandBarComponentId BlackMarketSlotPrice00 = new(GameEngineType.Foc, 0x350); + public static readonly CommandBarComponentId BlackMarketSlotPrice01 = new(GameEngineType.Foc, 0x351); + public static readonly CommandBarComponentId BlackMarketSlotPrice02 = new(GameEngineType.Foc, 0x352); + public static readonly CommandBarComponentId BlackMarketSlotPrice03 = new(GameEngineType.Foc, 0x353); + public static readonly CommandBarComponentId BlackMarketSlotPrice04 = new(GameEngineType.Foc, 0x354); + public static readonly CommandBarComponentId SabotageShell = new(GameEngineType.Foc, 0x355); + public static readonly CommandBarComponentId SabotageTitle = new(GameEngineType.Foc, 0x356); + public static readonly CommandBarComponentId SabotageTitle2 = new(GameEngineType.Foc, 0x357); + public static readonly CommandBarComponentId SabotageTechLevel = new(GameEngineType.Foc, 0x358); + public static readonly CommandBarComponentId SabotageCancel = new(GameEngineType.Foc, 0x359); + public static readonly CommandBarComponentId SabotageDescription = new(GameEngineType.Foc, 0x35a); + public static readonly CommandBarComponentId SabotageSlot00 = new(GameEngineType.Foc, 0x35b); + public static readonly CommandBarComponentId SabotageSlot01 = new(GameEngineType.Foc, 0x35c); + public static readonly CommandBarComponentId SabotageSlot02 = new(GameEngineType.Foc, 0x35d); + public static readonly CommandBarComponentId SabotageSlot03 = new(GameEngineType.Foc, 0x35e); + public static readonly CommandBarComponentId SabotageSlot04 = new(GameEngineType.Foc, 0x35f); + public static readonly CommandBarComponentId SabotageSlot05 = new(GameEngineType.Foc, 0x360); + public static readonly CommandBarComponentId SabotageSlot06 = new(GameEngineType.Foc, 0x361); + public static readonly CommandBarComponentId SabotageSlot07 = new(GameEngineType.Foc, 0x362); + public static readonly CommandBarComponentId SabotageSlot08 = new(GameEngineType.Foc, 0x363); + public static readonly CommandBarComponentId SabotageSlot09 = new(GameEngineType.Foc, 0x364); + public static readonly CommandBarComponentId SabotagePrice00 = new(GameEngineType.Foc, 0x365); + public static readonly CommandBarComponentId SabotagePrice01 = new(GameEngineType.Foc, 0x366); + public static readonly CommandBarComponentId SabotagePrice02 = new(GameEngineType.Foc, 0x367); + public static readonly CommandBarComponentId SabotagePrice03 = new(GameEngineType.Foc, 0x368); + public static readonly CommandBarComponentId SabotagePrice04 = new(GameEngineType.Foc, 0x369); + public static readonly CommandBarComponentId SabotagePrice05 = new(GameEngineType.Foc, 0x36a); + public static readonly CommandBarComponentId SabotagePrice06 = new(GameEngineType.Foc, 0x36b); + public static readonly CommandBarComponentId SabotagePrice07 = new(GameEngineType.Foc, 0x36c); + public static readonly CommandBarComponentId SabotagePrice08 = new(GameEngineType.Foc, 0x36d); + public static readonly CommandBarComponentId SabotagePrice09 = new(GameEngineType.Foc, 0x36e); + public static readonly CommandBarComponentId SabotageName00 = new(GameEngineType.Foc, 0x36f); + public static readonly CommandBarComponentId SabotageName01 = new(GameEngineType.Foc, 0x370); + public static readonly CommandBarComponentId SabotageName02 = new(GameEngineType.Foc, 0x371); + public static readonly CommandBarComponentId SabotageName03 = new(GameEngineType.Foc, 0x372); + public static readonly CommandBarComponentId SabotageName04 = new(GameEngineType.Foc, 0x373); + public static readonly CommandBarComponentId SabotageName05 = new(GameEngineType.Foc, 0x374); + public static readonly CommandBarComponentId SabotageName06 = new(GameEngineType.Foc, 0x375); + public static readonly CommandBarComponentId SabotageName07 = new(GameEngineType.Foc, 0x376); + public static readonly CommandBarComponentId SabotageName08 = new(GameEngineType.Foc, 0x377); + public static readonly CommandBarComponentId SabotageName09 = new(GameEngineType.Foc, 0x378); + public static readonly CommandBarComponentId PlanetaryBombardment = new(GameEngineType.Foc, 0x379); + public static readonly CommandBarComponentId PlanetaryBombardmentRecharge = new(GameEngineType.Foc, 0x37a); + public static readonly CommandBarComponentId SuperLaser = new(GameEngineType.Foc, 0x37b); + public static readonly CommandBarComponentId SuperLaserRecharge = new(GameEngineType.Foc, 0x37c); + public static readonly CommandBarComponentId GenericFlytext = new(GameEngineType.Foc, 0x37d); + public static readonly CommandBarComponentId BribedIcon = new(GameEngineType.Foc, 0x37e); + public static readonly CommandBarComponentId SurfaceModIcon = new(GameEngineType.Foc, 0x37f); + public static readonly CommandBarComponentId RemoteBombIcon = new(GameEngineType.Foc, 0x380); + public static readonly CommandBarComponentId CorruptionShell = new(GameEngineType.Foc, 0x381); + public static readonly CommandBarComponentId CorruptionTitle = new(GameEngineType.Foc, 0x382); + public static readonly CommandBarComponentId CorruptionPlanetName = new(GameEngineType.Foc, 0x383); + public static readonly CommandBarComponentId CorruptionEncyclopedia = new(GameEngineType.Foc, 0x384); + public static readonly CommandBarComponentId CorruptionClose = new(GameEngineType.Foc, 0x385); + public static readonly CommandBarComponentId CorruptionPlanetModel = new(GameEngineType.Foc, 0x386); + public static readonly CommandBarComponentId CorruptionInfo00 = new(GameEngineType.Foc, 0x387); + public static readonly CommandBarComponentId CorruptionInfo01 = new(GameEngineType.Foc, 0x388); + public static readonly CommandBarComponentId CorruptionChoiceIcon0 = new(GameEngineType.Foc, 0x389); + public static readonly CommandBarComponentId CorruptionChoiceIcon1 = new(GameEngineType.Foc, 0x38a); + public static readonly CommandBarComponentId CorruptionChoiceIcon2 = new(GameEngineType.Foc, 0x38b); + public static readonly CommandBarComponentId CorruptionChoiceCost0 = new(GameEngineType.Foc, 0x38c); + public static readonly CommandBarComponentId CorruptionChoiceCost1 = new(GameEngineType.Foc, 0x38d); + public static readonly CommandBarComponentId CorruptionChoiceCost2 = new(GameEngineType.Foc, 0x38e); + public static readonly CommandBarComponentId CorruptionChoiceTitle0 = new(GameEngineType.Foc, 0x38f); + public static readonly CommandBarComponentId CorruptionChoiceTitle1 = new(GameEngineType.Foc, 0x390); + public static readonly CommandBarComponentId CorruptionChoiceTitle2 = new(GameEngineType.Foc, 0x391); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText00 = new(GameEngineType.Foc, 0x392); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText01 = new(GameEngineType.Foc, 0x393); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText02 = new(GameEngineType.Foc, 0x394); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText03 = new(GameEngineType.Foc, 0x395); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText10 = new(GameEngineType.Foc, 0x396); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText11 = new(GameEngineType.Foc, 0x397); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText12 = new(GameEngineType.Foc, 0x398); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText13 = new(GameEngineType.Foc, 0x399); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText20 = new(GameEngineType.Foc, 0x39a); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText21 = new(GameEngineType.Foc, 0x39b); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText22 = new(GameEngineType.Foc, 0x39c); + public static readonly CommandBarComponentId CorruptionChoiceBenefitText23 = new(GameEngineType.Foc, 0x39d); + public static readonly CommandBarComponentId CorruptionChoiceRequirement0 = new(GameEngineType.Foc, 0x39e); + public static readonly CommandBarComponentId CorruptionChoiceRequirement1 = new(GameEngineType.Foc, 0x39f); + public static readonly CommandBarComponentId CorruptionChoiceRequirement2 = new(GameEngineType.Foc, 0x3a0); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0000 = new(GameEngineType.Foc, 0x3a1); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0001 = new(GameEngineType.Foc, 0x3a2); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0002 = new(GameEngineType.Foc, 0x3a3); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0003 = new(GameEngineType.Foc, 0x3a4); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0100 = new(GameEngineType.Foc, 0x3a5); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0101 = new(GameEngineType.Foc, 0x3a6); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0102 = new(GameEngineType.Foc, 0x3a7); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon0103 = new(GameEngineType.Foc, 0x3a8); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1000 = new(GameEngineType.Foc, 0x3a9); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1001 = new(GameEngineType.Foc, 0x3aa); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1002 = new(GameEngineType.Foc, 0x3ab); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1003 = new(GameEngineType.Foc, 0x3ac); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1100 = new(GameEngineType.Foc, 0x3ad); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1101 = new(GameEngineType.Foc, 0x3ae); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1102 = new(GameEngineType.Foc, 0x3af); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon1103 = new(GameEngineType.Foc, 0x3b0); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2000 = new(GameEngineType.Foc, 0x3b1); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2001 = new(GameEngineType.Foc, 0x3b2); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2002 = new(GameEngineType.Foc, 0x3b3); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2003 = new(GameEngineType.Foc, 0x3b4); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2100 = new(GameEngineType.Foc, 0x3b5); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2101 = new(GameEngineType.Foc, 0x3b6); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2102 = new(GameEngineType.Foc, 0x3b7); + public static readonly CommandBarComponentId CorruptionChoiceRequirementIcon2103 = new(GameEngineType.Foc, 0x3b8); + public static readonly CommandBarComponentId HackSuperWeaponShell = new(GameEngineType.Foc, 0x3b9); + public static readonly CommandBarComponentId HackSuperWeaponTitle = new(GameEngineType.Foc, 0x3ba); + public static readonly CommandBarComponentId HackSuperWeaponText00 = new(GameEngineType.Foc, 0x3bb); + public static readonly CommandBarComponentId HackSuperWeaponText01 = new(GameEngineType.Foc, 0x3bc); + public static readonly CommandBarComponentId HackSuperWeaponText02 = new(GameEngineType.Foc, 0x3bd); + public static readonly CommandBarComponentId HackSuperWeaponText03 = new(GameEngineType.Foc, 0x3be); + public static readonly CommandBarComponentId HackSuperWeaponText04 = new(GameEngineType.Foc, 0x3bf); + public static readonly CommandBarComponentId HackSuperWeaponText05 = new(GameEngineType.Foc, 0x3c0); + public static readonly CommandBarComponentId HackSuperWeaponText06 = new(GameEngineType.Foc, 0x3c1); + public static readonly CommandBarComponentId HackSuperWeaponCost = new(GameEngineType.Foc, 0x3c2); + public static readonly CommandBarComponentId HackSuperWeaponAccept = new(GameEngineType.Foc, 0x3c3); + public static readonly CommandBarComponentId HackSuperWeaponCancel = new(GameEngineType.Foc, 0x3c4); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs index 5b09d6f..7b6eeb6 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/SupportedCommandBarComponentData.cs @@ -1,976 +1,1652 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace PG.StarWarsGame.Engine.CommandBar; -internal static class SupportedCommandBarComponentData +public static class SupportedCommandBarComponentData { - public static readonly IReadOnlyDictionary SupportedComponents = + public static IReadOnlyDictionary GetComponentIdsForEngine(GameEngineType engineType) + { + return engineType switch + { + GameEngineType.Eaw => EawSupportedComponents, + GameEngineType.Foc => FocSupportedComponents, + _ => throw new NotSupportedException() + }; + } + + private static readonly IReadOnlyDictionary EawSupportedComponents = + new Dictionary + { + { EawCommandBarComponentIds.MainShell, "i_main_commandbar" }, + { EawCommandBarComponentIds.ProductionOptions, "b_option_g" }, + { EawCommandBarComponentIds.GalacticCamera, "b_camera_g" }, + { EawCommandBarComponentIds.PlanetName, "Text_Planet" }, + { EawCommandBarComponentIds.PlanetValue, "Text_Planetary_Income" }, + { EawCommandBarComponentIds.PlanetAffiliation, "Text_Allegiance_A" }, + { EawCommandBarComponentIds.PlayerCredits, "Text_Credits" }, + { EawCommandBarComponentIds.ReinforcementCap, "Text_Planetary_Pop" }, + { EawCommandBarComponentIds.PlanetNameTactical, "Text_Planet_tactical" }, + { EawCommandBarComponentIds.PlanetInfo, "planet_info" }, + { EawCommandBarComponentIds.Production0, "b_Create_00" }, + { EawCommandBarComponentIds.Production1, "b_Create_01" }, + { EawCommandBarComponentIds.Production2, "b_Create_02" }, + { EawCommandBarComponentIds.Production3, "b_Create_03" }, + { EawCommandBarComponentIds.Production4, "b_Create_04" }, + { EawCommandBarComponentIds.Production5, "b_Create_05" }, + { EawCommandBarComponentIds.Production6, "b_Create_06" }, + { EawCommandBarComponentIds.Production7, "b_Create_07" }, + { EawCommandBarComponentIds.Production8, "b_Create_08" }, + { EawCommandBarComponentIds.Production9, "b_Create_09" }, + { EawCommandBarComponentIds.Production10, "b_Create_10" }, + { EawCommandBarComponentIds.Production11, "b_Create_11" }, + { EawCommandBarComponentIds.Production12, "b_Create_12" }, + { EawCommandBarComponentIds.Production13, "b_Create_13" }, + { EawCommandBarComponentIds.Production14, "b_Create_14" }, + { EawCommandBarComponentIds.Production15, "b_Create_15" }, + { EawCommandBarComponentIds.Production16, "b_Create_16" }, + { EawCommandBarComponentIds.Production17, "b_Create_17" }, + { EawCommandBarComponentIds.Production18, "b_Create_18" }, + { EawCommandBarComponentIds.Production19, "b_Create_19" }, + { EawCommandBarComponentIds.Production20, "b_Create_20" }, + { EawCommandBarComponentIds.Production21, "b_Create_21" }, + { EawCommandBarComponentIds.Production22, "b_Create_22" }, + { EawCommandBarComponentIds.Production23, "b_Create_23" }, + { EawCommandBarComponentIds.Production24, "b_Create_24" }, + { EawCommandBarComponentIds.Production25, "b_Create_25" }, + { EawCommandBarComponentIds.Production26, "b_Create_26" }, + { EawCommandBarComponentIds.Production27, "b_Create_27" }, + { EawCommandBarComponentIds.Production28, "b_Create_28" }, + { EawCommandBarComponentIds.Production29, "b_Create_29" }, + { EawCommandBarComponentIds.Production30, "b_Create_30" }, + { EawCommandBarComponentIds.Production31, "b_Create_31" }, + { EawCommandBarComponentIds.Production32, "b_Create_32" }, + { EawCommandBarComponentIds.Production33, "b_Create_33" }, + { EawCommandBarComponentIds.Production34, "b_Create_34" }, + { EawCommandBarComponentIds.Production35, "b_Create_35" }, + { EawCommandBarComponentIds.Production36, "b_Create_36" }, + { EawCommandBarComponentIds.Production37, "b_Create_37" }, + { EawCommandBarComponentIds.Production38, "b_Create_38" }, + { EawCommandBarComponentIds.Production39, "b_Create_39" }, + { EawCommandBarComponentIds.Production40, "b_Create_40" }, + { EawCommandBarComponentIds.Production41, "b_Create_41" }, + { EawCommandBarComponentIds.Production42, "b_Create_42" }, + { EawCommandBarComponentIds.Production43, "b_Create_43" }, + { EawCommandBarComponentIds.Production44, "b_Create_44" }, + { EawCommandBarComponentIds.Production45, "b_Create_45" }, + { EawCommandBarComponentIds.Production46, "b_Create_46" }, + { EawCommandBarComponentIds.Production47, "b_Create_47" }, + { EawCommandBarComponentIds.DroidHelp, "b_droid_help" }, + { EawCommandBarComponentIds.DroidHelpTactical, "b_droid_help_tactical" }, + { EawCommandBarComponentIds.CurrentDay, "Text_Day_Counter" }, + { EawCommandBarComponentIds.DayCredits, "Text_Day_Credits" }, + { EawCommandBarComponentIds.PopulationCap, "Text_Pop_Cap" }, + { EawCommandBarComponentIds.Filter0, "b_filters0" }, + { EawCommandBarComponentIds.Filter1, "b_filters1" }, + { EawCommandBarComponentIds.Filter2, "b_filters2" }, + { EawCommandBarComponentIds.Filter3, "b_filters3" }, + { EawCommandBarComponentIds.StoryArcButton, "b_story_arc_g" }, + { EawCommandBarComponentIds.PlanetSummaryButton, "b_quick_ref" }, + { EawCommandBarComponentIds.SpaceTab, "b_space_tab" }, + { EawCommandBarComponentIds.LandTab, "b_planet_tab" }, + { EawCommandBarComponentIds.Dial, "b_dial" }, + { EawCommandBarComponentIds.ScrollRight, "b_scroll_right" }, + { EawCommandBarComponentIds.ScrollLeft, "b_scroll_left" }, + { EawCommandBarComponentIds.ZoomView, "b_zoom" }, + { EawCommandBarComponentIds.PrevPlanet, "b_planet_left" }, + { EawCommandBarComponentIds.NextPlanet, "b_planet_right" }, + { EawCommandBarComponentIds.RadarGalactic, "radar_galactic" }, + { EawCommandBarComponentIds.TechLevel, "Text_Tech_Level" }, + { EawCommandBarComponentIds.BalancePip, "balance_pip" }, + { EawCommandBarComponentIds.BuildQueue00, "queue00" }, + { EawCommandBarComponentIds.BuildQueue01, "queue01" }, + { EawCommandBarComponentIds.BuildQueue02, "queue02" }, + { EawCommandBarComponentIds.BuildQueue03, "queue03" }, + { EawCommandBarComponentIds.BuildQueue04, "queue04" }, + { EawCommandBarComponentIds.BuildQueue05, "queue05" }, + { EawCommandBarComponentIds.BuildQueue06, "queue06" }, + { EawCommandBarComponentIds.BuildQueue07, "queue07" }, + { EawCommandBarComponentIds.BuildQueue08, "queue08" }, + { EawCommandBarComponentIds.BuildQueue09, "queue09" }, + { EawCommandBarComponentIds.OrganizationShell, "i_main_organize" }, + { EawCommandBarComponentIds.OrganizationCollision, "land_art_ALT0" }, + { EawCommandBarComponentIds.SmugglerBox, "b_underground" }, + { EawCommandBarComponentIds.SpaceStationUpgrade01, "space_Pad_1" }, + { EawCommandBarComponentIds.SpaceStationUpgrade02, "space_Pad_02" }, + { EawCommandBarComponentIds.HeroAbilitySlot, "Slot_00" }, + { EawCommandBarComponentIds.PlanetOrganize0, "Pad_00" }, + { EawCommandBarComponentIds.PlanetOrganize1, "Pad_01" }, + { EawCommandBarComponentIds.PlanetOrganize2, "Pad_02" }, + { EawCommandBarComponentIds.PlanetOrganize3, "Pad_03" }, + { EawCommandBarComponentIds.PlanetOrganize4, "Pad_04" }, + { EawCommandBarComponentIds.PlanetOrganize5, "Pad_05" }, + { EawCommandBarComponentIds.PlanetOrganize6, "Pad_06" }, + { EawCommandBarComponentIds.PlanetOrganize7, "Pad_07" }, + { EawCommandBarComponentIds.PlanetOrganize8, "Pad_08" }, + { EawCommandBarComponentIds.PlanetOrganize9, "Pad_09" }, + { EawCommandBarComponentIds.SpecialStructureLand0, "Planet_structure_00" }, + { EawCommandBarComponentIds.SpecialStructureLand1, "Planet_structure_01" }, + { EawCommandBarComponentIds.SpecialStructureLand2, "Planet_structure_02" }, + { EawCommandBarComponentIds.SpecialStructureLand3, "Planet_structure_03" }, + { EawCommandBarComponentIds.SpecialStructureLand4, "Planet_structure_04" }, + { EawCommandBarComponentIds.SpecialStructureLand5, "Planet_structure_05" }, + { EawCommandBarComponentIds.SpecialStructureLand6, "Planet_structure_06" }, + { EawCommandBarComponentIds.SpecialStructureLand7, "Planet_structure_07" }, + { EawCommandBarComponentIds.SpecialStructureLand8, "Planet_structure_08" }, + { EawCommandBarComponentIds.SpecialStructureLandSell, "g_ground_sell" }, + { EawCommandBarComponentIds.BigFleet0, "q1_fleet" }, + { EawCommandBarComponentIds.BigFleet1, "q2_fleet" }, + { EawCommandBarComponentIds.BigFleet2, "q3_fleet" }, + { EawCommandBarComponentIds.BigFleet3, "q4_fleet" }, + { EawCommandBarComponentIds.Fleet0Slot0, "q1_0000" }, + { EawCommandBarComponentIds.Fleet0Slot1, "q1_0001" }, + { EawCommandBarComponentIds.Fleet0Slot2, "q1_0002" }, + { EawCommandBarComponentIds.Fleet0Slot3, "q1_0003" }, + { EawCommandBarComponentIds.Fleet0Slot4, "q1_0004" }, + { EawCommandBarComponentIds.Fleet0Slot5, "q1_0005" }, + { EawCommandBarComponentIds.Fleet0Slot6, "q1_0006" }, + { EawCommandBarComponentIds.Fleet0Slot7, "q1_0100" }, + { EawCommandBarComponentIds.Fleet0Slot8, "q1_0101" }, + { EawCommandBarComponentIds.Fleet0Slot9, "q1_0102" }, + { EawCommandBarComponentIds.Fleet0Slot10, "q1_0103" }, + { EawCommandBarComponentIds.Fleet0Slot11, "q1_0104" }, + { EawCommandBarComponentIds.Fleet0Slot12, "q1_0105" }, + { EawCommandBarComponentIds.Fleet0Slot13, "q1_0106" }, + { EawCommandBarComponentIds.Fleet0Slot14, "q1_0200" }, + { EawCommandBarComponentIds.Fleet0Slot15, "q1_0201" }, + { EawCommandBarComponentIds.Fleet0Slot16, "q1_0202" }, + { EawCommandBarComponentIds.Fleet0Slot17, "q1_0203" }, + { EawCommandBarComponentIds.Fleet0Slot18, "q1_0204" }, + { EawCommandBarComponentIds.Fleet0Slot19, "q1_0205" }, + { EawCommandBarComponentIds.Fleet0Slot20, "q1_0206" }, + { EawCommandBarComponentIds.Fleet0Slot21, "q1_0300" }, + { EawCommandBarComponentIds.Fleet0Slot22, "q1_0301" }, + { EawCommandBarComponentIds.Fleet0Slot23, "q1_0302" }, + { EawCommandBarComponentIds.Fleet0Slot24, "q1_0303" }, + { EawCommandBarComponentIds.Fleet0Slot25, "q1_0304" }, + { EawCommandBarComponentIds.Fleet0Slot26, "q1_0305" }, + { EawCommandBarComponentIds.Fleet0Slot27, "q1_0306" }, + { EawCommandBarComponentIds.Fleet0Slot28, "q1_0400" }, + { EawCommandBarComponentIds.Fleet0Slot29, "q1_0401" }, + { EawCommandBarComponentIds.Fleet0Slot30, "q1_0402" }, + { EawCommandBarComponentIds.Fleet0Slot31, "q1_0403" }, + { EawCommandBarComponentIds.Fleet0Slot32, "q1_0404" }, + { EawCommandBarComponentIds.Fleet0Slot33, "q1_0405" }, + { EawCommandBarComponentIds.Fleet0Slot34, "q1_0406" }, + { EawCommandBarComponentIds.Fleet1Slot0, "q2_0000" }, + { EawCommandBarComponentIds.Fleet1Slot1, "q2_0001" }, + { EawCommandBarComponentIds.Fleet1Slot2, "q2_0002" }, + { EawCommandBarComponentIds.Fleet1Slot3, "q2_0003" }, + { EawCommandBarComponentIds.Fleet1Slot4, "q2_0004" }, + { EawCommandBarComponentIds.Fleet1Slot5, "q2_0005" }, + { EawCommandBarComponentIds.Fleet1Slot6, "q2_0006" }, + { EawCommandBarComponentIds.Fleet1Slot7, "q2_0100" }, + { EawCommandBarComponentIds.Fleet1Slot8, "q2_0101" }, + { EawCommandBarComponentIds.Fleet1Slot9, "q2_0102" }, + { EawCommandBarComponentIds.Fleet1Slot10, "q2_0103" }, + { EawCommandBarComponentIds.Fleet1Slot11, "q2_0104" }, + { EawCommandBarComponentIds.Fleet1Slot12, "q2_0105" }, + { EawCommandBarComponentIds.Fleet1Slot13, "q2_0106" }, + { EawCommandBarComponentIds.Fleet1Slot14, "q2_0200" }, + { EawCommandBarComponentIds.Fleet1Slot15, "q2_0201" }, + { EawCommandBarComponentIds.Fleet1Slot16, "q2_0202" }, + { EawCommandBarComponentIds.Fleet1Slot17, "q2_0203" }, + { EawCommandBarComponentIds.Fleet1Slot18, "q2_0204" }, + { EawCommandBarComponentIds.Fleet1Slot19, "q2_0205" }, + { EawCommandBarComponentIds.Fleet1Slot20, "q2_0206" }, + { EawCommandBarComponentIds.Fleet1Slot21, "q2_0300" }, + { EawCommandBarComponentIds.Fleet1Slot22, "q2_0301" }, + { EawCommandBarComponentIds.Fleet1Slot23, "q2_0302" }, + { EawCommandBarComponentIds.Fleet1Slot24, "q2_0303" }, + { EawCommandBarComponentIds.Fleet1Slot25, "q2_0304" }, + { EawCommandBarComponentIds.Fleet1Slot26, "q2_0305" }, + { EawCommandBarComponentIds.Fleet1Slot27, "q2_0306" }, + { EawCommandBarComponentIds.Fleet1Slot28, "q2_0400" }, + { EawCommandBarComponentIds.Fleet1Slot29, "q2_0401" }, + { EawCommandBarComponentIds.Fleet1Slot30, "q2_0402" }, + { EawCommandBarComponentIds.Fleet1Slot31, "q2_0403" }, + { EawCommandBarComponentIds.Fleet1Slot32, "q2_0404" }, + { EawCommandBarComponentIds.Fleet1Slot33, "q2_0405" }, + { EawCommandBarComponentIds.Fleet1Slot34, "q2_0406" }, + { EawCommandBarComponentIds.Fleet2Slot0, "q3_0000" }, + { EawCommandBarComponentIds.Fleet2Slot1, "q3_0001" }, + { EawCommandBarComponentIds.Fleet2Slot2, "q3_0002" }, + { EawCommandBarComponentIds.Fleet2Slot3, "q3_0003" }, + { EawCommandBarComponentIds.Fleet2Slot4, "q3_0004" }, + { EawCommandBarComponentIds.Fleet2Slot5, "q3_0005" }, + { EawCommandBarComponentIds.Fleet2Slot6, "q3_0006" }, + { EawCommandBarComponentIds.Fleet2Slot7, "q3_0100" }, + { EawCommandBarComponentIds.Fleet2Slot8, "q3_0101" }, + { EawCommandBarComponentIds.Fleet2Slot9, "q3_0102" }, + { EawCommandBarComponentIds.Fleet2Slot10, "q3_0103" }, + { EawCommandBarComponentIds.Fleet2Slot11, "q3_0104" }, + { EawCommandBarComponentIds.Fleet2Slot12, "q3_0105" }, + { EawCommandBarComponentIds.Fleet2Slot13, "q3_0106" }, + { EawCommandBarComponentIds.Fleet2Slot14, "q3_0200" }, + { EawCommandBarComponentIds.Fleet2Slot15, "q3_0201" }, + { EawCommandBarComponentIds.Fleet2Slot16, "q3_0202" }, + { EawCommandBarComponentIds.Fleet2Slot17, "q3_0203" }, + { EawCommandBarComponentIds.Fleet2Slot18, "q3_0204" }, + { EawCommandBarComponentIds.Fleet2Slot19, "q3_0205" }, + { EawCommandBarComponentIds.Fleet2Slot20, "q3_0206" }, + { EawCommandBarComponentIds.Fleet2Slot21, "q3_0300" }, + { EawCommandBarComponentIds.Fleet2Slot22, "q3_0301" }, + { EawCommandBarComponentIds.Fleet2Slot23, "q3_0302" }, + { EawCommandBarComponentIds.Fleet2Slot24, "q3_0303" }, + { EawCommandBarComponentIds.Fleet2Slot25, "q3_0304" }, + { EawCommandBarComponentIds.Fleet2Slot26, "q3_0305" }, + { EawCommandBarComponentIds.Fleet2Slot27, "q3_0306" }, + { EawCommandBarComponentIds.Fleet2Slot28, "q3_0400" }, + { EawCommandBarComponentIds.Fleet2Slot29, "q3_0401" }, + { EawCommandBarComponentIds.Fleet2Slot30, "q3_0402" }, + { EawCommandBarComponentIds.Fleet2Slot31, "q3_0403" }, + { EawCommandBarComponentIds.Fleet2Slot32, "q3_0404" }, + { EawCommandBarComponentIds.Fleet2Slot33, "q3_0405" }, + { EawCommandBarComponentIds.Fleet2Slot34, "q3_0406" }, + { EawCommandBarComponentIds.TacticalMain, "i_main_skirmish" }, + { EawCommandBarComponentIds.TacticalOptions, "b_option_t" }, + { EawCommandBarComponentIds.TacticalCamera, "b_camera_t" }, + { EawCommandBarComponentIds.TacticalBeacon, "b_beacon_t" }, + { EawCommandBarComponentIds.TacticalWeapon0, "b_special_weapon" }, + { EawCommandBarComponentIds.TacticalWeapon1, "b_special_weapon2" }, + { EawCommandBarComponentIds.TacticalReinforce, "b_reinforcement" }, + { EawCommandBarComponentIds.TacticalRetreat, "b_retreat" }, + { EawCommandBarComponentIds.TacticalStoryArc, "b_story_arc_t" }, + { EawCommandBarComponentIds.TacticalTechLevel, "Text_Tactical_Tech" }, + { EawCommandBarComponentIds.TacticalAttack, "c_button00" }, + { EawCommandBarComponentIds.TacticalAttackMove, "c_button01" }, + { EawCommandBarComponentIds.TacticalMove, "c_button02" }, + { EawCommandBarComponentIds.TacticalWaypoint, "c_button03" }, + { EawCommandBarComponentIds.TacticalStop, "c_button04" }, + { EawCommandBarComponentIds.TacticalGuard, "c_button05" }, + { EawCommandBarComponentIds.SpaceTacticalRadar, "radar" }, + { EawCommandBarComponentIds.SpaceTacticalCredits, "Text_Credits_tactical" }, + { EawCommandBarComponentIds.SpaceTacticalGrabBar, "st_grab_bar" }, + { EawCommandBarComponentIds.SpaceTacticalHealthBar, "st_health_bar" }, + { EawCommandBarComponentIds.SpaceTacticalBracketSmall, "st_bracket_small" }, + { EawCommandBarComponentIds.SpaceTacticalBracketMedium, "st_bracket_medium" }, + { EawCommandBarComponentIds.SpaceTacticalBracketLarge, "st_bracket_large" }, + { EawCommandBarComponentIds.SpaceTacticalHeroIcon, "st_hero_icon" }, + { EawCommandBarComponentIds.SpaceTacticalHeroHealth, "st_hero_health" }, + { EawCommandBarComponentIds.SpaceTacticalHealth, "st_health" }, + { EawCommandBarComponentIds.SpaceTacticalHealthMedium, "st_health_medium" }, + { EawCommandBarComponentIds.SpaceTacticalHealthLarge, "st_health_large" }, + { EawCommandBarComponentIds.SpaceTacticalShields, "st_shields" }, + { EawCommandBarComponentIds.SpaceTacticalShieldsMedium, "st_shields_medium" }, + { EawCommandBarComponentIds.SpaceTacticalShieldsLarge, "st_shields_large" }, + { EawCommandBarComponentIds.SpaceTacticalPower, "st_power" }, + { EawCommandBarComponentIds.SpaceTacticalControlGroup, "st_control_group" }, + { EawCommandBarComponentIds.SpaceTacticalAbilityIcon, "st_ability_icon" }, + { EawCommandBarComponentIds.SpaceTacticalGarrisonIcon, "st_garrison_icon" }, + { EawCommandBarComponentIds.LandTacticalWeatherIcon, "lt_weather_icon" }, + { EawCommandBarComponentIds.DsFireShell, "ds_shell" }, + { EawCommandBarComponentIds.DsFire, "deathstar_switch" }, + { EawCommandBarComponentIds.DsCountdownTimer, "DS_Countdown_Timer" }, + { EawCommandBarComponentIds.TacticalSelect00, "s_select_00" }, + { EawCommandBarComponentIds.TacticalSelect01, "s_select_01" }, + { EawCommandBarComponentIds.TacticalSelect02, "s_select_02" }, + { EawCommandBarComponentIds.TacticalSelect03, "s_select_03" }, + { EawCommandBarComponentIds.TacticalSelect04, "s_select_04" }, + { EawCommandBarComponentIds.TacticalSelect05, "s_select_05" }, + { EawCommandBarComponentIds.TacticalSelect06, "s_select_06" }, + { EawCommandBarComponentIds.TacticalSelect07, "s_select_07" }, + { EawCommandBarComponentIds.TacticalSelect08, "s_select_08" }, + { EawCommandBarComponentIds.TacticalSelect09, "s_select_09" }, + { EawCommandBarComponentIds.TacticalSelect10, "s_select_10" }, + { EawCommandBarComponentIds.TacticalSelect11, "s_select_11" }, + { EawCommandBarComponentIds.TacticalSelect12, "s_select_12" }, + { EawCommandBarComponentIds.TacticalSelect13, "s_select_13" }, + { EawCommandBarComponentIds.TacticalSelect14, "s_select_14" }, + { EawCommandBarComponentIds.TacticalSelect15, "s_select_15" }, + { EawCommandBarComponentIds.TacticalSelect16, "s_select_16" }, + { EawCommandBarComponentIds.TacticalSelect17, "s_select_17" }, + { EawCommandBarComponentIds.TacticalSelect18, "s_select_18" }, + { EawCommandBarComponentIds.TacticalSelect19, "s_select_19" }, + { EawCommandBarComponentIds.TacticalSelect20, "s_select_20" }, + { EawCommandBarComponentIds.TacticalSelect21, "s_select_21" }, + { EawCommandBarComponentIds.TacticalSelect22, "s_select_22" }, + { EawCommandBarComponentIds.TacticalSelect23, "s_select_23" }, + { EawCommandBarComponentIds.TacticalSelect24, "s_select_24" }, + { EawCommandBarComponentIds.TacticalSelect25, "s_select_25" }, + { EawCommandBarComponentIds.TacticalSelect26, "s_select_26" }, + { EawCommandBarComponentIds.TacticalSelect27, "s_select_27" }, + { EawCommandBarComponentIds.TacticalSelect28, "s_select_28" }, + { EawCommandBarComponentIds.TacticalSelect29, "s_select_29" }, + { EawCommandBarComponentIds.TacticalSelect30, "s_select_30" }, + { EawCommandBarComponentIds.TacticalSelect31, "s_select_31" }, + { EawCommandBarComponentIds.TacticalSelect32, "s_select_32" }, + { EawCommandBarComponentIds.TacticalSelect33, "s_select_33" }, + { EawCommandBarComponentIds.TacticalSelect34, "s_select_34" }, + { EawCommandBarComponentIds.TacticalSelect35, "s_select_35" }, + { EawCommandBarComponentIds.TacticalSelect36, "s_select_36" }, + { EawCommandBarComponentIds.TacticalSelect37, "s_select_37" }, + { EawCommandBarComponentIds.TacticalSelect38, "s_select_38" }, + { EawCommandBarComponentIds.TacticalSelect39, "s_select_39" }, + { EawCommandBarComponentIds.TacticalSelect40, "s_select_40" }, + { EawCommandBarComponentIds.TacticalSelect41, "s_select_41" }, + { EawCommandBarComponentIds.TacticalSelect42, "s_select_42" }, + { EawCommandBarComponentIds.TacticalSelect43, "s_select_43" }, + { EawCommandBarComponentIds.TacticalSelect44, "s_select_44" }, + { EawCommandBarComponentIds.TacticalSelect45, "s_select_45" }, + { EawCommandBarComponentIds.TacticalSelect46, "s_select_46" }, + { EawCommandBarComponentIds.TacticalSelect47, "s_select_47" }, + { EawCommandBarComponentIds.TacticalSelectHealth00, "s_health_00" }, + { EawCommandBarComponentIds.TacticalSelectHealth01, "s_health_01" }, + { EawCommandBarComponentIds.TacticalSelectHealth02, "s_health_02" }, + { EawCommandBarComponentIds.TacticalSelectHealth03, "s_health_03" }, + { EawCommandBarComponentIds.TacticalSelectHealth04, "s_health_04" }, + { EawCommandBarComponentIds.TacticalSelectHealth05, "s_health_05" }, + { EawCommandBarComponentIds.TacticalSelectHealth06, "s_health_06" }, + { EawCommandBarComponentIds.TacticalSelectHealth07, "s_health_07" }, + { EawCommandBarComponentIds.TacticalSelectHealth08, "s_health_08" }, + { EawCommandBarComponentIds.TacticalSelectHealth09, "s_health_09" }, + { EawCommandBarComponentIds.TacticalSelectHealth10, "s_health_10" }, + { EawCommandBarComponentIds.TacticalSelectHealth11, "s_health_11" }, + { EawCommandBarComponentIds.TacticalSelectHealth12, "s_health_12" }, + { EawCommandBarComponentIds.TacticalSelectHealth13, "s_health_13" }, + { EawCommandBarComponentIds.TacticalSelectHealth14, "s_health_14" }, + { EawCommandBarComponentIds.TacticalSelectHealth15, "s_health_15" }, + { EawCommandBarComponentIds.TacticalSelectHealth16, "s_health_16" }, + { EawCommandBarComponentIds.TacticalSelectHealth17, "s_health_17" }, + { EawCommandBarComponentIds.TacticalSelectHealth18, "s_health_18" }, + { EawCommandBarComponentIds.TacticalSelectHealth19, "s_health_19" }, + { EawCommandBarComponentIds.TacticalSelectHealth20, "s_health_20" }, + { EawCommandBarComponentIds.TacticalSelectHealth21, "s_health_21" }, + { EawCommandBarComponentIds.TacticalSelectHealth22, "s_health_22" }, + { EawCommandBarComponentIds.TacticalSelectHealth23, "s_health_23" }, + { EawCommandBarComponentIds.TacticalSelectHealth24, "s_health_24" }, + { EawCommandBarComponentIds.TacticalSelectHealth25, "s_health_25" }, + { EawCommandBarComponentIds.TacticalSelectHealth26, "s_health_26" }, + { EawCommandBarComponentIds.TacticalSelectHealth27, "s_health_27" }, + { EawCommandBarComponentIds.TacticalSelectHealth28, "s_health_28" }, + { EawCommandBarComponentIds.TacticalSelectHealth29, "s_health_29" }, + { EawCommandBarComponentIds.TacticalSelectHealth30, "s_health_30" }, + { EawCommandBarComponentIds.TacticalSelectHealth31, "s_health_31" }, + { EawCommandBarComponentIds.TacticalSelectHealth32, "s_health_32" }, + { EawCommandBarComponentIds.TacticalSelectHealth33, "s_health_33" }, + { EawCommandBarComponentIds.TacticalSelectHealth34, "s_health_34" }, + { EawCommandBarComponentIds.TacticalSelectHealth35, "s_health_35" }, + { EawCommandBarComponentIds.TacticalSelectHealth36, "s_health_36" }, + { EawCommandBarComponentIds.TacticalSelectHealth37, "s_health_37" }, + { EawCommandBarComponentIds.TacticalSelectHealth38, "s_health_38" }, + { EawCommandBarComponentIds.TacticalSelectHealth39, "s_health_39" }, + { EawCommandBarComponentIds.TacticalSelectHealth40, "s_health_40" }, + { EawCommandBarComponentIds.TacticalSelectHealth41, "s_health_41" }, + { EawCommandBarComponentIds.TacticalSelectHealth42, "s_health_42" }, + { EawCommandBarComponentIds.TacticalSelectHealth43, "s_health_43" }, + { EawCommandBarComponentIds.TacticalSelectHealth44, "s_health_44" }, + { EawCommandBarComponentIds.TacticalSelectHealth45, "s_health_45" }, + { EawCommandBarComponentIds.TacticalSelectHealth46, "s_health_46" }, + { EawCommandBarComponentIds.TacticalSelectHealth47, "s_health_47" }, + { EawCommandBarComponentIds.TacticalSelectShield00, "s_shield_00" }, + { EawCommandBarComponentIds.TacticalSelectShield01, "s_shield_01" }, + { EawCommandBarComponentIds.TacticalSelectShield02, "s_shield_02" }, + { EawCommandBarComponentIds.TacticalSelectShield03, "s_shield_03" }, + { EawCommandBarComponentIds.TacticalSelectShield04, "s_shield_04" }, + { EawCommandBarComponentIds.TacticalSelectShield05, "s_shield_05" }, + { EawCommandBarComponentIds.TacticalSelectShield06, "s_shield_06" }, + { EawCommandBarComponentIds.TacticalSelectShield07, "s_shield_07" }, + { EawCommandBarComponentIds.TacticalSelectShield08, "s_shield_08" }, + { EawCommandBarComponentIds.TacticalSelectShield09, "s_shield_09" }, + { EawCommandBarComponentIds.TacticalSelectShield10, "s_shield_10" }, + { EawCommandBarComponentIds.TacticalSelectShield11, "s_shield_11" }, + { EawCommandBarComponentIds.TacticalSelectShield12, "s_shield_12" }, + { EawCommandBarComponentIds.TacticalSelectShield13, "s_shield_13" }, + { EawCommandBarComponentIds.TacticalSelectShield14, "s_shield_14" }, + { EawCommandBarComponentIds.TacticalSelectShield15, "s_shield_15" }, + { EawCommandBarComponentIds.TacticalSelectShield16, "s_shield_16" }, + { EawCommandBarComponentIds.TacticalSelectShield17, "s_shield_17" }, + { EawCommandBarComponentIds.TacticalSelectShield18, "s_shield_18" }, + { EawCommandBarComponentIds.TacticalSelectShield19, "s_shield_19" }, + { EawCommandBarComponentIds.TacticalSelectShield20, "s_shield_20" }, + { EawCommandBarComponentIds.TacticalSelectShield21, "s_shield_21" }, + { EawCommandBarComponentIds.TacticalSelectShield22, "s_shield_22" }, + { EawCommandBarComponentIds.TacticalSelectShield23, "s_shield_23" }, + { EawCommandBarComponentIds.TacticalSelectShield24, "s_shield_24" }, + { EawCommandBarComponentIds.TacticalSelectShield25, "s_shield_25" }, + { EawCommandBarComponentIds.TacticalSelectShield26, "s_shield_26" }, + { EawCommandBarComponentIds.TacticalSelectShield27, "s_shield_27" }, + { EawCommandBarComponentIds.TacticalSelectShield28, "s_shield_28" }, + { EawCommandBarComponentIds.TacticalSelectShield29, "s_shield_29" }, + { EawCommandBarComponentIds.TacticalSelectShield30, "s_shield_30" }, + { EawCommandBarComponentIds.TacticalSelectShield31, "s_shield_31" }, + { EawCommandBarComponentIds.TacticalSelectShield32, "s_shield_32" }, + { EawCommandBarComponentIds.TacticalSelectShield33, "s_shield_33" }, + { EawCommandBarComponentIds.TacticalSelectShield34, "s_shield_34" }, + { EawCommandBarComponentIds.TacticalSelectShield35, "s_shield_35" }, + { EawCommandBarComponentIds.TacticalSelectShield36, "s_shield_36" }, + { EawCommandBarComponentIds.TacticalSelectShield37, "s_shield_37" }, + { EawCommandBarComponentIds.TacticalSelectShield38, "s_shield_38" }, + { EawCommandBarComponentIds.TacticalSelectShield39, "s_shield_39" }, + { EawCommandBarComponentIds.TacticalSelectShield40, "s_shield_40" }, + { EawCommandBarComponentIds.TacticalSelectShield41, "s_shield_41" }, + { EawCommandBarComponentIds.TacticalSelectShield42, "s_shield_42" }, + { EawCommandBarComponentIds.TacticalSelectShield43, "s_shield_43" }, + { EawCommandBarComponentIds.TacticalSelectShield44, "s_shield_44" }, + { EawCommandBarComponentIds.TacticalSelectShield45, "s_shield_45" }, + { EawCommandBarComponentIds.TacticalSelectShield46, "s_shield_46" }, + { EawCommandBarComponentIds.TacticalSelectShield47, "s_shield_47" }, + { EawCommandBarComponentIds.TacticalBorder00, "special_border_00" }, + { EawCommandBarComponentIds.TacticalBorder01, "special_border_01" }, + { EawCommandBarComponentIds.TacticalBorder02, "special_border_02" }, + { EawCommandBarComponentIds.TacticalBorder03, "special_border_03" }, + { EawCommandBarComponentIds.TacticalBorder04, "special_border_04" }, + { EawCommandBarComponentIds.TacticalBorder05, "special_border_05" }, + { EawCommandBarComponentIds.TacticalBorder06, "special_border_06" }, + { EawCommandBarComponentIds.TacticalBorder07, "special_border_07" }, + { EawCommandBarComponentIds.TacticalBorder08, "special_border_08" }, + { EawCommandBarComponentIds.TacticalBorder09, "special_border_09" }, + { EawCommandBarComponentIds.TacticalBorder10, "special_border_10" }, + { EawCommandBarComponentIds.TacticalSelectButton00, "special_button_00" }, + { EawCommandBarComponentIds.TacticalSelectButton01, "special_button_01" }, + { EawCommandBarComponentIds.TacticalSelectButton02, "special_button_02" }, + { EawCommandBarComponentIds.TacticalSelectButton03, "special_button_03" }, + { EawCommandBarComponentIds.TacticalSelectButton04, "special_button_04" }, + { EawCommandBarComponentIds.TacticalSelectButton05, "special_button_05" }, + { EawCommandBarComponentIds.TacticalSelectButton06, "special_button_06" }, + { EawCommandBarComponentIds.TacticalSelectButton07, "special_button_07" }, + { EawCommandBarComponentIds.TacticalSelectButton08, "special_button_08" }, + { EawCommandBarComponentIds.TacticalSelectButton09, "special_button_09" }, + { EawCommandBarComponentIds.TacticalSelectButton10, "special_button_10" }, + { EawCommandBarComponentIds.TacticalSelectButton11, "special_button_11" }, + { EawCommandBarComponentIds.TacticalSelectButton12, "special_button_12" }, + { EawCommandBarComponentIds.TacticalSelectButton13, "special_button_13" }, + { EawCommandBarComponentIds.TacticalSelectButton14, "special_button_14" }, + { EawCommandBarComponentIds.TacticalSelectButton15, "special_button_15" }, + { EawCommandBarComponentIds.TacticalSelectButton16, "special_button_16" }, + { EawCommandBarComponentIds.TacticalSelectButton17, "special_button_17" }, + { EawCommandBarComponentIds.TacticalSelectButton18, "special_button_18" }, + { EawCommandBarComponentIds.TacticalSelectButton19, "special_button_19" }, + { EawCommandBarComponentIds.TacticalSelectButton20, "special_button_20" }, + { EawCommandBarComponentIds.TacticalSelectButton21, "special_button_21" }, + { EawCommandBarComponentIds.TacticalSelectButton22, "special_button_22" }, + { EawCommandBarComponentIds.TacticalSelectButton23, "special_button_23" }, + { EawCommandBarComponentIds.TacticalSelectButton24, "special_button_24" }, + { EawCommandBarComponentIds.TacticalSelectButton25, "special_button_25" }, + { EawCommandBarComponentIds.TacticalSelectButton26, "special_button_26" }, + { EawCommandBarComponentIds.TacticalSelectButton27, "special_button_27" }, + { EawCommandBarComponentIds.TacticalSelectButton28, "special_button_28" }, + { EawCommandBarComponentIds.TacticalSelectButton29, "special_button_29" }, + { EawCommandBarComponentIds.TacticalSelectButton30, "special_button_30" }, + { EawCommandBarComponentIds.TacticalSelectButton31, "special_button_31" }, + { EawCommandBarComponentIds.TacticalSelectButton32, "special_button_32" }, + { EawCommandBarComponentIds.TacticalSelectButton33, "special_button_33" }, + { EawCommandBarComponentIds.TacticalSelectButton34, "special_button_34" }, + { EawCommandBarComponentIds.TacticalSelectButton35, "special_button_35" }, + { EawCommandBarComponentIds.TacticalSelectButton36, "special_button_36" }, + { EawCommandBarComponentIds.TacticalSelectButton37, "special_button_37" }, + { EawCommandBarComponentIds.TacticalSelectButton38, "special_button_38" }, + { EawCommandBarComponentIds.TacticalSelectButton39, "special_button_39" }, + { EawCommandBarComponentIds.TacticalSelectButton40, "special_button_40" }, + { EawCommandBarComponentIds.TacticalSelectButton41, "special_button_41" }, + { EawCommandBarComponentIds.TacticalSelectButton42, "special_button_42" }, + { EawCommandBarComponentIds.TacticalSelectButton43, "special_button_43" }, + { EawCommandBarComponentIds.TacticalSelectButton44, "special_button_44" }, + { EawCommandBarComponentIds.TacticalSelectButton45, "special_button_45" }, + { EawCommandBarComponentIds.TacticalSelectButton46, "special_button_46" }, + { EawCommandBarComponentIds.TacticalSelectButton47, "special_button_47" }, + { EawCommandBarComponentIds.TacticalBuildQueue00, "tqueue00" }, + { EawCommandBarComponentIds.TacticalBuildQueue01, "tqueue01" }, + { EawCommandBarComponentIds.TacticalBuildQueue02, "tqueue02" }, + { EawCommandBarComponentIds.TacticalBuildQueue03, "tqueue03" }, + { EawCommandBarComponentIds.TacticalBuildQueue04, "tqueue04" }, + { EawCommandBarComponentIds.TacticalBuildQueue05, "tqueue05" }, + { EawCommandBarComponentIds.TacticalBuildQueue06, "tqueue06" }, + { EawCommandBarComponentIds.TacticalBuildQueue07, "tqueue07" }, + { EawCommandBarComponentIds.TacticalBuildQueue08, "tqueue08" }, + { EawCommandBarComponentIds.TacticalBuildQueue09, "tqueue09" }, + { EawCommandBarComponentIds.TooltipBack, "tooltip_back" }, + { EawCommandBarComponentIds.TooltipName, "tooltip_name" }, + { EawCommandBarComponentIds.TooltipPrice, "tooltip_price" }, + { EawCommandBarComponentIds.TooltipIcon, "tooltip_icon" }, + { EawCommandBarComponentIds.TooltipIconLand, "tooltip_icon_land" }, + { EawCommandBarComponentIds.TooltipLeftJustified, "tooltip_left_text" }, + { EawCommandBarComponentIds.EncyclopediaBack, "encyclopedia_back" }, + { EawCommandBarComponentIds.EncyclopediaHeaderText, "encyclopedia_header_text" }, + { EawCommandBarComponentIds.EncyclopediaText, "encyclopedia_text" }, + { EawCommandBarComponentIds.EncyclopediaRightText, "encyclopedia_right_text" }, + { EawCommandBarComponentIds.EncyclopediaCenterText, "encyclopedia_center_text" }, + { EawCommandBarComponentIds.EncyclopediaIcon, "encyclopedia_icon" }, + { EawCommandBarComponentIds.EncyclopediaCostText, "encyclopedia_cost_text" }, + { EawCommandBarComponentIds.ZoomedBack, "zoomed_back" }, + { EawCommandBarComponentIds.ZoomedHeaderText, "zoomed_header_text" }, + { EawCommandBarComponentIds.ZoomedText, "zoomed_text" }, + { EawCommandBarComponentIds.ZoomedRightText, "zoomed_right_text" }, + { EawCommandBarComponentIds.ZoomedCenterText, "zoomed_center_text" }, + { EawCommandBarComponentIds.ZoomedCostText, "zoomed_cost_text" }, + { EawCommandBarComponentIds.GPlanetFleet, "g_planet_fleet" }, + { EawCommandBarComponentIds.GPlanetName, "g_planet_name" }, + { EawCommandBarComponentIds.GPlanetValue, "g_planet_value" }, + { EawCommandBarComponentIds.GPoliticalControl, "g_political_control" }, + { EawCommandBarComponentIds.GSpaceLevel, "g_space_level" }, + { EawCommandBarComponentIds.GSpaceIcon, "g_space_icon" }, + { EawCommandBarComponentIds.GSpaceLevelPips, "g_space_level_pips" }, + { EawCommandBarComponentIds.GGroundLevel, "g_ground_level" }, + { EawCommandBarComponentIds.GGroundIcon, "g_ground_icon" }, + { EawCommandBarComponentIds.GGroundLevelPips, "g_ground_level_pips" }, + { EawCommandBarComponentIds.GConflict, "g_conflict" }, + { EawCommandBarComponentIds.GHero, "g_hero" }, + { EawCommandBarComponentIds.GEnemyHero, "g_enemy_hero" }, + { EawCommandBarComponentIds.GBuild, "g_build" }, + { EawCommandBarComponentIds.GSmuggler, "g_smuggler" }, + { EawCommandBarComponentIds.GBountyHunter, "g_bounty_hunter" }, + { EawCommandBarComponentIds.GPlanetLandForces, "g_planet_land_forces" }, + { EawCommandBarComponentIds.GGalacticRadarBlip, "g_radar_blip" }, + { EawCommandBarComponentIds.GGalacticRadarView, "g_radar_view" }, + { EawCommandBarComponentIds.GSmuggled, "g_smuggled" }, + { EawCommandBarComponentIds.GSpecialAbility, "g_special_ability" }, + { EawCommandBarComponentIds.GHeroIcon, "g_hero_icon" }, + { EawCommandBarComponentIds.GPlanetRing, "g_planet_ring" }, + { EawCommandBarComponentIds.GWeather, "g_weather" }, + { EawCommandBarComponentIds.GPlanetAbility, "g_planet_ability" }, + { EawCommandBarComponentIds.TutorialText, "tutorial_text" }, + { EawCommandBarComponentIds.TutorialTextBack, "tutorial_text_back" }, + { EawCommandBarComponentIds.RadarBlip, "radar_blip" }, + { EawCommandBarComponentIds.TacticalBuildButtonShell, "i_build_buttons" }, + { EawCommandBarComponentIds.TacticalBuildButton0, "Build_00" }, + { EawCommandBarComponentIds.TacticalBuildButton1, "Build_01" }, + { EawCommandBarComponentIds.TacticalBuildButton2, "Build_02" }, + { EawCommandBarComponentIds.TacticalBuildButton3, "Build_03" }, + { EawCommandBarComponentIds.TacticalBuildButton4, "Build_04" }, + { EawCommandBarComponentIds.TacticalBuildButton5, "Build_05" }, + { EawCommandBarComponentIds.TacticalSellButton, "tactical_sell" }, + { EawCommandBarComponentIds.ReinforcementShell, "i_main_reinforce" }, + { EawCommandBarComponentIds.ReinforcementCancel, "r_close" }, + { EawCommandBarComponentIds.ReinforcementSlot00, "r_0000" }, + { EawCommandBarComponentIds.ReinforcementSlot01, "r_0001" }, + { EawCommandBarComponentIds.ReinforcementSlot02, "r_0002" }, + { EawCommandBarComponentIds.ReinforcementSlot03, "r_0100" }, + { EawCommandBarComponentIds.ReinforcementSlot04, "r_0101" }, + { EawCommandBarComponentIds.ReinforcementSlot05, "r_0102" }, + { EawCommandBarComponentIds.ReinforcementSlot06, "r_0200" }, + { EawCommandBarComponentIds.ReinforcementSlot07, "r_0201" }, + { EawCommandBarComponentIds.ReinforcementSlot08, "r_0202" }, + { EawCommandBarComponentIds.ReinforcementSlot09, "r_0300" }, + { EawCommandBarComponentIds.ReinforcementSlot10, "r_0301" }, + { EawCommandBarComponentIds.ReinforcementSlot11, "r_0302" }, + { EawCommandBarComponentIds.ReinforcementSlot12, "r_0400" }, + { EawCommandBarComponentIds.ReinforcementSlot13, "r_0401" }, + { EawCommandBarComponentIds.ReinforcementSlot14, "r_0402" }, + { EawCommandBarComponentIds.ReinforcementCap2, "r_pop_icon" }, + { EawCommandBarComponentIds.ReinforcementCap2Text, "r_pop_text" }, + { EawCommandBarComponentIds.ReinforcementCounter, "reinforcement_counter" }, + { EawCommandBarComponentIds.GarrisonRespawnCounter, "garrison_respawn_counter" }, + { EawCommandBarComponentIds.SkirmishUpgrade, "skirmish_upgrade" }, + { EawCommandBarComponentIds.PendingBattleShell, "pending_battle_shell" }, + { EawCommandBarComponentIds.PendingBattleText, "text_attack_choice" }, + { EawCommandBarComponentIds.PendingBattleButton, "choice_button_left" }, + { EawCommandBarComponentIds.PendingBattleAutoresolve, "choice_button_right" }, + { EawCommandBarComponentIds.PendingBattleGraphLeft, "graph_left" }, + { EawCommandBarComponentIds.PendingBattleGraphRight, "graph_right" }, + { EawCommandBarComponentIds.TatcicalAutoresolveShell, "autoresolve_shell" }, + { EawCommandBarComponentIds.TacticalAutoresolveButton, "resolve_button" }, + { EawCommandBarComponentIds.TacticalAutoresolveGraphLeft, "graph_left_resolve" }, + { EawCommandBarComponentIds.TacticalAutoresolveGraphRight, "graph_right_resolve" }, + { EawCommandBarComponentIds.ObjectiveBack, "objective_back" }, + { EawCommandBarComponentIds.ObjectiveHeaderText, "objective_header_text" }, + { EawCommandBarComponentIds.ObjectiveText, "objective_text" }, + { EawCommandBarComponentIds.ObjectiveIcon, "objective_icon" }, + { EawCommandBarComponentIds.GuiDialogTooltip, "gui_dialog_tooltip" }, + { EawCommandBarComponentIds.TargetUnitTypeShell, "target_type_back" }, + { EawCommandBarComponentIds.TargetUnitTypeTitle, "s_title" }, + { EawCommandBarComponentIds.TargetUnitTypeCancel, "s_close" }, + { EawCommandBarComponentIds.TargetUnitTypeDescription, "text_steal" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot00, "s_0000" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot01, "s_0001" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot02, "s_0002" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot03, "s_0003" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot04, "s_0100" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot05, "s_0101" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot06, "s_0102" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot07, "s_0103" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot08, "s_0200" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot09, "s_0201" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot10, "s_0202" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot11, "s_0203" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot12, "s_0300" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot13, "s_0301" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot14, "s_0302" }, + { EawCommandBarComponentIds.TargetUnitTypeSlot15, "s_0303" }, + { EawCommandBarComponentIds.VcrButtonPlayPause, "b_play_pause" }, + { EawCommandBarComponentIds.VcrButtonFastForward, "b_fast_forward" }, + { EawCommandBarComponentIds.VcrButtonFastForwardTactical, "b_fast_forward_t" }, + { EawCommandBarComponentIds.VcrButtonPlayPauseTactical, "b_play_pause_t" }, + { EawCommandBarComponentIds.AdvisorHintPopupGalactic, "text_galactic_help" }, + { EawCommandBarComponentIds.AdvisorHintPopupTactical, "text_tactical_help" }, + { EawCommandBarComponentIds.AdvisorHintBack, "help_back" }, + { EawCommandBarComponentIds.TextOrganizeFleet00, "text_fleet1" }, + { EawCommandBarComponentIds.TextOrganizeFleet01, "text_fleet2" }, + { EawCommandBarComponentIds.TextOrganizeFleet02, "text_fleet3" }, + { EawCommandBarComponentIds.IconOrganizeFleet00, "icon_fleet01" }, + { EawCommandBarComponentIds.IconOrganizeFleet01, "icon_fleet02" }, + { EawCommandBarComponentIds.IconOrganizeFleet02, "icon_fleet03" }, + { EawCommandBarComponentIds.TextOrganizeLandFleet, "Text_Land_Fleet" }, + { EawCommandBarComponentIds.CsAbilityButton, "cs_ability_button" }, + { EawCommandBarComponentIds.CsAbilityText, "cs_ability_text" }, + { EawCommandBarComponentIds.MovieBoneGalactic, "Movie_galactic" }, + { EawCommandBarComponentIds.MovieBoneTactical, "Movie_tactical" }, + { EawCommandBarComponentIds.GenericCollision, "generic_collision" }, + { EawCommandBarComponentIds.GoodHeroShell, "good_hero_frame" }, + { EawCommandBarComponentIds.GoodHeroSlot00, "hero_slot_good00" }, + { EawCommandBarComponentIds.GoodHeroSlot01, "hero_slot_good01" }, + { EawCommandBarComponentIds.GoodHeroSlot02, "hero_slot_good02" }, + { EawCommandBarComponentIds.GoodHeroSlot03, "hero_slot_good03" }, + { EawCommandBarComponentIds.GoodHeroSlot04, "hero_slot_good04" }, + { EawCommandBarComponentIds.GoodHeroSlot05, "hero_slot_good05" }, + { EawCommandBarComponentIds.GoodHeroSlot06, "hero_slot_good06" }, + { EawCommandBarComponentIds.GoodHeroSlot07, "hero_slot_good07" }, + { EawCommandBarComponentIds.GoodHeroSlot08, "hero_slot_good08" }, + { EawCommandBarComponentIds.GoodHeroSlot09, "hero_slot_good09" }, + { EawCommandBarComponentIds.GoodHeroSlot10, "hero_slot_good10" }, + { EawCommandBarComponentIds.GoodHeroHealth00, "hero_hb_good00" }, + { EawCommandBarComponentIds.GoodHeroHealth01, "hero_hb_good01" }, + { EawCommandBarComponentIds.GoodHeroHealth02, "hero_hb_good02" }, + { EawCommandBarComponentIds.GoodHeroHealth03, "hero_hb_good03" }, + { EawCommandBarComponentIds.GoodHeroHealth04, "hero_hb_good04" }, + { EawCommandBarComponentIds.GoodHeroHealth05, "hero_hb_good05" }, + { EawCommandBarComponentIds.GoodHeroHealth06, "hero_hb_good06" }, + { EawCommandBarComponentIds.GoodHeroHealth07, "hero_hb_good07" }, + { EawCommandBarComponentIds.GoodHeroHealth08, "hero_hb_good08" }, + { EawCommandBarComponentIds.GoodHeroHealth09, "hero_hb_good09" }, + { EawCommandBarComponentIds.GoodHeroHealth10, "hero_hb_good10" }, + { EawCommandBarComponentIds.EvilHeroShell, "evil_hero_frame" }, + { EawCommandBarComponentIds.EvilHeroSlot00, "hero_slot_evil00" }, + { EawCommandBarComponentIds.EvilHeroSlot01, "hero_slot_evil01" }, + { EawCommandBarComponentIds.EvilHeroSlot02, "hero_slot_evil02" }, + { EawCommandBarComponentIds.EvilHeroSlot03, "hero_slot_evil03" }, + { EawCommandBarComponentIds.EvilHeroSlot04, "hero_slot_evil04" }, + { EawCommandBarComponentIds.EvilHeroSlot05, "hero_slot_evil05" }, + { EawCommandBarComponentIds.EvilHeroSlot06, "hero_slot_evil06" }, + { EawCommandBarComponentIds.EvilHeroSlot07, "hero_slot_evil07" }, + { EawCommandBarComponentIds.EvilHeroSlot08, "hero_slot_evil08" }, + { EawCommandBarComponentIds.EvilHeroSlot09, "hero_slot_evil09" }, + { EawCommandBarComponentIds.EvilHeroSlot10, "hero_slot_evil10" }, + { EawCommandBarComponentIds.EvilHeroHealth00, "hero_hb_evil00" }, + { EawCommandBarComponentIds.EvilHeroHealth01, "hero_hb_evil01" }, + { EawCommandBarComponentIds.EvilHeroHealth02, "hero_hb_evil02" }, + { EawCommandBarComponentIds.EvilHeroHealth03, "hero_hb_evil03" }, + { EawCommandBarComponentIds.EvilHeroHealth04, "hero_hb_evil04" }, + { EawCommandBarComponentIds.EvilHeroHealth05, "hero_hb_evil05" }, + { EawCommandBarComponentIds.EvilHeroHealth06, "hero_hb_evil06" }, + { EawCommandBarComponentIds.EvilHeroHealth07, "hero_hb_evil07" }, + { EawCommandBarComponentIds.EvilHeroHealth08, "hero_hb_evil08" }, + { EawCommandBarComponentIds.EvilHeroHealth09, "hero_hb_evil09" }, + { EawCommandBarComponentIds.EvilHeroHealth10, "hero_hb_evil10" }, + { EawCommandBarComponentIds.PauseShell, "pause_shell" }, + { EawCommandBarComponentIds.PauseText, "text_attack" }, + { EawCommandBarComponentIds.PauseButton, "attack_button" }, + { EawCommandBarComponentIds.StoryCampaignPendingBattleShell, "story_pending_battle_shell" }, + { EawCommandBarComponentIds.StoryCampaignPendingBattleText, "text_attack_single" }, + { EawCommandBarComponentIds.StoryCampaignPendingBattleButton, "attack_button_single" }, + }; + + private static readonly IReadOnlyDictionary FocSupportedComponents = new Dictionary { - { CommandBarComponentId.MainShell, "i_main_commandbar" }, - { CommandBarComponentId.ProductionOptions, "b_option_g" }, - { CommandBarComponentId.GalacticCamera, "b_camera_g" }, - { CommandBarComponentId.PlanetName, "Text_Planet" }, - { CommandBarComponentId.PlanetValue, "Text_Planetary_Income" }, - { CommandBarComponentId.PlanetAffiliation, "Text_Allegiance_A" }, - { CommandBarComponentId.PlayerCredits, "Text_Credits" }, - { CommandBarComponentId.ReinforcementCap, "Text_Planetary_Pop" }, - { CommandBarComponentId.PlanetNameTactical, "Text_Planet_tactical" }, - { CommandBarComponentId.PlanetInfo, "planet_info" }, - { CommandBarComponentId.Production0, "b_Create_00" }, - { CommandBarComponentId.Production1, "b_Create_01" }, - { CommandBarComponentId.Production2, "b_Create_02" }, - { CommandBarComponentId.Production3, "b_Create_03" }, - { CommandBarComponentId.Production4, "b_Create_04" }, - { CommandBarComponentId.Production5, "b_Create_05" }, - { CommandBarComponentId.Production6, "b_Create_06" }, - { CommandBarComponentId.Production7, "b_Create_07" }, - { CommandBarComponentId.Production8, "b_Create_08" }, - { CommandBarComponentId.Production9, "b_Create_09" }, - { CommandBarComponentId.Production10, "b_Create_10" }, - { CommandBarComponentId.Production11, "b_Create_11" }, - { CommandBarComponentId.Production12, "b_Create_12" }, - { CommandBarComponentId.Production13, "b_Create_13" }, - { CommandBarComponentId.Production14, "b_Create_14" }, - { CommandBarComponentId.Production15, "b_Create_15" }, - { CommandBarComponentId.Production16, "b_Create_16" }, - { CommandBarComponentId.Production17, "b_Create_17" }, - { CommandBarComponentId.Production18, "b_Create_18" }, - { CommandBarComponentId.Production19, "b_Create_19" }, - { CommandBarComponentId.Production20, "b_Create_20" }, - { CommandBarComponentId.Production21, "b_Create_21" }, - { CommandBarComponentId.Production22, "b_Create_22" }, - { CommandBarComponentId.Production23, "b_Create_23" }, - { CommandBarComponentId.Production24, "b_Create_24" }, - { CommandBarComponentId.Production25, "b_Create_25" }, - { CommandBarComponentId.Production26, "b_Create_26" }, - { CommandBarComponentId.Production27, "b_Create_27" }, - { CommandBarComponentId.Production28, "b_Create_28" }, - { CommandBarComponentId.Production29, "b_Create_29" }, - { CommandBarComponentId.Production30, "b_Create_30" }, - { CommandBarComponentId.Production31, "b_Create_31" }, - { CommandBarComponentId.Production32, "b_Create_32" }, - { CommandBarComponentId.Production33, "b_Create_33" }, - { CommandBarComponentId.Production34, "b_Create_34" }, - { CommandBarComponentId.Production35, "b_Create_35" }, - { CommandBarComponentId.Production36, "b_Create_36" }, - { CommandBarComponentId.Production37, "b_Create_37" }, - { CommandBarComponentId.Production38, "b_Create_38" }, - { CommandBarComponentId.Production39, "b_Create_39" }, - { CommandBarComponentId.Production40, "b_Create_40" }, - { CommandBarComponentId.Production41, "b_Create_41" }, - { CommandBarComponentId.Production42, "b_Create_42" }, - { CommandBarComponentId.Production43, "b_Create_43" }, - { CommandBarComponentId.Production44, "b_Create_44" }, - { CommandBarComponentId.Production45, "b_Create_45" }, - { CommandBarComponentId.Production46, "b_Create_46" }, - { CommandBarComponentId.Production47, "b_Create_47" }, - { CommandBarComponentId.DroidHelp, "b_droid_help" }, - { CommandBarComponentId.DroidHelpTactical, "b_droid_help_tactical" }, - { CommandBarComponentId.CurrentDay, "Text_Day_Counter" }, - { CommandBarComponentId.DayCredits, "Text_Day_Credits" }, - { CommandBarComponentId.PopulationCap, "Text_Pop_Cap" }, - { CommandBarComponentId.Filter0, "b_filters0" }, - { CommandBarComponentId.Filter1, "b_filters1" }, - { CommandBarComponentId.Filter2, "b_filters2" }, - { CommandBarComponentId.Filter3, "b_filters3" }, - { CommandBarComponentId.StoryArcButton, "b_story_arc_g" }, - { CommandBarComponentId.PlanetSummaryButton, "b_quick_ref" }, - { CommandBarComponentId.SpaceTab, "b_space_tab" }, - { CommandBarComponentId.LandTab, "b_planet_tab" }, - { CommandBarComponentId.Dial, "b_dial" }, - { CommandBarComponentId.ScrollRight, "b_scroll_right" }, - { CommandBarComponentId.ScrollLeft, "b_scroll_left" }, - { CommandBarComponentId.ZoomView, "b_zoom" }, - { CommandBarComponentId.PrevPlanet, "b_planet_left" }, - { CommandBarComponentId.NextPlanet, "b_planet_right" }, - { CommandBarComponentId.RadarGalactic, "radar_galactic" }, - { CommandBarComponentId.TechLevel, "Text_Tech_Level" }, - { CommandBarComponentId.BalancePip, "balance_pip" }, - { CommandBarComponentId.BuildQueue00, "queue00" }, - { CommandBarComponentId.BuildQueue01, "queue01" }, - { CommandBarComponentId.BuildQueue02, "queue02" }, - { CommandBarComponentId.BuildQueue03, "queue03" }, - { CommandBarComponentId.BuildQueue04, "queue04" }, - { CommandBarComponentId.BuildQueue05, "queue05" }, - { CommandBarComponentId.BuildQueue06, "queue06" }, - { CommandBarComponentId.BuildQueue07, "queue07" }, - { CommandBarComponentId.BuildQueue08, "queue08" }, - { CommandBarComponentId.BuildQueue09, "queue09" }, - { CommandBarComponentId.OrganizationShell, "i_main_organize" }, - { CommandBarComponentId.OrganizationCollision, "land_art_ALT0" }, - { CommandBarComponentId.SmugglerBox, "b_underground" }, - { CommandBarComponentId.SpaceStationUpgrade01, "space_Pad_1" }, - { CommandBarComponentId.SpaceStationUpgrade02, "space_Pad_02" }, - { CommandBarComponentId.HeroAbilitySlot, "Slot_00" }, - { CommandBarComponentId.PlanetOrganize0, "Pad_00" }, - { CommandBarComponentId.PlanetOrganize1, "Pad_01" }, - { CommandBarComponentId.PlanetOrganize2, "Pad_02" }, - { CommandBarComponentId.PlanetOrganize3, "Pad_03" }, - { CommandBarComponentId.PlanetOrganize4, "Pad_04" }, - { CommandBarComponentId.PlanetOrganize5, "Pad_05" }, - { CommandBarComponentId.PlanetOrganize6, "Pad_06" }, - { CommandBarComponentId.PlanetOrganize7, "Pad_07" }, - { CommandBarComponentId.PlanetOrganize8, "Pad_08" }, - { CommandBarComponentId.PlanetOrganize9, "Pad_09" }, - { CommandBarComponentId.SpecialStructureLand0, "Planet_structure_00" }, - { CommandBarComponentId.SpecialStructureLand1, "Planet_structure_01" }, - { CommandBarComponentId.SpecialStructureLand2, "Planet_structure_02" }, - { CommandBarComponentId.SpecialStructureLand3, "Planet_structure_03" }, - { CommandBarComponentId.SpecialStructureLand4, "Planet_structure_04" }, - { CommandBarComponentId.SpecialStructureLand5, "Planet_structure_05" }, - { CommandBarComponentId.SpecialStructureLand6, "Planet_structure_06" }, - { CommandBarComponentId.SpecialStructureLand7, "Planet_structure_07" }, - { CommandBarComponentId.SpecialStructureLand8, "Planet_structure_08" }, - { CommandBarComponentId.SpecialStructureLandSell, "g_ground_sell" }, - { CommandBarComponentId.BigFleet0, "q1_fleet" }, - { CommandBarComponentId.BigFleet1, "q2_fleet" }, - { CommandBarComponentId.BigFleet2, "q3_fleet" }, - { CommandBarComponentId.BigFleet3, "q4_fleet" }, - { CommandBarComponentId.Fleet0Slot0, "q1_0000" }, - { CommandBarComponentId.Fleet0Slot1, "q1_0001" }, - { CommandBarComponentId.Fleet0Slot2, "q1_0002" }, - { CommandBarComponentId.Fleet0Slot3, "q1_0003" }, - { CommandBarComponentId.Fleet0Slot4, "q1_0004" }, - { CommandBarComponentId.Fleet0Slot5, "q1_0005" }, - { CommandBarComponentId.Fleet0Slot6, "q1_0006" }, - { CommandBarComponentId.Fleet0Slot7, "q1_0100" }, - { CommandBarComponentId.Fleet0Slot8, "q1_0101" }, - { CommandBarComponentId.Fleet0Slot9, "q1_0102" }, - { CommandBarComponentId.Fleet0Slot10, "q1_0103" }, - { CommandBarComponentId.Fleet0Slot11, "q1_0104" }, - { CommandBarComponentId.Fleet0Slot12, "q1_0105" }, - { CommandBarComponentId.Fleet0Slot13, "q1_0106" }, - { CommandBarComponentId.Fleet0Slot14, "q1_0200" }, - { CommandBarComponentId.Fleet0Slot15, "q1_0201" }, - { CommandBarComponentId.Fleet0Slot16, "q1_0202" }, - { CommandBarComponentId.Fleet0Slot17, "q1_0203" }, - { CommandBarComponentId.Fleet0Slot18, "q1_0204" }, - { CommandBarComponentId.Fleet0Slot19, "q1_0205" }, - { CommandBarComponentId.Fleet0Slot20, "q1_0206" }, - { CommandBarComponentId.Fleet0Slot21, "q1_0300" }, - { CommandBarComponentId.Fleet0Slot22, "q1_0301" }, - { CommandBarComponentId.Fleet0Slot23, "q1_0302" }, - { CommandBarComponentId.Fleet0Slot24, "q1_0303" }, - { CommandBarComponentId.Fleet0Slot25, "q1_0304" }, - { CommandBarComponentId.Fleet0Slot26, "q1_0305" }, - { CommandBarComponentId.Fleet0Slot27, "q1_0306" }, - { CommandBarComponentId.Fleet0Slot28, "q1_0400" }, - { CommandBarComponentId.Fleet0Slot29, "q1_0401" }, - { CommandBarComponentId.Fleet0Slot30, "q1_0402" }, - { CommandBarComponentId.Fleet0Slot31, "q1_0403" }, - { CommandBarComponentId.Fleet0Slot32, "q1_0404" }, - { CommandBarComponentId.Fleet0Slot33, "q1_0405" }, - { CommandBarComponentId.Fleet0Slot34, "q1_0406" }, - { CommandBarComponentId.Fleet1Slot0, "q2_0000" }, - { CommandBarComponentId.Fleet1Slot1, "q2_0001" }, - { CommandBarComponentId.Fleet1Slot2, "q2_0002" }, - { CommandBarComponentId.Fleet1Slot3, "q2_0003" }, - { CommandBarComponentId.Fleet1Slot4, "q2_0004" }, - { CommandBarComponentId.Fleet1Slot5, "q2_0005" }, - { CommandBarComponentId.Fleet1Slot6, "q2_0006" }, - { CommandBarComponentId.Fleet1Slot7, "q2_0100" }, - { CommandBarComponentId.Fleet1Slot8, "q2_0101" }, - { CommandBarComponentId.Fleet1Slot9, "q2_0102" }, - { CommandBarComponentId.Fleet1Slot10, "q2_0103" }, - { CommandBarComponentId.Fleet1Slot11, "q2_0104" }, - { CommandBarComponentId.Fleet1Slot12, "q2_0105" }, - { CommandBarComponentId.Fleet1Slot13, "q2_0106" }, - { CommandBarComponentId.Fleet1Slot14, "q2_0200" }, - { CommandBarComponentId.Fleet1Slot15, "q2_0201" }, - { CommandBarComponentId.Fleet1Slot16, "q2_0202" }, - { CommandBarComponentId.Fleet1Slot17, "q2_0203" }, - { CommandBarComponentId.Fleet1Slot18, "q2_0204" }, - { CommandBarComponentId.Fleet1Slot19, "q2_0205" }, - { CommandBarComponentId.Fleet1Slot20, "q2_0206" }, - { CommandBarComponentId.Fleet1Slot21, "q2_0300" }, - { CommandBarComponentId.Fleet1Slot22, "q2_0301" }, - { CommandBarComponentId.Fleet1Slot23, "q2_0302" }, - { CommandBarComponentId.Fleet1Slot24, "q2_0303" }, - { CommandBarComponentId.Fleet1Slot25, "q2_0304" }, - { CommandBarComponentId.Fleet1Slot26, "q2_0305" }, - { CommandBarComponentId.Fleet1Slot27, "q2_0306" }, - { CommandBarComponentId.Fleet1Slot28, "q2_0400" }, - { CommandBarComponentId.Fleet1Slot29, "q2_0401" }, - { CommandBarComponentId.Fleet1Slot30, "q2_0402" }, - { CommandBarComponentId.Fleet1Slot31, "q2_0403" }, - { CommandBarComponentId.Fleet1Slot32, "q2_0404" }, - { CommandBarComponentId.Fleet1Slot33, "q2_0405" }, - { CommandBarComponentId.Fleet1Slot34, "q2_0406" }, - { CommandBarComponentId.Fleet2Slot0, "q3_0000" }, - { CommandBarComponentId.Fleet2Slot1, "q3_0001" }, - { CommandBarComponentId.Fleet2Slot2, "q3_0002" }, - { CommandBarComponentId.Fleet2Slot3, "q3_0003" }, - { CommandBarComponentId.Fleet2Slot4, "q3_0004" }, - { CommandBarComponentId.Fleet2Slot5, "q3_0005" }, - { CommandBarComponentId.Fleet2Slot6, "q3_0006" }, - { CommandBarComponentId.Fleet2Slot7, "q3_0100" }, - { CommandBarComponentId.Fleet2Slot8, "q3_0101" }, - { CommandBarComponentId.Fleet2Slot9, "q3_0102" }, - { CommandBarComponentId.Fleet2Slot10, "q3_0103" }, - { CommandBarComponentId.Fleet2Slot11, "q3_0104" }, - { CommandBarComponentId.Fleet2Slot12, "q3_0105" }, - { CommandBarComponentId.Fleet2Slot13, "q3_0106" }, - { CommandBarComponentId.Fleet2Slot14, "q3_0200" }, - { CommandBarComponentId.Fleet2Slot15, "q3_0201" }, - { CommandBarComponentId.Fleet2Slot16, "q3_0202" }, - { CommandBarComponentId.Fleet2Slot17, "q3_0203" }, - { CommandBarComponentId.Fleet2Slot18, "q3_0204" }, - { CommandBarComponentId.Fleet2Slot19, "q3_0205" }, - { CommandBarComponentId.Fleet2Slot20, "q3_0206" }, - { CommandBarComponentId.Fleet2Slot21, "q3_0300" }, - { CommandBarComponentId.Fleet2Slot22, "q3_0301" }, - { CommandBarComponentId.Fleet2Slot23, "q3_0302" }, - { CommandBarComponentId.Fleet2Slot24, "q3_0303" }, - { CommandBarComponentId.Fleet2Slot25, "q3_0304" }, - { CommandBarComponentId.Fleet2Slot26, "q3_0305" }, - { CommandBarComponentId.Fleet2Slot27, "q3_0306" }, - { CommandBarComponentId.Fleet2Slot28, "q3_0400" }, - { CommandBarComponentId.Fleet2Slot29, "q3_0401" }, - { CommandBarComponentId.Fleet2Slot30, "q3_0402" }, - { CommandBarComponentId.Fleet2Slot31, "q3_0403" }, - { CommandBarComponentId.Fleet2Slot32, "q3_0404" }, - { CommandBarComponentId.Fleet2Slot33, "q3_0405" }, - { CommandBarComponentId.Fleet2Slot34, "q3_0406" }, - { CommandBarComponentId.TacticalMain, "i_main_skirmish" }, - { CommandBarComponentId.TacticalOptions, "b_option_t" }, - { CommandBarComponentId.TacticalCamera, "b_camera_t" }, - { CommandBarComponentId.TacticalBeacon, "b_beacon_t" }, - { CommandBarComponentId.TacticalWeapon0, "b_special_weapon" }, - { CommandBarComponentId.TacticalWeapon1, "b_special_weapon2" }, - { CommandBarComponentId.TacticalReinforce, "b_reinforcement" }, - { CommandBarComponentId.TacticalRetreat, "b_retreat" }, - { CommandBarComponentId.TacticalStoryArc, "b_story_arc_t" }, - { CommandBarComponentId.TacticalTechLevel, "Text_Tactical_Tech" }, - { CommandBarComponentId.TacticalAttack, "c_button00" }, - { CommandBarComponentId.TacticalAttackMove, "c_button01" }, - { CommandBarComponentId.TacticalMove, "c_button02" }, - { CommandBarComponentId.TacticalWaypoint, "c_button03" }, - { CommandBarComponentId.TacticalStop, "c_button04" }, - { CommandBarComponentId.TacticalGuard, "c_button05" }, - { CommandBarComponentId.SpaceTacticalRadar, "radar" }, - { CommandBarComponentId.SpaceTacticalCredits, "Text_Credits_tactical" }, - { CommandBarComponentId.SpaceTacticalGrabBar, "st_grab_bar" }, - { CommandBarComponentId.SpaceTacticalHealthBar, "st_health_bar" }, - { CommandBarComponentId.SpaceTacticalBracketSmall, "st_bracket_small" }, - { CommandBarComponentId.SpaceTacticalBracketMedium, "st_bracket_medium" }, - { CommandBarComponentId.SpaceTacticalBracketLarge, "st_bracket_large" }, - { CommandBarComponentId.SpaceTacticalHeroIcon, "st_hero_icon" }, - { CommandBarComponentId.SpaceTacticalHeroHealth, "st_hero_health" }, - { CommandBarComponentId.SpaceTacticalHealth, "st_health" }, - { CommandBarComponentId.SpaceTacticalHealthMedium, "st_health_medium" }, - { CommandBarComponentId.SpaceTacticalHealthLarge, "st_health_large" }, - { CommandBarComponentId.SpaceTacticalShields, "st_shields" }, - { CommandBarComponentId.SpaceTacticalShieldsMedium, "st_shields_medium" }, - { CommandBarComponentId.SpaceTacticalShieldsLarge, "st_shields_large" }, - { CommandBarComponentId.SpaceTacticalPower, "st_power" }, - { CommandBarComponentId.SpaceTacticalControlGroup, "st_control_group" }, - { CommandBarComponentId.SpaceTacticalAbilityIcon, "st_ability_icon" }, - { CommandBarComponentId.SpaceTacticalGarrisonIcon, "st_garrison_icon" }, - { CommandBarComponentId.LandTacticalWeatherIcon, "lt_weather_icon" }, - { CommandBarComponentId.GarrisonSlotIcon, "garrison_slot_icon" }, - { CommandBarComponentId.DsFireShell, "ds_shell" }, - { CommandBarComponentId.DsFire, "deathstar_switch" }, - { CommandBarComponentId.DsCountdownTimer, "DS_Countdown_Timer" }, - { CommandBarComponentId.TacticalSelect00, "s_select_00" }, - { CommandBarComponentId.TacticalSelect01, "s_select_01" }, - { CommandBarComponentId.TacticalSelect02, "s_select_02" }, - { CommandBarComponentId.TacticalSelect03, "s_select_03" }, - { CommandBarComponentId.TacticalSelect04, "s_select_04" }, - { CommandBarComponentId.TacticalSelect05, "s_select_05" }, - { CommandBarComponentId.TacticalSelect06, "s_select_06" }, - { CommandBarComponentId.TacticalSelect07, "s_select_07" }, - { CommandBarComponentId.TacticalSelect08, "s_select_08" }, - { CommandBarComponentId.TacticalSelect09, "s_select_09" }, - { CommandBarComponentId.TacticalSelect10, "s_select_10" }, - { CommandBarComponentId.TacticalSelect11, "s_select_11" }, - { CommandBarComponentId.TacticalSelect12, "s_select_12" }, - { CommandBarComponentId.TacticalSelect13, "s_select_13" }, - { CommandBarComponentId.TacticalSelect14, "s_select_14" }, - { CommandBarComponentId.TacticalSelect15, "s_select_15" }, - { CommandBarComponentId.TacticalSelect16, "s_select_16" }, - { CommandBarComponentId.TacticalSelect17, "s_select_17" }, - { CommandBarComponentId.TacticalSelect18, "s_select_18" }, - { CommandBarComponentId.TacticalSelect19, "s_select_19" }, - { CommandBarComponentId.TacticalSelect20, "s_select_20" }, - { CommandBarComponentId.TacticalSelect21, "s_select_21" }, - { CommandBarComponentId.TacticalSelect22, "s_select_22" }, - { CommandBarComponentId.TacticalSelect23, "s_select_23" }, - { CommandBarComponentId.TacticalSelect24, "s_select_24" }, - { CommandBarComponentId.TacticalSelect25, "s_select_25" }, - { CommandBarComponentId.TacticalSelect26, "s_select_26" }, - { CommandBarComponentId.TacticalSelect27, "s_select_27" }, - { CommandBarComponentId.TacticalSelect28, "s_select_28" }, - { CommandBarComponentId.TacticalSelect29, "s_select_29" }, - { CommandBarComponentId.TacticalSelect30, "s_select_30" }, - { CommandBarComponentId.TacticalSelect31, "s_select_31" }, - { CommandBarComponentId.TacticalSelect32, "s_select_32" }, - { CommandBarComponentId.TacticalSelect33, "s_select_33" }, - { CommandBarComponentId.TacticalSelect34, "s_select_34" }, - { CommandBarComponentId.TacticalSelect35, "s_select_35" }, - { CommandBarComponentId.TacticalSelect36, "s_select_36" }, - { CommandBarComponentId.TacticalSelect37, "s_select_37" }, - { CommandBarComponentId.TacticalSelect38, "s_select_38" }, - { CommandBarComponentId.TacticalSelect39, "s_select_39" }, - { CommandBarComponentId.TacticalSelect40, "s_select_40" }, - { CommandBarComponentId.TacticalSelect41, "s_select_41" }, - { CommandBarComponentId.TacticalSelect42, "s_select_42" }, - { CommandBarComponentId.TacticalSelect43, "s_select_43" }, - { CommandBarComponentId.TacticalSelect44, "s_select_44" }, - { CommandBarComponentId.TacticalSelect45, "s_select_45" }, - { CommandBarComponentId.TacticalSelect46, "s_select_46" }, - { CommandBarComponentId.TacticalSelect47, "s_select_47" }, - { CommandBarComponentId.TacticalSelectHealth00, "s_health_00" }, - { CommandBarComponentId.TacticalSelectHealth01, "s_health_01" }, - { CommandBarComponentId.TacticalSelectHealth02, "s_health_02" }, - { CommandBarComponentId.TacticalSelectHealth03, "s_health_03" }, - { CommandBarComponentId.TacticalSelectHealth04, "s_health_04" }, - { CommandBarComponentId.TacticalSelectHealth05, "s_health_05" }, - { CommandBarComponentId.TacticalSelectHealth06, "s_health_06" }, - { CommandBarComponentId.TacticalSelectHealth07, "s_health_07" }, - { CommandBarComponentId.TacticalSelectHealth08, "s_health_08" }, - { CommandBarComponentId.TacticalSelectHealth09, "s_health_09" }, - { CommandBarComponentId.TacticalSelectHealth10, "s_health_10" }, - { CommandBarComponentId.TacticalSelectHealth11, "s_health_11" }, - { CommandBarComponentId.TacticalSelectHealth12, "s_health_12" }, - { CommandBarComponentId.TacticalSelectHealth13, "s_health_13" }, - { CommandBarComponentId.TacticalSelectHealth14, "s_health_14" }, - { CommandBarComponentId.TacticalSelectHealth15, "s_health_15" }, - { CommandBarComponentId.TacticalSelectHealth16, "s_health_16" }, - { CommandBarComponentId.TacticalSelectHealth17, "s_health_17" }, - { CommandBarComponentId.TacticalSelectHealth18, "s_health_18" }, - { CommandBarComponentId.TacticalSelectHealth19, "s_health_19" }, - { CommandBarComponentId.TacticalSelectHealth20, "s_health_20" }, - { CommandBarComponentId.TacticalSelectHealth21, "s_health_21" }, - { CommandBarComponentId.TacticalSelectHealth22, "s_health_22" }, - { CommandBarComponentId.TacticalSelectHealth23, "s_health_23" }, - { CommandBarComponentId.TacticalSelectHealth24, "s_health_24" }, - { CommandBarComponentId.TacticalSelectHealth25, "s_health_25" }, - { CommandBarComponentId.TacticalSelectHealth26, "s_health_26" }, - { CommandBarComponentId.TacticalSelectHealth27, "s_health_27" }, - { CommandBarComponentId.TacticalSelectHealth28, "s_health_28" }, - { CommandBarComponentId.TacticalSelectHealth29, "s_health_29" }, - { CommandBarComponentId.TacticalSelectHealth30, "s_health_30" }, - { CommandBarComponentId.TacticalSelectHealth31, "s_health_31" }, - { CommandBarComponentId.TacticalSelectHealth32, "s_health_32" }, - { CommandBarComponentId.TacticalSelectHealth33, "s_health_33" }, - { CommandBarComponentId.TacticalSelectHealth34, "s_health_34" }, - { CommandBarComponentId.TacticalSelectHealth35, "s_health_35" }, - { CommandBarComponentId.TacticalSelectHealth36, "s_health_36" }, - { CommandBarComponentId.TacticalSelectHealth37, "s_health_37" }, - { CommandBarComponentId.TacticalSelectHealth38, "s_health_38" }, - { CommandBarComponentId.TacticalSelectHealth39, "s_health_39" }, - { CommandBarComponentId.TacticalSelectHealth40, "s_health_40" }, - { CommandBarComponentId.TacticalSelectHealth41, "s_health_41" }, - { CommandBarComponentId.TacticalSelectHealth42, "s_health_42" }, - { CommandBarComponentId.TacticalSelectHealth43, "s_health_43" }, - { CommandBarComponentId.TacticalSelectHealth44, "s_health_44" }, - { CommandBarComponentId.TacticalSelectHealth45, "s_health_45" }, - { CommandBarComponentId.TacticalSelectHealth46, "s_health_46" }, - { CommandBarComponentId.TacticalSelectHealth47, "s_health_47" }, - { CommandBarComponentId.TacticalSelectShield00, "s_shield_00" }, - { CommandBarComponentId.TacticalSelectShield01, "s_shield_01" }, - { CommandBarComponentId.TacticalSelectShield02, "s_shield_02" }, - { CommandBarComponentId.TacticalSelectShield03, "s_shield_03" }, - { CommandBarComponentId.TacticalSelectShield04, "s_shield_04" }, - { CommandBarComponentId.TacticalSelectShield05, "s_shield_05" }, - { CommandBarComponentId.TacticalSelectShield06, "s_shield_06" }, - { CommandBarComponentId.TacticalSelectShield07, "s_shield_07" }, - { CommandBarComponentId.TacticalSelectShield08, "s_shield_08" }, - { CommandBarComponentId.TacticalSelectShield09, "s_shield_09" }, - { CommandBarComponentId.TacticalSelectShield10, "s_shield_10" }, - { CommandBarComponentId.TacticalSelectShield11, "s_shield_11" }, - { CommandBarComponentId.TacticalSelectShield12, "s_shield_12" }, - { CommandBarComponentId.TacticalSelectShield13, "s_shield_13" }, - { CommandBarComponentId.TacticalSelectShield14, "s_shield_14" }, - { CommandBarComponentId.TacticalSelectShield15, "s_shield_15" }, - { CommandBarComponentId.TacticalSelectShield16, "s_shield_16" }, - { CommandBarComponentId.TacticalSelectShield17, "s_shield_17" }, - { CommandBarComponentId.TacticalSelectShield18, "s_shield_18" }, - { CommandBarComponentId.TacticalSelectShield19, "s_shield_19" }, - { CommandBarComponentId.TacticalSelectShield20, "s_shield_20" }, - { CommandBarComponentId.TacticalSelectShield21, "s_shield_21" }, - { CommandBarComponentId.TacticalSelectShield22, "s_shield_22" }, - { CommandBarComponentId.TacticalSelectShield23, "s_shield_23" }, - { CommandBarComponentId.TacticalSelectShield24, "s_shield_24" }, - { CommandBarComponentId.TacticalSelectShield25, "s_shield_25" }, - { CommandBarComponentId.TacticalSelectShield26, "s_shield_26" }, - { CommandBarComponentId.TacticalSelectShield27, "s_shield_27" }, - { CommandBarComponentId.TacticalSelectShield28, "s_shield_28" }, - { CommandBarComponentId.TacticalSelectShield29, "s_shield_29" }, - { CommandBarComponentId.TacticalSelectShield30, "s_shield_30" }, - { CommandBarComponentId.TacticalSelectShield31, "s_shield_31" }, - { CommandBarComponentId.TacticalSelectShield32, "s_shield_32" }, - { CommandBarComponentId.TacticalSelectShield33, "s_shield_33" }, - { CommandBarComponentId.TacticalSelectShield34, "s_shield_34" }, - { CommandBarComponentId.TacticalSelectShield35, "s_shield_35" }, - { CommandBarComponentId.TacticalSelectShield36, "s_shield_36" }, - { CommandBarComponentId.TacticalSelectShield37, "s_shield_37" }, - { CommandBarComponentId.TacticalSelectShield38, "s_shield_38" }, - { CommandBarComponentId.TacticalSelectShield39, "s_shield_39" }, - { CommandBarComponentId.TacticalSelectShield40, "s_shield_40" }, - { CommandBarComponentId.TacticalSelectShield41, "s_shield_41" }, - { CommandBarComponentId.TacticalSelectShield42, "s_shield_42" }, - { CommandBarComponentId.TacticalSelectShield43, "s_shield_43" }, - { CommandBarComponentId.TacticalSelectShield44, "s_shield_44" }, - { CommandBarComponentId.TacticalSelectShield45, "s_shield_45" }, - { CommandBarComponentId.TacticalSelectShield46, "s_shield_46" }, - { CommandBarComponentId.TacticalSelectShield47, "s_shield_47" }, - { CommandBarComponentId.TacticalBorder00, "special_border_00" }, - { CommandBarComponentId.TacticalBorder01, "special_border_01" }, - { CommandBarComponentId.TacticalBorder02, "special_border_02" }, - { CommandBarComponentId.TacticalBorder03, "special_border_03" }, - { CommandBarComponentId.TacticalBorder04, "special_border_04" }, - { CommandBarComponentId.TacticalBorder05, "special_border_05" }, - { CommandBarComponentId.TacticalBorder06, "special_border_06" }, - { CommandBarComponentId.TacticalBorder07, "special_border_07" }, - { CommandBarComponentId.TacticalBorder08, "special_border_08" }, - { CommandBarComponentId.TacticalBorder09, "special_border_09" }, - { CommandBarComponentId.TacticalBorder10, "special_border_10" }, - { CommandBarComponentId.TacticalBorder11, "special_border_11" }, - { CommandBarComponentId.TacticalSelectButton00, "special_button_00" }, - { CommandBarComponentId.TacticalSelectButton01, "special_button_01" }, - { CommandBarComponentId.TacticalSelectButton02, "special_button_02" }, - { CommandBarComponentId.TacticalSelectButton03, "special_button_03" }, - { CommandBarComponentId.TacticalSelectButton04, "special_button_04" }, - { CommandBarComponentId.TacticalSelectButton05, "special_button_05" }, - { CommandBarComponentId.TacticalSelectButton06, "special_button_06" }, - { CommandBarComponentId.TacticalSelectButton07, "special_button_07" }, - { CommandBarComponentId.TacticalSelectButton08, "special_button_08" }, - { CommandBarComponentId.TacticalSelectButton09, "special_button_09" }, - { CommandBarComponentId.TacticalSelectButton10, "special_button_10" }, - { CommandBarComponentId.TacticalSelectButton11, "special_button_11" }, - { CommandBarComponentId.TacticalSelectButton12, "special_button_12" }, - { CommandBarComponentId.TacticalSelectButton13, "special_button_13" }, - { CommandBarComponentId.TacticalSelectButton14, "special_button_14" }, - { CommandBarComponentId.TacticalSelectButton15, "special_button_15" }, - { CommandBarComponentId.TacticalSelectButton16, "special_button_16" }, - { CommandBarComponentId.TacticalSelectButton17, "special_button_17" }, - { CommandBarComponentId.TacticalSelectButton18, "special_button_18" }, - { CommandBarComponentId.TacticalSelectButton19, "special_button_19" }, - { CommandBarComponentId.TacticalSelectButton20, "special_button_20" }, - { CommandBarComponentId.TacticalSelectButton21, "special_button_21" }, - { CommandBarComponentId.TacticalSelectButton22, "special_button_22" }, - { CommandBarComponentId.TacticalSelectButton23, "special_button_23" }, - { CommandBarComponentId.TacticalSelectButton24, "special_button_24" }, - { CommandBarComponentId.TacticalSelectButton25, "special_button_25" }, - { CommandBarComponentId.TacticalSelectButton26, "special_button_26" }, - { CommandBarComponentId.TacticalSelectButton27, "special_button_27" }, - { CommandBarComponentId.TacticalSelectButton28, "special_button_28" }, - { CommandBarComponentId.TacticalSelectButton29, "special_button_29" }, - { CommandBarComponentId.TacticalSelectButton30, "special_button_30" }, - { CommandBarComponentId.TacticalSelectButton31, "special_button_31" }, - { CommandBarComponentId.TacticalSelectButton32, "special_button_32" }, - { CommandBarComponentId.TacticalSelectButton33, "special_button_33" }, - { CommandBarComponentId.TacticalSelectButton34, "special_button_34" }, - { CommandBarComponentId.TacticalSelectButton35, "special_button_35" }, - { CommandBarComponentId.TacticalSelectButton36, "special_button_36" }, - { CommandBarComponentId.TacticalSelectButton37, "special_button_37" }, - { CommandBarComponentId.TacticalSelectButton38, "special_button_38" }, - { CommandBarComponentId.TacticalSelectButton39, "special_button_39" }, - { CommandBarComponentId.TacticalSelectButton40, "special_button_40" }, - { CommandBarComponentId.TacticalSelectButton41, "special_button_41" }, - { CommandBarComponentId.TacticalSelectButton42, "special_button_42" }, - { CommandBarComponentId.TacticalSelectButton43, "special_button_43" }, - { CommandBarComponentId.TacticalSelectButton44, "special_button_44" }, - { CommandBarComponentId.TacticalSelectButton45, "special_button_45" }, - { CommandBarComponentId.TacticalSelectButton46, "special_button_46" }, - { CommandBarComponentId.TacticalSelectButton47, "special_button_47" }, - { CommandBarComponentId.TacticalBuildQueue00, "tqueue00" }, - { CommandBarComponentId.TacticalBuildQueue01, "tqueue01" }, - { CommandBarComponentId.TacticalBuildQueue02, "tqueue02" }, - { CommandBarComponentId.TacticalBuildQueue03, "tqueue03" }, - { CommandBarComponentId.TacticalBuildQueue04, "tqueue04" }, - { CommandBarComponentId.TacticalBuildQueue05, "tqueue05" }, - { CommandBarComponentId.TacticalBuildQueue06, "tqueue06" }, - { CommandBarComponentId.TacticalBuildQueue07, "tqueue07" }, - { CommandBarComponentId.TacticalBuildQueue08, "tqueue08" }, - { CommandBarComponentId.TacticalBuildQueue09, "tqueue09" }, - { CommandBarComponentId.TooltipBack, "tooltip_back" }, - { CommandBarComponentId.TooltipName, "tooltip_name" }, - { CommandBarComponentId.TooltipPrice, "tooltip_price" }, - { CommandBarComponentId.TooltipIcon, "tooltip_icon" }, - { CommandBarComponentId.TooltipIconLand, "tooltip_icon_land" }, - { CommandBarComponentId.TooltipLeftJustified, "tooltip_left_text" }, - { CommandBarComponentId.EncyclopediaBack, "encyclopedia_back" }, - { CommandBarComponentId.EncyclopediaHeaderText, "encyclopedia_header_text" }, - { CommandBarComponentId.EncyclopediaText, "encyclopedia_text" }, - { CommandBarComponentId.EncyclopediaRightText, "encyclopedia_right_text" }, - { CommandBarComponentId.EncyclopediaCenterText, "encyclopedia_center_text" }, - { CommandBarComponentId.EncyclopediaIcon, "encyclopedia_icon" }, - { CommandBarComponentId.EncyclopediaCostText, "encyclopedia_cost_text" }, - { CommandBarComponentId.ZoomedBack, "zoomed_back" }, - { CommandBarComponentId.ZoomedHeaderText, "zoomed_header_text" }, - { CommandBarComponentId.ZoomedText, "zoomed_text" }, - { CommandBarComponentId.ZoomedRightText, "zoomed_right_text" }, - { CommandBarComponentId.ZoomedCenterText, "zoomed_center_text" }, - { CommandBarComponentId.ZoomedCostText, "zoomed_cost_text" }, - { CommandBarComponentId.GPlanetFleet, "g_planet_fleet" }, - { CommandBarComponentId.GPlanetName, "g_planet_name" }, - { CommandBarComponentId.GPlanetValue, "g_planet_value" }, - { CommandBarComponentId.GPoliticalControl, "g_political_control" }, - { CommandBarComponentId.GSpaceLevel, "g_space_level" }, - { CommandBarComponentId.GSpaceIcon, "g_space_icon" }, - { CommandBarComponentId.GSpaceLevelPips, "g_space_level_pips" }, - { CommandBarComponentId.GGroundLevel, "g_ground_level" }, - { CommandBarComponentId.GGroundIcon, "g_ground_icon" }, - { CommandBarComponentId.GGroundLevelPips, "g_ground_level_pips" }, - { CommandBarComponentId.GConflict, "g_conflict" }, - { CommandBarComponentId.GHero, "g_hero" }, - { CommandBarComponentId.GEnemyHero, "g_enemy_hero" }, - { CommandBarComponentId.GBuild, "g_build" }, - { CommandBarComponentId.GSmuggler, "g_smuggler" }, - { CommandBarComponentId.GBountyHunter, "g_bounty_hunter" }, - { CommandBarComponentId.GPlanetLandForces, "g_planet_land_forces" }, - { CommandBarComponentId.GGalacticRadarBlip, "g_radar_blip" }, - { CommandBarComponentId.GGalacticRadarView, "g_radar_view" }, - { CommandBarComponentId.GSmuggled, "g_smuggled" }, - { CommandBarComponentId.GSpecialAbility, "g_special_ability" }, - { CommandBarComponentId.GHeroIcon, "g_hero_icon" }, - { CommandBarComponentId.GPlanetRing, "g_planet_ring" }, - { CommandBarComponentId.GWeather, "g_weather" }, - { CommandBarComponentId.GPlanetAbility, "g_planet_ability" }, - { CommandBarComponentId.GCorruptionText, "g_corruption_text" }, - { CommandBarComponentId.GCorruptionIcon, "g_corruption_icon" }, - { CommandBarComponentId.TutorialText, "tutorial_text" }, - { CommandBarComponentId.TutorialTextBack, "tutorial_text_back" }, - { CommandBarComponentId.RadarBlip, "radar_blip" }, - { CommandBarComponentId.TacticalBuildButtonShell, "i_build_buttons" }, - { CommandBarComponentId.TacticalBuildButton0, "Build_00" }, - { CommandBarComponentId.TacticalBuildButton1, "Build_01" }, - { CommandBarComponentId.TacticalBuildButton2, "Build_02" }, - { CommandBarComponentId.TacticalBuildButton3, "Build_03" }, - { CommandBarComponentId.TacticalBuildButton4, "Build_04" }, - { CommandBarComponentId.TacticalBuildButton5, "Build_05" }, - { CommandBarComponentId.TacticalSellButton, "tactical_sell" }, - { CommandBarComponentId.ReinforcementShell, "i_main_reinforce" }, - { CommandBarComponentId.ReinforcementCancel, "r_close" }, - { CommandBarComponentId.ReinforcementSlot00, "r_0000" }, - { CommandBarComponentId.ReinforcementSlot01, "r_0001" }, - { CommandBarComponentId.ReinforcementSlot02, "r_0002" }, - { CommandBarComponentId.ReinforcementSlot03, "r_0003" }, - { CommandBarComponentId.ReinforcementSlot04, "r_0100" }, - { CommandBarComponentId.ReinforcementSlot05, "r_0101" }, - { CommandBarComponentId.ReinforcementSlot06, "r_0102" }, - { CommandBarComponentId.ReinforcementSlot07, "r_0103" }, - { CommandBarComponentId.ReinforcementSlot08, "r_0200" }, - { CommandBarComponentId.ReinforcementSlot09, "r_0201" }, - { CommandBarComponentId.ReinforcementSlot10, "r_0202" }, - { CommandBarComponentId.ReinforcementSlot11, "r_0203" }, - { CommandBarComponentId.ReinforcementSlot12, "r_0300" }, - { CommandBarComponentId.ReinforcementSlot13, "r_0301" }, - { CommandBarComponentId.ReinforcementSlot14, "r_0302" }, - { CommandBarComponentId.ReinforcementSlot15, "r_0303" }, - { CommandBarComponentId.ReinforcementSlot16, "r_0400" }, - { CommandBarComponentId.ReinforcementSlot17, "r_0401" }, - { CommandBarComponentId.ReinforcementSlot18, "r_0402" }, - { CommandBarComponentId.ReinforcementSlot19, "r_0403" }, - { CommandBarComponentId.ReinforcementCap2, "r_pop_icon" }, - { CommandBarComponentId.ReinforcementCap2Text, "r_pop_text" }, - { CommandBarComponentId.ReinforcementCounter, "reinforcement_counter" }, - { CommandBarComponentId.GarrisonRespawnCounter, "garrison_respawn_counter" }, - { CommandBarComponentId.SkirmishUpgrade, "skirmish_upgrade" }, - { CommandBarComponentId.PendingBattleShell, "pending_battle_shell" }, - { CommandBarComponentId.PendingBattleText, "text_attack_choice" }, - { CommandBarComponentId.PendingBattleButton, "choice_button_left" }, - { CommandBarComponentId.PendingBattleAutoresolve, "choice_button_right" }, - { CommandBarComponentId.PendingBattleGraphLeft, "graph_left" }, - { CommandBarComponentId.PendingBattleGraphRight, "graph_right" }, - { CommandBarComponentId.TatcicalAutoresolveShell, "autoresolve_shell" }, - { CommandBarComponentId.TacticalAutoresolveButton, "resolve_button" }, - { CommandBarComponentId.TacticalAutoresolveGraphLeft, "graph_left_resolve" }, - { CommandBarComponentId.TacticalAutoresolveGraphRight, "graph_right_resolve" }, - { CommandBarComponentId.ObjectiveBack, "objective_back" }, - { CommandBarComponentId.ObjectiveHeaderText, "objective_header_text" }, - { CommandBarComponentId.ObjectiveText, "objective_text" }, - { CommandBarComponentId.ObjectiveIcon, "objective_icon" }, - { CommandBarComponentId.GuiDialogTooltip, "gui_dialog_tooltip" }, - { CommandBarComponentId.TargetUnitTypeShell, "target_type_back" }, - { CommandBarComponentId.TargetUnitTypeTitle, "h_title" }, - { CommandBarComponentId.TargetUnitTypeCancel, "h_close" }, - { CommandBarComponentId.TargetUnitTypeDescription, "text_hero" }, - { CommandBarComponentId.TargetUnitTypeSlot00, "h_0000" }, - { CommandBarComponentId.TargetUnitTypeSlot01, "h_0001" }, - { CommandBarComponentId.TargetUnitTypeSlot02, "h_0100" }, - { CommandBarComponentId.TargetUnitTypeSlot03, "h_0101" }, - { CommandBarComponentId.TargetUnitTypeSlot04, "h_0200" }, - { CommandBarComponentId.TargetUnitTypeSlot05, "h_0201" }, - { CommandBarComponentId.TargetUnitTypeSlot06, "h_0300" }, - { CommandBarComponentId.TargetUnitTypeSlot07, "h_0301" }, - { CommandBarComponentId.TargetUnitTypeSlot08, "h_0400" }, - { CommandBarComponentId.TargetUnitTypeSlot09, "h_0401" }, - { CommandBarComponentId.TargetUnitTypeSlot10, "h_0500" }, - { CommandBarComponentId.TargetUnitTypeSlot11, "h_0501" }, - { CommandBarComponentId.TargetUnitTypeName00, "h_name_00" }, - { CommandBarComponentId.TargetUnitTypeName01, "h_name_01" }, - { CommandBarComponentId.TargetUnitTypeName02, "h_name_02" }, - { CommandBarComponentId.TargetUnitTypeName03, "h_name_03" }, - { CommandBarComponentId.TargetUnitTypeName04, "h_name_04" }, - { CommandBarComponentId.TargetUnitTypeName05, "h_name_05" }, - { CommandBarComponentId.TargetUnitTypeName06, "h_name_06" }, - { CommandBarComponentId.TargetUnitTypeName07, "h_name_07" }, - { CommandBarComponentId.TargetUnitTypeName08, "h_name_08" }, - { CommandBarComponentId.TargetUnitTypeName09, "h_name_09" }, - { CommandBarComponentId.TargetUnitTypeName10, "h_name_10" }, - { CommandBarComponentId.TargetUnitTypeName11, "h_name_11" }, - { CommandBarComponentId.TargetUnitTypePrice00, "h_price_00" }, - { CommandBarComponentId.TargetUnitTypePrice01, "h_price_01" }, - { CommandBarComponentId.TargetUnitTypePrice02, "h_price_02" }, - { CommandBarComponentId.TargetUnitTypePrice03, "h_price_03" }, - { CommandBarComponentId.TargetUnitTypePrice04, "h_price_04" }, - { CommandBarComponentId.TargetUnitTypePrice05, "h_price_05" }, - { CommandBarComponentId.TargetUnitTypePrice06, "h_price_06" }, - { CommandBarComponentId.TargetUnitTypePrice07, "h_price_07" }, - { CommandBarComponentId.TargetUnitTypePrice08, "h_price_08" }, - { CommandBarComponentId.TargetUnitTypePrice09, "h_price_09" }, - { CommandBarComponentId.TargetUnitTypePrice10, "h_price_10" }, - { CommandBarComponentId.TargetUnitTypePrice11, "h_price_11" }, - { CommandBarComponentId.VcrButtonPlayPause, "b_play_pause" }, - { CommandBarComponentId.VcrButtonFastForward, "b_fast_forward" }, - { CommandBarComponentId.VcrButtonFastForwardTactical, "b_fast_forward_t" }, - { CommandBarComponentId.VcrButtonPlayPauseTactical, "b_play_pause_t" }, - { CommandBarComponentId.AdvisorHintPopupGalactic, "text_galactic_help" }, - { CommandBarComponentId.AdvisorHintPopupTactical, "text_tactical_help" }, - { CommandBarComponentId.AdvisorHintBack, "help_back" }, - { CommandBarComponentId.TextOrganizeFleet00, "text_fleet1" }, - { CommandBarComponentId.TextOrganizeFleet01, "text_fleet2" }, - { CommandBarComponentId.TextOrganizeFleet02, "text_fleet3" }, - { CommandBarComponentId.IconOrganizeFleet00, "icon_fleet01" }, - { CommandBarComponentId.IconOrganizeFleet01, "icon_fleet02" }, - { CommandBarComponentId.IconOrganizeFleet02, "icon_fleet03" }, - { CommandBarComponentId.TextOrganizeLandFleet, "Text_Land_Fleet" }, - { CommandBarComponentId.CsAbilityButton, "cs_ability_button" }, - { CommandBarComponentId.CsAbilityText, "cs_ability_text" }, - { CommandBarComponentId.MovieBoneGalactic, "Movie_galactic" }, - { CommandBarComponentId.MovieBoneTactical, "Movie_tactical" }, - { CommandBarComponentId.GenericCollision, "generic_collision" }, - { CommandBarComponentId.GoodHeroShell, "good_hero_frame" }, - { CommandBarComponentId.GoodHeroSlot00, "hero_slot_good00" }, - { CommandBarComponentId.GoodHeroSlot01, "hero_slot_good01" }, - { CommandBarComponentId.GoodHeroSlot02, "hero_slot_good02" }, - { CommandBarComponentId.GoodHeroSlot03, "hero_slot_good03" }, - { CommandBarComponentId.GoodHeroSlot04, "hero_slot_good04" }, - { CommandBarComponentId.GoodHeroSlot05, "hero_slot_good05" }, - { CommandBarComponentId.GoodHeroSlot06, "hero_slot_good06" }, - { CommandBarComponentId.GoodHeroSlot07, "hero_slot_good07" }, - { CommandBarComponentId.GoodHeroSlot08, "hero_slot_good08" }, - { CommandBarComponentId.GoodHeroSlot09, "hero_slot_good09" }, - { CommandBarComponentId.GoodHeroSlot10, "hero_slot_good10" }, - { CommandBarComponentId.GoodHeroHealth00, "hero_hb_good00" }, - { CommandBarComponentId.GoodHeroHealth01, "hero_hb_good01" }, - { CommandBarComponentId.GoodHeroHealth02, "hero_hb_good02" }, - { CommandBarComponentId.GoodHeroHealth03, "hero_hb_good03" }, - { CommandBarComponentId.GoodHeroHealth04, "hero_hb_good04" }, - { CommandBarComponentId.GoodHeroHealth05, "hero_hb_good05" }, - { CommandBarComponentId.GoodHeroHealth06, "hero_hb_good06" }, - { CommandBarComponentId.GoodHeroHealth07, "hero_hb_good07" }, - { CommandBarComponentId.GoodHeroHealth08, "hero_hb_good08" }, - { CommandBarComponentId.GoodHeroHealth09, "hero_hb_good09" }, - { CommandBarComponentId.GoodHeroHealth10, "hero_hb_good10" }, - { CommandBarComponentId.EvilHeroShell, "evil_hero_frame" }, - { CommandBarComponentId.EvilHeroSlot00, "hero_slot_evil00" }, - { CommandBarComponentId.EvilHeroSlot01, "hero_slot_evil01" }, - { CommandBarComponentId.EvilHeroSlot02, "hero_slot_evil02" }, - { CommandBarComponentId.EvilHeroSlot03, "hero_slot_evil03" }, - { CommandBarComponentId.EvilHeroSlot04, "hero_slot_evil04" }, - { CommandBarComponentId.EvilHeroSlot05, "hero_slot_evil05" }, - { CommandBarComponentId.EvilHeroSlot06, "hero_slot_evil06" }, - { CommandBarComponentId.EvilHeroSlot07, "hero_slot_evil07" }, - { CommandBarComponentId.EvilHeroSlot08, "hero_slot_evil08" }, - { CommandBarComponentId.EvilHeroSlot09, "hero_slot_evil09" }, - { CommandBarComponentId.EvilHeroSlot10, "hero_slot_evil10" }, - { CommandBarComponentId.EvilHeroHealth00, "hero_hb_evil00" }, - { CommandBarComponentId.EvilHeroHealth01, "hero_hb_evil01" }, - { CommandBarComponentId.EvilHeroHealth02, "hero_hb_evil02" }, - { CommandBarComponentId.EvilHeroHealth03, "hero_hb_evil03" }, - { CommandBarComponentId.EvilHeroHealth04, "hero_hb_evil04" }, - { CommandBarComponentId.EvilHeroHealth05, "hero_hb_evil05" }, - { CommandBarComponentId.EvilHeroHealth06, "hero_hb_evil06" }, - { CommandBarComponentId.EvilHeroHealth07, "hero_hb_evil07" }, - { CommandBarComponentId.EvilHeroHealth08, "hero_hb_evil08" }, - { CommandBarComponentId.EvilHeroHealth09, "hero_hb_evil09" }, - { CommandBarComponentId.EvilHeroHealth10, "hero_hb_evil10" }, - { CommandBarComponentId.PauseShell, "pause_shell" }, - { CommandBarComponentId.PauseText, "text_attack" }, - { CommandBarComponentId.PauseButton, "attack_button" }, - { CommandBarComponentId.StoryCampaignPendingBattleShell, "story_pending_battle_shell" }, - { CommandBarComponentId.StoryCampaignPendingBattleText, "text_attack_single" }, - { CommandBarComponentId.StoryCampaignPendingBattleButton, "attack_button_single" }, - { CommandBarComponentId.MapActivate, "advanced_planetary" }, - { CommandBarComponentId.MapShell, "map_shell" }, - { CommandBarComponentId.MapOverlayShell, "map_overlay_shell" }, - { CommandBarComponentId.MapGroundForcesText, "ground_forces_text" }, - { CommandBarComponentId.MapDragDropText, "drage_drop_text" }, - { CommandBarComponentId.MapUnitCapText, "unit_cap_text" }, - { CommandBarComponentId.MapUnitCapNumber, "unit_cap_number" }, - { CommandBarComponentId.MapBuildingCapText, "building_cap_text" }, - { CommandBarComponentId.MapBuildingCapNumber, "building_cap_number" }, - { CommandBarComponentId.MapFactionIcon, "Allegiance_A" }, - { CommandBarComponentId.MapPlanetNameText, "Planet_name_text" }, - { CommandBarComponentId.MapPlanetIncomeNumber, "Planetary_Income_number" }, - { CommandBarComponentId.MapDayCounterText, "Day_Counter_text" }, - { CommandBarComponentId.MapDayCounter, "day_clock" }, - { CommandBarComponentId.MapBackButton, "back_button" }, - { CommandBarComponentId.MapWeatherIcon, "weather" }, - { CommandBarComponentId.MapPlanetBonusIcon, "planet_bonus" }, - { CommandBarComponentId.MapRadarMap, "radar_galactic_adv_map" }, - { CommandBarComponentId.MapZoomOut, "b_zoom_adv_map" }, - { CommandBarComponentId.MapEncyclopediaPopup, "Text_adv_help" }, - { CommandBarComponentId.AdvancedMapFilter0, "c_filters0" }, - { CommandBarComponentId.AdvancedMapFilter1, "c_filters1" }, - { CommandBarComponentId.AdvancedMapFilter2, "c_filters2" }, - { CommandBarComponentId.AdvancedMapFilter3, "c_filters3" }, - { CommandBarComponentId.AdvancedMapOptions, "c_option_g" }, - { CommandBarComponentId.MapMainMap, "map_window" }, - { CommandBarComponentId.MapSpaceUnit00, "land_fleet_00" }, - { CommandBarComponentId.MapSpaceUnit01, "land_fleet_01" }, - { CommandBarComponentId.MapSpaceUnit02, "land_fleet_02" }, - { CommandBarComponentId.MapSpaceUnit03, "land_fleet_03" }, - { CommandBarComponentId.MapSpaceUnit04, "land_fleet_04" }, - { CommandBarComponentId.MapSpaceUnit05, "land_fleet_05" }, - { CommandBarComponentId.MapSpaceUnit06, "land_fleet_06" }, - { CommandBarComponentId.MapSpaceUnit07, "land_fleet_07" }, - { CommandBarComponentId.MapSpaceUnit08, "land_fleet_08" }, - { CommandBarComponentId.MapSpaceUnit09, "land_fleet_09" }, - { CommandBarComponentId.MapSpaceUnit10, "land_fleet_10" }, - { CommandBarComponentId.MapSpaceUnit11, "land_fleet_11" }, - { CommandBarComponentId.MapSpaceUnit12, "land_fleet_12" }, - { CommandBarComponentId.MapSpaceUnit13, "land_fleet_13" }, - { CommandBarComponentId.MapSpaceUnit14, "land_fleet_14" }, - { CommandBarComponentId.MapSpaceUnit15, "land_fleet_15" }, - { CommandBarComponentId.MapSpaceUnit16, "land_fleet_16" }, - { CommandBarComponentId.MapSpaceUnit17, "land_fleet_17" }, - { CommandBarComponentId.MapSpaceUnit18, "land_fleet_18" }, - { CommandBarComponentId.MapSpaceUnit19, "land_fleet_19" }, - { CommandBarComponentId.MapBuildPad00, "build_pad00" }, - { CommandBarComponentId.MapBuildPad01, "build_pad01" }, - { CommandBarComponentId.MapBuildPad02, "build_pad02" }, - { CommandBarComponentId.MapBuildPad03, "build_pad03" }, - { CommandBarComponentId.MapBuildPad04, "build_pad04" }, - { CommandBarComponentId.MapBuildPad05, "build_pad05" }, - { CommandBarComponentId.MapBuildPad06, "build_pad06" }, - { CommandBarComponentId.MapBuildPad07, "build_pad07" }, - { CommandBarComponentId.MapBuildPad08, "build_pad08" }, - { CommandBarComponentId.MapBuildPad09, "build_pad09" }, - { CommandBarComponentId.MapBuildPad10, "build_pad10" }, - { CommandBarComponentId.MapBuildPad11, "build_pad11" }, - { CommandBarComponentId.MapBuildPad12, "build_pad12" }, - { CommandBarComponentId.MapBuildPad13, "build_pad13" }, - { CommandBarComponentId.MapBuildPad14, "build_pad14" }, - { CommandBarComponentId.MapBuildPad15, "build_pad15" }, - { CommandBarComponentId.MapBuildPad16, "build_pad16" }, - { CommandBarComponentId.MapBuildPad17, "build_pad17" }, - { CommandBarComponentId.MapBuildPad18, "build_pad18" }, - { CommandBarComponentId.MapBuildPad19, "build_pad19" }, - { CommandBarComponentId.MapBuildPad20, "build_pad20" }, - { CommandBarComponentId.MapBuildPad21, "build_pad21" }, - { CommandBarComponentId.MapBuildPad22, "build_pad22" }, - { CommandBarComponentId.MapBuildPad23, "build_pad23" }, - { CommandBarComponentId.MapBuildPad24, "build_pad24" }, - { CommandBarComponentId.MapBuildPad25, "build_pad25" }, - { CommandBarComponentId.MapBuildPad26, "build_pad26" }, - { CommandBarComponentId.MapBuildPad27, "build_pad27" }, - { CommandBarComponentId.MapBuildPad28, "build_pad28" }, - { CommandBarComponentId.MapBuildPad29, "build_pad29" }, - { CommandBarComponentId.MapBuildPad30, "build_pad30" }, - { CommandBarComponentId.MapBuildPad31, "build_pad31" }, - { CommandBarComponentId.MapBuildPad32, "build_pad32" }, - { CommandBarComponentId.MapReinforcePad00, "build_pad33" }, - { CommandBarComponentId.MapReinforcePad01, "build_pad34" }, - { CommandBarComponentId.MapReinforcePad02, "build_pad35" }, - { CommandBarComponentId.MapReinforcePad03, "build_pad36" }, - { CommandBarComponentId.MapReinforcePad04, "build_pad37" }, - { CommandBarComponentId.MapReinforcePad05, "build_pad38" }, - { CommandBarComponentId.MapReinforcePad06, "build_pad39" }, - { CommandBarComponentId.MapBunkerAndBuildPad00, "build_pad40" }, - { CommandBarComponentId.MapBunkerAndBuildPad01, "build_pad41" }, - { CommandBarComponentId.MapBunkerAndBuildPad02, "build_pad42" }, - { CommandBarComponentId.MapBunkerAndBuildPad03, "build_pad43" }, - { CommandBarComponentId.MapBunkerAndBuildPad04, "build_pad44" }, - { CommandBarComponentId.MapBunkerAndBuildPad05, "build_pad45" }, - { CommandBarComponentId.MapBunkerAndBuildPad06, "build_pad46" }, - { CommandBarComponentId.MapBunkerAndBuildPad07, "build_pad47" }, - { CommandBarComponentId.MapBunkerAndBuildPad08, "build_pad48" }, - { CommandBarComponentId.MapBunkerAndBuildPad09, "build_pad49" }, - { CommandBarComponentId.MapBunkerAndBuildPad10, "build_pad50" }, - { CommandBarComponentId.MapBunkerAndBuildPad11, "build_pad51" }, - { CommandBarComponentId.MapBunkerAndBuildPad12, "build_pad52" }, - { CommandBarComponentId.MapBunkerAndBuildPad13, "build_pad53" }, - { CommandBarComponentId.MapBunkerAndBuildPad14, "build_pad54" }, - { CommandBarComponentId.MapBunkerAndBuildPad15, "build_pad55" }, - { CommandBarComponentId.MapBunkerAndBuildPad16, "build_pad56" }, - { CommandBarComponentId.MapBunkerAndBuildPad17, "build_pad57" }, - { CommandBarComponentId.MapBunkerAndBuildPad18, "build_pad58" }, - { CommandBarComponentId.MapBunkerAndBuildPad19, "build_pad59" }, - { CommandBarComponentId.MapBunkerAndBuildPad20, "build_pad60" }, - { CommandBarComponentId.MapBunkerAndBuildPad21, "build_pad61" }, - { CommandBarComponentId.MapBunkerAndBuildPad22, "build_pad62" }, - { CommandBarComponentId.MapBunkerAndBuildPad23, "build_pad63" }, - { CommandBarComponentId.MapBunkerAndBuildPad24, "build_pad64" }, - { CommandBarComponentId.MapBunkerAndBuildPad25, "build_pad65" }, - { CommandBarComponentId.MapBunkerAndBuildPad26, "build_pad66" }, - { CommandBarComponentId.MapBunkerAndBuildPad27, "build_pad67" }, - { CommandBarComponentId.MapBunkerAndBuildPad28, "build_pad68" }, - { CommandBarComponentId.MapBunkerAndBuildPad29, "build_pad69" }, - { CommandBarComponentId.MapBunkerAndBuildPad30, "build_pad70" }, - { CommandBarComponentId.MapBunkerAndBuildPad31, "build_pad71" }, - { CommandBarComponentId.MapBunkerAndBuildPad32, "build_pad72" }, - { CommandBarComponentId.MapBunkerAndBuildPad33, "build_pad73" }, - { CommandBarComponentId.MapBunkerAndBuildPad34, "build_pad74" }, - { CommandBarComponentId.MapBunkerAndBuildPad35, "build_pad75" }, - { CommandBarComponentId.MapBunkerAndBuildPad36, "build_pad76" }, - { CommandBarComponentId.MapBunkerAndBuildPad37, "build_pad77" }, - { CommandBarComponentId.MapBunkerAndBuildPad38, "build_pad78" }, - { CommandBarComponentId.MapBunkerAndBuildPad39, "build_pad79" }, - { CommandBarComponentId.MapUnitPad00, "unit_pad00" }, - { CommandBarComponentId.MapUnitPad01, "unit_pad01" }, - { CommandBarComponentId.MapUnitPad02, "unit_pad02" }, - { CommandBarComponentId.MapUnitPad03, "unit_pad03" }, - { CommandBarComponentId.MapUnitPad04, "unit_pad04" }, - { CommandBarComponentId.MapUnitPad05, "unit_pad05" }, - { CommandBarComponentId.MapUnitPad06, "unit_pad06" }, - { CommandBarComponentId.MapUnitPad07, "unit_pad07" }, - { CommandBarComponentId.MapUnitPad08, "unit_pad08" }, - { CommandBarComponentId.MapUnitPad09, "unit_pad09" }, - { CommandBarComponentId.BribeDisplay, "bribe_display" }, - { CommandBarComponentId.BlackMarketShell, "market_frame" }, - { CommandBarComponentId.BlackMarketTitle, "market_title00" }, - { CommandBarComponentId.BlackMarketPlanet, "market_title01" }, - { CommandBarComponentId.BlackMarketTechLevelHeader, "market_title02" }, - { CommandBarComponentId.BlackMarketSlotHeader, "market_title03" }, - { CommandBarComponentId.BlackMarketCancel, "market_close" }, - { CommandBarComponentId.BlackMarketEncyclopedia, "i_blackmarket_encycl" }, - { CommandBarComponentId.BlackMarketTechLevel00, "market_tech_level00" }, - { CommandBarComponentId.BlackMarketTechLevel01, "market_tech_level01" }, - { CommandBarComponentId.BlackMarketTechLevel02, "market_tech_level02" }, - { CommandBarComponentId.BlackMarketTechLevel03, "market_tech_level03" }, - { CommandBarComponentId.BlackMarketTechLevel04, "market_tech_level04" }, - { CommandBarComponentId.BlackMarketSlot00, "market_00" }, - { CommandBarComponentId.BlackMarketSlot01, "market_01" }, - { CommandBarComponentId.BlackMarketSlot02, "market_02" }, - { CommandBarComponentId.BlackMarketSlot03, "market_03" }, - { CommandBarComponentId.BlackMarketSlot04, "market_04" }, - { CommandBarComponentId.BlackMarketSlotDescription00, "market_title04" }, - { CommandBarComponentId.BlackMarketSlotDescription01, "market_title06" }, - { CommandBarComponentId.BlackMarketSlotDescription02, "market_title08" }, - { CommandBarComponentId.BlackMarketSlotDescription03, "market_title10" }, - { CommandBarComponentId.BlackMarketSlotDescription04, "market_title12" }, - { CommandBarComponentId.BlackMarketSlotPrice00, "market_title05" }, - { CommandBarComponentId.BlackMarketSlotPrice01, "market_title07" }, - { CommandBarComponentId.BlackMarketSlotPrice02, "market_title09" }, - { CommandBarComponentId.BlackMarketSlotPrice03, "market_title11" }, - { CommandBarComponentId.BlackMarketSlotPrice04, "market_title13" }, - { CommandBarComponentId.SabotageShell, "bm_frame" }, - { CommandBarComponentId.SabotageTitle, "bm_title" }, - { CommandBarComponentId.SabotageTitle2, "bm_title01" }, - { CommandBarComponentId.SabotageTechLevel, "bm_tech_level" }, - { CommandBarComponentId.SabotageCancel, "bm_close" }, - { CommandBarComponentId.SabotageDescription, "bm_text_steal" }, - { CommandBarComponentId.SabotageSlot00, "bm_0000" }, - { CommandBarComponentId.SabotageSlot01, "bm_0001" }, - { CommandBarComponentId.SabotageSlot02, "bm_0100" }, - { CommandBarComponentId.SabotageSlot03, "bm_0101" }, - { CommandBarComponentId.SabotageSlot04, "bm_0200" }, - { CommandBarComponentId.SabotageSlot05, "bm_0201" }, - { CommandBarComponentId.SabotageSlot06, "bm_0300" }, - { CommandBarComponentId.SabotageSlot07, "bm_0301" }, - { CommandBarComponentId.SabotageSlot08, "bm_0400" }, - { CommandBarComponentId.SabotageSlot09, "bm_0401" }, - { CommandBarComponentId.SabotagePrice00, "bm_title_000" }, - { CommandBarComponentId.SabotagePrice01, "bm_title_001" }, - { CommandBarComponentId.SabotagePrice02, "bm_title_100" }, - { CommandBarComponentId.SabotagePrice03, "bm_title_101" }, - { CommandBarComponentId.SabotagePrice04, "bm_title_200" }, - { CommandBarComponentId.SabotagePrice05, "bm_title_201" }, - { CommandBarComponentId.SabotagePrice06, "bm_title_300" }, - { CommandBarComponentId.SabotagePrice07, "bm_title_301" }, - { CommandBarComponentId.SabotagePrice08, "bm_title_400" }, - { CommandBarComponentId.SabotagePrice09, "bm_title_401" }, - { CommandBarComponentId.SabotageName00, "bm_title_402" }, - { CommandBarComponentId.SabotageName01, "bm_title_403" }, - { CommandBarComponentId.SabotageName02, "bm_title_404" }, - { CommandBarComponentId.SabotageName03, "bm_title_405" }, - { CommandBarComponentId.SabotageName04, "bm_title_406" }, - { CommandBarComponentId.SabotageName05, "bm_title_407" }, - { CommandBarComponentId.SabotageName06, "bm_title_408" }, - { CommandBarComponentId.SabotageName07, "bm_title_409" }, - { CommandBarComponentId.SabotageName08, "bm_title_4010" }, - { CommandBarComponentId.SabotageName09, "bm_title_4011" }, - { CommandBarComponentId.PlanetaryBombardment, "pb_switch" }, - { CommandBarComponentId.PlanetaryBombardmentRecharge, "pb_switch_recharge" }, - { CommandBarComponentId.SuperLaser, "sl_switch" }, - { CommandBarComponentId.SuperLaserRecharge, "sl_switch_recharge" }, - { CommandBarComponentId.GenericFlytext, "generic_flytext" }, - { CommandBarComponentId.BribedIcon, "bribed_icon" }, - { CommandBarComponentId.SurfaceModIcon, "surface_mod_icon" }, - { CommandBarComponentId.RemoteBombIcon, "remote_bomb_icon" }, - { CommandBarComponentId.CorruptionShell, "corruption_shell" }, - { CommandBarComponentId.CorruptionTitle, "corrupt_planet_title00" }, - { CommandBarComponentId.CorruptionPlanetName, "corrupt_planet_title01" }, - { CommandBarComponentId.CorruptionEncyclopedia, "i_corrupt_encycl" }, - { CommandBarComponentId.CorruptionClose, "corrupt_close" }, - { CommandBarComponentId.CorruptionPlanetModel, "i_planet_image" }, - { CommandBarComponentId.CorruptionInfo00, "corrupt_planet_defiler_00" }, - { CommandBarComponentId.CorruptionInfo01, "corrupt_planet_defiler_01" }, - { CommandBarComponentId.CorruptionChoiceIcon0, "corrupt_button_icon_a" }, - { CommandBarComponentId.CorruptionChoiceIcon1, "corrupt_button_icon_b" }, - { CommandBarComponentId.CorruptionChoiceIcon2, "corrupt_button_icon_c" }, - { CommandBarComponentId.CorruptionChoiceCost0, "corrupt_cost_a" }, - { CommandBarComponentId.CorruptionChoiceCost1, "corrupt_cost_b" }, - { CommandBarComponentId.CorruptionChoiceCost2, "corrupt_cost_c" }, - { CommandBarComponentId.CorruptionChoiceTitle0, "corrupt_title_a" }, - { CommandBarComponentId.CorruptionChoiceTitle1, "corrupt_title_b" }, - { CommandBarComponentId.CorruptionChoiceTitle2, "corrupt_title_c" }, - { CommandBarComponentId.CorruptionChoiceBenefitText00, "corrupt_text00a" }, - { CommandBarComponentId.CorruptionChoiceBenefitText01, "corrupt_text01a" }, - { CommandBarComponentId.CorruptionChoiceBenefitText02, "corrupt_text02a" }, - { CommandBarComponentId.CorruptionChoiceBenefitText03, "corrupt_text03a" }, - { CommandBarComponentId.CorruptionChoiceBenefitText10, "corrupt_text00b" }, - { CommandBarComponentId.CorruptionChoiceBenefitText11, "corrupt_text01b" }, - { CommandBarComponentId.CorruptionChoiceBenefitText12, "corrupt_text02b" }, - { CommandBarComponentId.CorruptionChoiceBenefitText13, "corrupt_text03b" }, - { CommandBarComponentId.CorruptionChoiceBenefitText20, "corrupt_text00c" }, - { CommandBarComponentId.CorruptionChoiceBenefitText21, "corrupt_text01c" }, - { CommandBarComponentId.CorruptionChoiceBenefitText22, "corrupt_text02c" }, - { CommandBarComponentId.CorruptionChoiceBenefitText23, "corrupt_text03c" }, - { CommandBarComponentId.CorruptionChoiceRequirement0, "corrupt_text04a" }, - { CommandBarComponentId.CorruptionChoiceRequirement1, "corrupt_text04b" }, - { CommandBarComponentId.CorruptionChoiceRequirement2, "corrupt_text04c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0001, "corrupt_icon_00a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0002, "corrupt_icon_01a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0003, "corrupt_icon_02a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0000, "corrupt_icon_03a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0100, "corrupt_icon_04a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0101, "corrupt_icon_05a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0102, "corrupt_icon_06a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon0103, "corrupt_icon_07a" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1000, "corrupt_icon_00b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1001, "corrupt_icon_01b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1002, "corrupt_icon_02b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1003, "corrupt_icon_03b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1100, "corrupt_icon_04b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1101, "corrupt_icon_05b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1102, "corrupt_icon_06b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon1103, "corrupt_icon_07b" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2000, "corrupt_icon_00c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2001, "corrupt_icon_01c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2002, "corrupt_icon_02c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2003, "corrupt_icon_03c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2100, "corrupt_icon_04c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2101, "corrupt_icon_05c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2102, "corrupt_icon_06c" }, - { CommandBarComponentId.CorruptionChoiceRequirementIcon2103, "corrupt_icon_07c" }, - { CommandBarComponentId.HackSuperWeaponShell, "hack_shell" }, - { CommandBarComponentId.HackSuperWeaponTitle, "hack_title01" }, - { CommandBarComponentId.HackSuperWeaponText00, "hack_text_00" }, - { CommandBarComponentId.HackSuperWeaponText01, "hack_text_01" }, - { CommandBarComponentId.HackSuperWeaponText02, "hack_text_02" }, - { CommandBarComponentId.HackSuperWeaponText03, "hack_text_03" }, - { CommandBarComponentId.HackSuperWeaponText04, "hack_text_04" }, - { CommandBarComponentId.HackSuperWeaponText05, "hack_text_05" }, - { CommandBarComponentId.HackSuperWeaponText06, "hack_text_06" }, - { CommandBarComponentId.HackSuperWeaponCost, "hack_cost" }, - { CommandBarComponentId.HackSuperWeaponAccept, "hack_hack" }, - { CommandBarComponentId.HackSuperWeaponCancel, "hack_close" }, + { FocCommandBarComponentIds.MainShell, "i_main_commandbar" }, + { FocCommandBarComponentIds.ProductionOptions, "b_option_g" }, + { FocCommandBarComponentIds.GalacticCamera, "b_camera_g" }, + { FocCommandBarComponentIds.PlanetName, "Text_Planet" }, + { FocCommandBarComponentIds.PlanetValue, "Text_Planetary_Income" }, + { FocCommandBarComponentIds.PlanetAffiliation, "Text_Allegiance_A" }, + { FocCommandBarComponentIds.PlayerCredits, "Text_Credits" }, + { FocCommandBarComponentIds.ReinforcementCap, "Text_Planetary_Pop" }, + { FocCommandBarComponentIds.PlanetNameTactical, "Text_Planet_tactical" }, + { FocCommandBarComponentIds.PlanetInfo, "planet_info" }, + { FocCommandBarComponentIds.Production0, "b_Create_00" }, + { FocCommandBarComponentIds.Production1, "b_Create_01" }, + { FocCommandBarComponentIds.Production2, "b_Create_02" }, + { FocCommandBarComponentIds.Production3, "b_Create_03" }, + { FocCommandBarComponentIds.Production4, "b_Create_04" }, + { FocCommandBarComponentIds.Production5, "b_Create_05" }, + { FocCommandBarComponentIds.Production6, "b_Create_06" }, + { FocCommandBarComponentIds.Production7, "b_Create_07" }, + { FocCommandBarComponentIds.Production8, "b_Create_08" }, + { FocCommandBarComponentIds.Production9, "b_Create_09" }, + { FocCommandBarComponentIds.Production10, "b_Create_10" }, + { FocCommandBarComponentIds.Production11, "b_Create_11" }, + { FocCommandBarComponentIds.Production12, "b_Create_12" }, + { FocCommandBarComponentIds.Production13, "b_Create_13" }, + { FocCommandBarComponentIds.Production14, "b_Create_14" }, + { FocCommandBarComponentIds.Production15, "b_Create_15" }, + { FocCommandBarComponentIds.Production16, "b_Create_16" }, + { FocCommandBarComponentIds.Production17, "b_Create_17" }, + { FocCommandBarComponentIds.Production18, "b_Create_18" }, + { FocCommandBarComponentIds.Production19, "b_Create_19" }, + { FocCommandBarComponentIds.Production20, "b_Create_20" }, + { FocCommandBarComponentIds.Production21, "b_Create_21" }, + { FocCommandBarComponentIds.Production22, "b_Create_22" }, + { FocCommandBarComponentIds.Production23, "b_Create_23" }, + { FocCommandBarComponentIds.Production24, "b_Create_24" }, + { FocCommandBarComponentIds.Production25, "b_Create_25" }, + { FocCommandBarComponentIds.Production26, "b_Create_26" }, + { FocCommandBarComponentIds.Production27, "b_Create_27" }, + { FocCommandBarComponentIds.Production28, "b_Create_28" }, + { FocCommandBarComponentIds.Production29, "b_Create_29" }, + { FocCommandBarComponentIds.Production30, "b_Create_30" }, + { FocCommandBarComponentIds.Production31, "b_Create_31" }, + { FocCommandBarComponentIds.Production32, "b_Create_32" }, + { FocCommandBarComponentIds.Production33, "b_Create_33" }, + { FocCommandBarComponentIds.Production34, "b_Create_34" }, + { FocCommandBarComponentIds.Production35, "b_Create_35" }, + { FocCommandBarComponentIds.Production36, "b_Create_36" }, + { FocCommandBarComponentIds.Production37, "b_Create_37" }, + { FocCommandBarComponentIds.Production38, "b_Create_38" }, + { FocCommandBarComponentIds.Production39, "b_Create_39" }, + { FocCommandBarComponentIds.Production40, "b_Create_40" }, + { FocCommandBarComponentIds.Production41, "b_Create_41" }, + { FocCommandBarComponentIds.Production42, "b_Create_42" }, + { FocCommandBarComponentIds.Production43, "b_Create_43" }, + { FocCommandBarComponentIds.Production44, "b_Create_44" }, + { FocCommandBarComponentIds.Production45, "b_Create_45" }, + { FocCommandBarComponentIds.Production46, "b_Create_46" }, + { FocCommandBarComponentIds.Production47, "b_Create_47" }, + { FocCommandBarComponentIds.DroidHelp, "b_droid_help" }, + { FocCommandBarComponentIds.DroidHelpTactical, "b_droid_help_tactical" }, + { FocCommandBarComponentIds.CurrentDay, "Text_Day_Counter" }, + { FocCommandBarComponentIds.DayCredits, "Text_Day_Credits" }, + { FocCommandBarComponentIds.PopulationCap, "Text_Pop_Cap" }, + { FocCommandBarComponentIds.Filter0, "b_filters0" }, + { FocCommandBarComponentIds.Filter1, "b_filters1" }, + { FocCommandBarComponentIds.Filter2, "b_filters2" }, + { FocCommandBarComponentIds.Filter3, "b_filters3" }, + { FocCommandBarComponentIds.StoryArcButton, "b_story_arc_g" }, + { FocCommandBarComponentIds.PlanetSummaryButton, "b_quick_ref" }, + { FocCommandBarComponentIds.SpaceTab, "b_space_tab" }, + { FocCommandBarComponentIds.LandTab, "b_planet_tab" }, + { FocCommandBarComponentIds.Dial, "b_dial" }, + { FocCommandBarComponentIds.ScrollRight, "b_scroll_right" }, + { FocCommandBarComponentIds.ScrollLeft, "b_scroll_left" }, + { FocCommandBarComponentIds.ZoomView, "b_zoom" }, + { FocCommandBarComponentIds.PrevPlanet, "b_planet_left" }, + { FocCommandBarComponentIds.NextPlanet, "b_planet_right" }, + { FocCommandBarComponentIds.RadarGalactic, "radar_galactic" }, + { FocCommandBarComponentIds.TechLevel, "Text_Tech_Level" }, + { FocCommandBarComponentIds.BalancePip, "balance_pip" }, + { FocCommandBarComponentIds.BuildQueue00, "queue00" }, + { FocCommandBarComponentIds.BuildQueue01, "queue01" }, + { FocCommandBarComponentIds.BuildQueue02, "queue02" }, + { FocCommandBarComponentIds.BuildQueue03, "queue03" }, + { FocCommandBarComponentIds.BuildQueue04, "queue04" }, + { FocCommandBarComponentIds.BuildQueue05, "queue05" }, + { FocCommandBarComponentIds.BuildQueue06, "queue06" }, + { FocCommandBarComponentIds.BuildQueue07, "queue07" }, + { FocCommandBarComponentIds.BuildQueue08, "queue08" }, + { FocCommandBarComponentIds.BuildQueue09, "queue09" }, + { FocCommandBarComponentIds.OrganizationShell, "i_main_organize" }, + { FocCommandBarComponentIds.OrganizationCollision, "land_art_ALT0" }, + { FocCommandBarComponentIds.SmugglerBox, "b_underground" }, + { FocCommandBarComponentIds.SpaceStationUpgrade01, "space_Pad_1" }, + { FocCommandBarComponentIds.SpaceStationUpgrade02, "space_Pad_02" }, + { FocCommandBarComponentIds.HeroAbilitySlot, "Slot_00" }, + { FocCommandBarComponentIds.PlanetOrganize0, "Pad_00" }, + { FocCommandBarComponentIds.PlanetOrganize1, "Pad_01" }, + { FocCommandBarComponentIds.PlanetOrganize2, "Pad_02" }, + { FocCommandBarComponentIds.PlanetOrganize3, "Pad_03" }, + { FocCommandBarComponentIds.PlanetOrganize4, "Pad_04" }, + { FocCommandBarComponentIds.PlanetOrganize5, "Pad_05" }, + { FocCommandBarComponentIds.PlanetOrganize6, "Pad_06" }, + { FocCommandBarComponentIds.PlanetOrganize7, "Pad_07" }, + { FocCommandBarComponentIds.PlanetOrganize8, "Pad_08" }, + { FocCommandBarComponentIds.PlanetOrganize9, "Pad_09" }, + { FocCommandBarComponentIds.SpecialStructureLand0, "Planet_structure_00" }, + { FocCommandBarComponentIds.SpecialStructureLand1, "Planet_structure_01" }, + { FocCommandBarComponentIds.SpecialStructureLand2, "Planet_structure_02" }, + { FocCommandBarComponentIds.SpecialStructureLand3, "Planet_structure_03" }, + { FocCommandBarComponentIds.SpecialStructureLand4, "Planet_structure_04" }, + { FocCommandBarComponentIds.SpecialStructureLand5, "Planet_structure_05" }, + { FocCommandBarComponentIds.SpecialStructureLand6, "Planet_structure_06" }, + { FocCommandBarComponentIds.SpecialStructureLand7, "Planet_structure_07" }, + { FocCommandBarComponentIds.SpecialStructureLand8, "Planet_structure_08" }, + { FocCommandBarComponentIds.SpecialStructureLandSell, "g_ground_sell" }, + { FocCommandBarComponentIds.BigFleet0, "q1_fleet" }, + { FocCommandBarComponentIds.BigFleet1, "q2_fleet" }, + { FocCommandBarComponentIds.BigFleet2, "q3_fleet" }, + { FocCommandBarComponentIds.BigFleet3, "q4_fleet" }, + { FocCommandBarComponentIds.Fleet0Slot0, "q1_0000" }, + { FocCommandBarComponentIds.Fleet0Slot1, "q1_0001" }, + { FocCommandBarComponentIds.Fleet0Slot2, "q1_0002" }, + { FocCommandBarComponentIds.Fleet0Slot3, "q1_0003" }, + { FocCommandBarComponentIds.Fleet0Slot4, "q1_0004" }, + { FocCommandBarComponentIds.Fleet0Slot5, "q1_0005" }, + { FocCommandBarComponentIds.Fleet0Slot6, "q1_0006" }, + { FocCommandBarComponentIds.Fleet0Slot7, "q1_0100" }, + { FocCommandBarComponentIds.Fleet0Slot8, "q1_0101" }, + { FocCommandBarComponentIds.Fleet0Slot9, "q1_0102" }, + { FocCommandBarComponentIds.Fleet0Slot10, "q1_0103" }, + { FocCommandBarComponentIds.Fleet0Slot11, "q1_0104" }, + { FocCommandBarComponentIds.Fleet0Slot12, "q1_0105" }, + { FocCommandBarComponentIds.Fleet0Slot13, "q1_0106" }, + { FocCommandBarComponentIds.Fleet0Slot14, "q1_0200" }, + { FocCommandBarComponentIds.Fleet0Slot15, "q1_0201" }, + { FocCommandBarComponentIds.Fleet0Slot16, "q1_0202" }, + { FocCommandBarComponentIds.Fleet0Slot17, "q1_0203" }, + { FocCommandBarComponentIds.Fleet0Slot18, "q1_0204" }, + { FocCommandBarComponentIds.Fleet0Slot19, "q1_0205" }, + { FocCommandBarComponentIds.Fleet0Slot20, "q1_0206" }, + { FocCommandBarComponentIds.Fleet0Slot21, "q1_0300" }, + { FocCommandBarComponentIds.Fleet0Slot22, "q1_0301" }, + { FocCommandBarComponentIds.Fleet0Slot23, "q1_0302" }, + { FocCommandBarComponentIds.Fleet0Slot24, "q1_0303" }, + { FocCommandBarComponentIds.Fleet0Slot25, "q1_0304" }, + { FocCommandBarComponentIds.Fleet0Slot26, "q1_0305" }, + { FocCommandBarComponentIds.Fleet0Slot27, "q1_0306" }, + { FocCommandBarComponentIds.Fleet0Slot28, "q1_0400" }, + { FocCommandBarComponentIds.Fleet0Slot29, "q1_0401" }, + { FocCommandBarComponentIds.Fleet0Slot30, "q1_0402" }, + { FocCommandBarComponentIds.Fleet0Slot31, "q1_0403" }, + { FocCommandBarComponentIds.Fleet0Slot32, "q1_0404" }, + { FocCommandBarComponentIds.Fleet0Slot33, "q1_0405" }, + { FocCommandBarComponentIds.Fleet0Slot34, "q1_0406" }, + { FocCommandBarComponentIds.Fleet1Slot0, "q2_0000" }, + { FocCommandBarComponentIds.Fleet1Slot1, "q2_0001" }, + { FocCommandBarComponentIds.Fleet1Slot2, "q2_0002" }, + { FocCommandBarComponentIds.Fleet1Slot3, "q2_0003" }, + { FocCommandBarComponentIds.Fleet1Slot4, "q2_0004" }, + { FocCommandBarComponentIds.Fleet1Slot5, "q2_0005" }, + { FocCommandBarComponentIds.Fleet1Slot6, "q2_0006" }, + { FocCommandBarComponentIds.Fleet1Slot7, "q2_0100" }, + { FocCommandBarComponentIds.Fleet1Slot8, "q2_0101" }, + { FocCommandBarComponentIds.Fleet1Slot9, "q2_0102" }, + { FocCommandBarComponentIds.Fleet1Slot10, "q2_0103" }, + { FocCommandBarComponentIds.Fleet1Slot11, "q2_0104" }, + { FocCommandBarComponentIds.Fleet1Slot12, "q2_0105" }, + { FocCommandBarComponentIds.Fleet1Slot13, "q2_0106" }, + { FocCommandBarComponentIds.Fleet1Slot14, "q2_0200" }, + { FocCommandBarComponentIds.Fleet1Slot15, "q2_0201" }, + { FocCommandBarComponentIds.Fleet1Slot16, "q2_0202" }, + { FocCommandBarComponentIds.Fleet1Slot17, "q2_0203" }, + { FocCommandBarComponentIds.Fleet1Slot18, "q2_0204" }, + { FocCommandBarComponentIds.Fleet1Slot19, "q2_0205" }, + { FocCommandBarComponentIds.Fleet1Slot20, "q2_0206" }, + { FocCommandBarComponentIds.Fleet1Slot21, "q2_0300" }, + { FocCommandBarComponentIds.Fleet1Slot22, "q2_0301" }, + { FocCommandBarComponentIds.Fleet1Slot23, "q2_0302" }, + { FocCommandBarComponentIds.Fleet1Slot24, "q2_0303" }, + { FocCommandBarComponentIds.Fleet1Slot25, "q2_0304" }, + { FocCommandBarComponentIds.Fleet1Slot26, "q2_0305" }, + { FocCommandBarComponentIds.Fleet1Slot27, "q2_0306" }, + { FocCommandBarComponentIds.Fleet1Slot28, "q2_0400" }, + { FocCommandBarComponentIds.Fleet1Slot29, "q2_0401" }, + { FocCommandBarComponentIds.Fleet1Slot30, "q2_0402" }, + { FocCommandBarComponentIds.Fleet1Slot31, "q2_0403" }, + { FocCommandBarComponentIds.Fleet1Slot32, "q2_0404" }, + { FocCommandBarComponentIds.Fleet1Slot33, "q2_0405" }, + { FocCommandBarComponentIds.Fleet1Slot34, "q2_0406" }, + { FocCommandBarComponentIds.Fleet2Slot0, "q3_0000" }, + { FocCommandBarComponentIds.Fleet2Slot1, "q3_0001" }, + { FocCommandBarComponentIds.Fleet2Slot2, "q3_0002" }, + { FocCommandBarComponentIds.Fleet2Slot3, "q3_0003" }, + { FocCommandBarComponentIds.Fleet2Slot4, "q3_0004" }, + { FocCommandBarComponentIds.Fleet2Slot5, "q3_0005" }, + { FocCommandBarComponentIds.Fleet2Slot6, "q3_0006" }, + { FocCommandBarComponentIds.Fleet2Slot7, "q3_0100" }, + { FocCommandBarComponentIds.Fleet2Slot8, "q3_0101" }, + { FocCommandBarComponentIds.Fleet2Slot9, "q3_0102" }, + { FocCommandBarComponentIds.Fleet2Slot10, "q3_0103" }, + { FocCommandBarComponentIds.Fleet2Slot11, "q3_0104" }, + { FocCommandBarComponentIds.Fleet2Slot12, "q3_0105" }, + { FocCommandBarComponentIds.Fleet2Slot13, "q3_0106" }, + { FocCommandBarComponentIds.Fleet2Slot14, "q3_0200" }, + { FocCommandBarComponentIds.Fleet2Slot15, "q3_0201" }, + { FocCommandBarComponentIds.Fleet2Slot16, "q3_0202" }, + { FocCommandBarComponentIds.Fleet2Slot17, "q3_0203" }, + { FocCommandBarComponentIds.Fleet2Slot18, "q3_0204" }, + { FocCommandBarComponentIds.Fleet2Slot19, "q3_0205" }, + { FocCommandBarComponentIds.Fleet2Slot20, "q3_0206" }, + { FocCommandBarComponentIds.Fleet2Slot21, "q3_0300" }, + { FocCommandBarComponentIds.Fleet2Slot22, "q3_0301" }, + { FocCommandBarComponentIds.Fleet2Slot23, "q3_0302" }, + { FocCommandBarComponentIds.Fleet2Slot24, "q3_0303" }, + { FocCommandBarComponentIds.Fleet2Slot25, "q3_0304" }, + { FocCommandBarComponentIds.Fleet2Slot26, "q3_0305" }, + { FocCommandBarComponentIds.Fleet2Slot27, "q3_0306" }, + { FocCommandBarComponentIds.Fleet2Slot28, "q3_0400" }, + { FocCommandBarComponentIds.Fleet2Slot29, "q3_0401" }, + { FocCommandBarComponentIds.Fleet2Slot30, "q3_0402" }, + { FocCommandBarComponentIds.Fleet2Slot31, "q3_0403" }, + { FocCommandBarComponentIds.Fleet2Slot32, "q3_0404" }, + { FocCommandBarComponentIds.Fleet2Slot33, "q3_0405" }, + { FocCommandBarComponentIds.Fleet2Slot34, "q3_0406" }, + { FocCommandBarComponentIds.TacticalMain, "i_main_skirmish" }, + { FocCommandBarComponentIds.TacticalOptions, "b_option_t" }, + { FocCommandBarComponentIds.TacticalCamera, "b_camera_t" }, + { FocCommandBarComponentIds.TacticalBeacon, "b_beacon_t" }, + { FocCommandBarComponentIds.TacticalWeapon0, "b_special_weapon" }, + { FocCommandBarComponentIds.TacticalWeapon1, "b_special_weapon2" }, + { FocCommandBarComponentIds.TacticalReinforce, "b_reinforcement" }, + { FocCommandBarComponentIds.TacticalRetreat, "b_retreat" }, + { FocCommandBarComponentIds.TacticalStoryArc, "b_story_arc_t" }, + { FocCommandBarComponentIds.TacticalTechLevel, "Text_Tactical_Tech" }, + { FocCommandBarComponentIds.TacticalAttack, "c_button00" }, + { FocCommandBarComponentIds.TacticalAttackMove, "c_button01" }, + { FocCommandBarComponentIds.TacticalMove, "c_button02" }, + { FocCommandBarComponentIds.TacticalWaypoint, "c_button03" }, + { FocCommandBarComponentIds.TacticalStop, "c_button04" }, + { FocCommandBarComponentIds.TacticalGuard, "c_button05" }, + { FocCommandBarComponentIds.SpaceTacticalRadar, "radar" }, + { FocCommandBarComponentIds.SpaceTacticalCredits, "Text_Credits_tactical" }, + { FocCommandBarComponentIds.SpaceTacticalGrabBar, "st_grab_bar" }, + { FocCommandBarComponentIds.SpaceTacticalHealthBar, "st_health_bar" }, + { FocCommandBarComponentIds.SpaceTacticalBracketSmall, "st_bracket_small" }, + { FocCommandBarComponentIds.SpaceTacticalBracketMedium, "st_bracket_medium" }, + { FocCommandBarComponentIds.SpaceTacticalBracketLarge, "st_bracket_large" }, + { FocCommandBarComponentIds.SpaceTacticalHeroIcon, "st_hero_icon" }, + { FocCommandBarComponentIds.SpaceTacticalHeroHealth, "st_hero_health" }, + { FocCommandBarComponentIds.SpaceTacticalHealth, "st_health" }, + { FocCommandBarComponentIds.SpaceTacticalHealthMedium, "st_health_medium" }, + { FocCommandBarComponentIds.SpaceTacticalHealthLarge, "st_health_large" }, + { FocCommandBarComponentIds.SpaceTacticalShields, "st_shields" }, + { FocCommandBarComponentIds.SpaceTacticalShieldsMedium, "st_shields_medium" }, + { FocCommandBarComponentIds.SpaceTacticalShieldsLarge, "st_shields_large" }, + { FocCommandBarComponentIds.SpaceTacticalPower, "st_power" }, + { FocCommandBarComponentIds.SpaceTacticalControlGroup, "st_control_group" }, + { FocCommandBarComponentIds.SpaceTacticalAbilityIcon, "st_ability_icon" }, + { FocCommandBarComponentIds.SpaceTacticalGarrisonIcon, "st_garrison_icon" }, + { FocCommandBarComponentIds.LandTacticalWeatherIcon, "lt_weather_icon" }, + { FocCommandBarComponentIds.GarrisonSlotIcon, "garrison_slot_icon" }, + { FocCommandBarComponentIds.DsFireShell, "ds_shell" }, + { FocCommandBarComponentIds.DsFire, "deathstar_switch" }, + { FocCommandBarComponentIds.DsCountdownTimer, "DS_Countdown_Timer" }, + { FocCommandBarComponentIds.TacticalSelect00, "s_select_00" }, + { FocCommandBarComponentIds.TacticalSelect01, "s_select_01" }, + { FocCommandBarComponentIds.TacticalSelect02, "s_select_02" }, + { FocCommandBarComponentIds.TacticalSelect03, "s_select_03" }, + { FocCommandBarComponentIds.TacticalSelect04, "s_select_04" }, + { FocCommandBarComponentIds.TacticalSelect05, "s_select_05" }, + { FocCommandBarComponentIds.TacticalSelect06, "s_select_06" }, + { FocCommandBarComponentIds.TacticalSelect07, "s_select_07" }, + { FocCommandBarComponentIds.TacticalSelect08, "s_select_08" }, + { FocCommandBarComponentIds.TacticalSelect09, "s_select_09" }, + { FocCommandBarComponentIds.TacticalSelect10, "s_select_10" }, + { FocCommandBarComponentIds.TacticalSelect11, "s_select_11" }, + { FocCommandBarComponentIds.TacticalSelect12, "s_select_12" }, + { FocCommandBarComponentIds.TacticalSelect13, "s_select_13" }, + { FocCommandBarComponentIds.TacticalSelect14, "s_select_14" }, + { FocCommandBarComponentIds.TacticalSelect15, "s_select_15" }, + { FocCommandBarComponentIds.TacticalSelect16, "s_select_16" }, + { FocCommandBarComponentIds.TacticalSelect17, "s_select_17" }, + { FocCommandBarComponentIds.TacticalSelect18, "s_select_18" }, + { FocCommandBarComponentIds.TacticalSelect19, "s_select_19" }, + { FocCommandBarComponentIds.TacticalSelect20, "s_select_20" }, + { FocCommandBarComponentIds.TacticalSelect21, "s_select_21" }, + { FocCommandBarComponentIds.TacticalSelect22, "s_select_22" }, + { FocCommandBarComponentIds.TacticalSelect23, "s_select_23" }, + { FocCommandBarComponentIds.TacticalSelect24, "s_select_24" }, + { FocCommandBarComponentIds.TacticalSelect25, "s_select_25" }, + { FocCommandBarComponentIds.TacticalSelect26, "s_select_26" }, + { FocCommandBarComponentIds.TacticalSelect27, "s_select_27" }, + { FocCommandBarComponentIds.TacticalSelect28, "s_select_28" }, + { FocCommandBarComponentIds.TacticalSelect29, "s_select_29" }, + { FocCommandBarComponentIds.TacticalSelect30, "s_select_30" }, + { FocCommandBarComponentIds.TacticalSelect31, "s_select_31" }, + { FocCommandBarComponentIds.TacticalSelect32, "s_select_32" }, + { FocCommandBarComponentIds.TacticalSelect33, "s_select_33" }, + { FocCommandBarComponentIds.TacticalSelect34, "s_select_34" }, + { FocCommandBarComponentIds.TacticalSelect35, "s_select_35" }, + { FocCommandBarComponentIds.TacticalSelect36, "s_select_36" }, + { FocCommandBarComponentIds.TacticalSelect37, "s_select_37" }, + { FocCommandBarComponentIds.TacticalSelect38, "s_select_38" }, + { FocCommandBarComponentIds.TacticalSelect39, "s_select_39" }, + { FocCommandBarComponentIds.TacticalSelect40, "s_select_40" }, + { FocCommandBarComponentIds.TacticalSelect41, "s_select_41" }, + { FocCommandBarComponentIds.TacticalSelect42, "s_select_42" }, + { FocCommandBarComponentIds.TacticalSelect43, "s_select_43" }, + { FocCommandBarComponentIds.TacticalSelect44, "s_select_44" }, + { FocCommandBarComponentIds.TacticalSelect45, "s_select_45" }, + { FocCommandBarComponentIds.TacticalSelect46, "s_select_46" }, + { FocCommandBarComponentIds.TacticalSelect47, "s_select_47" }, + { FocCommandBarComponentIds.TacticalSelectHealth00, "s_health_00" }, + { FocCommandBarComponentIds.TacticalSelectHealth01, "s_health_01" }, + { FocCommandBarComponentIds.TacticalSelectHealth02, "s_health_02" }, + { FocCommandBarComponentIds.TacticalSelectHealth03, "s_health_03" }, + { FocCommandBarComponentIds.TacticalSelectHealth04, "s_health_04" }, + { FocCommandBarComponentIds.TacticalSelectHealth05, "s_health_05" }, + { FocCommandBarComponentIds.TacticalSelectHealth06, "s_health_06" }, + { FocCommandBarComponentIds.TacticalSelectHealth07, "s_health_07" }, + { FocCommandBarComponentIds.TacticalSelectHealth08, "s_health_08" }, + { FocCommandBarComponentIds.TacticalSelectHealth09, "s_health_09" }, + { FocCommandBarComponentIds.TacticalSelectHealth10, "s_health_10" }, + { FocCommandBarComponentIds.TacticalSelectHealth11, "s_health_11" }, + { FocCommandBarComponentIds.TacticalSelectHealth12, "s_health_12" }, + { FocCommandBarComponentIds.TacticalSelectHealth13, "s_health_13" }, + { FocCommandBarComponentIds.TacticalSelectHealth14, "s_health_14" }, + { FocCommandBarComponentIds.TacticalSelectHealth15, "s_health_15" }, + { FocCommandBarComponentIds.TacticalSelectHealth16, "s_health_16" }, + { FocCommandBarComponentIds.TacticalSelectHealth17, "s_health_17" }, + { FocCommandBarComponentIds.TacticalSelectHealth18, "s_health_18" }, + { FocCommandBarComponentIds.TacticalSelectHealth19, "s_health_19" }, + { FocCommandBarComponentIds.TacticalSelectHealth20, "s_health_20" }, + { FocCommandBarComponentIds.TacticalSelectHealth21, "s_health_21" }, + { FocCommandBarComponentIds.TacticalSelectHealth22, "s_health_22" }, + { FocCommandBarComponentIds.TacticalSelectHealth23, "s_health_23" }, + { FocCommandBarComponentIds.TacticalSelectHealth24, "s_health_24" }, + { FocCommandBarComponentIds.TacticalSelectHealth25, "s_health_25" }, + { FocCommandBarComponentIds.TacticalSelectHealth26, "s_health_26" }, + { FocCommandBarComponentIds.TacticalSelectHealth27, "s_health_27" }, + { FocCommandBarComponentIds.TacticalSelectHealth28, "s_health_28" }, + { FocCommandBarComponentIds.TacticalSelectHealth29, "s_health_29" }, + { FocCommandBarComponentIds.TacticalSelectHealth30, "s_health_30" }, + { FocCommandBarComponentIds.TacticalSelectHealth31, "s_health_31" }, + { FocCommandBarComponentIds.TacticalSelectHealth32, "s_health_32" }, + { FocCommandBarComponentIds.TacticalSelectHealth33, "s_health_33" }, + { FocCommandBarComponentIds.TacticalSelectHealth34, "s_health_34" }, + { FocCommandBarComponentIds.TacticalSelectHealth35, "s_health_35" }, + { FocCommandBarComponentIds.TacticalSelectHealth36, "s_health_36" }, + { FocCommandBarComponentIds.TacticalSelectHealth37, "s_health_37" }, + { FocCommandBarComponentIds.TacticalSelectHealth38, "s_health_38" }, + { FocCommandBarComponentIds.TacticalSelectHealth39, "s_health_39" }, + { FocCommandBarComponentIds.TacticalSelectHealth40, "s_health_40" }, + { FocCommandBarComponentIds.TacticalSelectHealth41, "s_health_41" }, + { FocCommandBarComponentIds.TacticalSelectHealth42, "s_health_42" }, + { FocCommandBarComponentIds.TacticalSelectHealth43, "s_health_43" }, + { FocCommandBarComponentIds.TacticalSelectHealth44, "s_health_44" }, + { FocCommandBarComponentIds.TacticalSelectHealth45, "s_health_45" }, + { FocCommandBarComponentIds.TacticalSelectHealth46, "s_health_46" }, + { FocCommandBarComponentIds.TacticalSelectHealth47, "s_health_47" }, + { FocCommandBarComponentIds.TacticalSelectShield00, "s_shield_00" }, + { FocCommandBarComponentIds.TacticalSelectShield01, "s_shield_01" }, + { FocCommandBarComponentIds.TacticalSelectShield02, "s_shield_02" }, + { FocCommandBarComponentIds.TacticalSelectShield03, "s_shield_03" }, + { FocCommandBarComponentIds.TacticalSelectShield04, "s_shield_04" }, + { FocCommandBarComponentIds.TacticalSelectShield05, "s_shield_05" }, + { FocCommandBarComponentIds.TacticalSelectShield06, "s_shield_06" }, + { FocCommandBarComponentIds.TacticalSelectShield07, "s_shield_07" }, + { FocCommandBarComponentIds.TacticalSelectShield08, "s_shield_08" }, + { FocCommandBarComponentIds.TacticalSelectShield09, "s_shield_09" }, + { FocCommandBarComponentIds.TacticalSelectShield10, "s_shield_10" }, + { FocCommandBarComponentIds.TacticalSelectShield11, "s_shield_11" }, + { FocCommandBarComponentIds.TacticalSelectShield12, "s_shield_12" }, + { FocCommandBarComponentIds.TacticalSelectShield13, "s_shield_13" }, + { FocCommandBarComponentIds.TacticalSelectShield14, "s_shield_14" }, + { FocCommandBarComponentIds.TacticalSelectShield15, "s_shield_15" }, + { FocCommandBarComponentIds.TacticalSelectShield16, "s_shield_16" }, + { FocCommandBarComponentIds.TacticalSelectShield17, "s_shield_17" }, + { FocCommandBarComponentIds.TacticalSelectShield18, "s_shield_18" }, + { FocCommandBarComponentIds.TacticalSelectShield19, "s_shield_19" }, + { FocCommandBarComponentIds.TacticalSelectShield20, "s_shield_20" }, + { FocCommandBarComponentIds.TacticalSelectShield21, "s_shield_21" }, + { FocCommandBarComponentIds.TacticalSelectShield22, "s_shield_22" }, + { FocCommandBarComponentIds.TacticalSelectShield23, "s_shield_23" }, + { FocCommandBarComponentIds.TacticalSelectShield24, "s_shield_24" }, + { FocCommandBarComponentIds.TacticalSelectShield25, "s_shield_25" }, + { FocCommandBarComponentIds.TacticalSelectShield26, "s_shield_26" }, + { FocCommandBarComponentIds.TacticalSelectShield27, "s_shield_27" }, + { FocCommandBarComponentIds.TacticalSelectShield28, "s_shield_28" }, + { FocCommandBarComponentIds.TacticalSelectShield29, "s_shield_29" }, + { FocCommandBarComponentIds.TacticalSelectShield30, "s_shield_30" }, + { FocCommandBarComponentIds.TacticalSelectShield31, "s_shield_31" }, + { FocCommandBarComponentIds.TacticalSelectShield32, "s_shield_32" }, + { FocCommandBarComponentIds.TacticalSelectShield33, "s_shield_33" }, + { FocCommandBarComponentIds.TacticalSelectShield34, "s_shield_34" }, + { FocCommandBarComponentIds.TacticalSelectShield35, "s_shield_35" }, + { FocCommandBarComponentIds.TacticalSelectShield36, "s_shield_36" }, + { FocCommandBarComponentIds.TacticalSelectShield37, "s_shield_37" }, + { FocCommandBarComponentIds.TacticalSelectShield38, "s_shield_38" }, + { FocCommandBarComponentIds.TacticalSelectShield39, "s_shield_39" }, + { FocCommandBarComponentIds.TacticalSelectShield40, "s_shield_40" }, + { FocCommandBarComponentIds.TacticalSelectShield41, "s_shield_41" }, + { FocCommandBarComponentIds.TacticalSelectShield42, "s_shield_42" }, + { FocCommandBarComponentIds.TacticalSelectShield43, "s_shield_43" }, + { FocCommandBarComponentIds.TacticalSelectShield44, "s_shield_44" }, + { FocCommandBarComponentIds.TacticalSelectShield45, "s_shield_45" }, + { FocCommandBarComponentIds.TacticalSelectShield46, "s_shield_46" }, + { FocCommandBarComponentIds.TacticalSelectShield47, "s_shield_47" }, + { FocCommandBarComponentIds.TacticalBorder00, "special_border_00" }, + { FocCommandBarComponentIds.TacticalBorder01, "special_border_01" }, + { FocCommandBarComponentIds.TacticalBorder02, "special_border_02" }, + { FocCommandBarComponentIds.TacticalBorder03, "special_border_03" }, + { FocCommandBarComponentIds.TacticalBorder04, "special_border_04" }, + { FocCommandBarComponentIds.TacticalBorder05, "special_border_05" }, + { FocCommandBarComponentIds.TacticalBorder06, "special_border_06" }, + { FocCommandBarComponentIds.TacticalBorder07, "special_border_07" }, + { FocCommandBarComponentIds.TacticalBorder08, "special_border_08" }, + { FocCommandBarComponentIds.TacticalBorder09, "special_border_09" }, + { FocCommandBarComponentIds.TacticalBorder10, "special_border_10" }, + { FocCommandBarComponentIds.TacticalBorder11, "special_border_11" }, + { FocCommandBarComponentIds.TacticalSelectButton00, "special_button_00" }, + { FocCommandBarComponentIds.TacticalSelectButton01, "special_button_01" }, + { FocCommandBarComponentIds.TacticalSelectButton02, "special_button_02" }, + { FocCommandBarComponentIds.TacticalSelectButton03, "special_button_03" }, + { FocCommandBarComponentIds.TacticalSelectButton04, "special_button_04" }, + { FocCommandBarComponentIds.TacticalSelectButton05, "special_button_05" }, + { FocCommandBarComponentIds.TacticalSelectButton06, "special_button_06" }, + { FocCommandBarComponentIds.TacticalSelectButton07, "special_button_07" }, + { FocCommandBarComponentIds.TacticalSelectButton08, "special_button_08" }, + { FocCommandBarComponentIds.TacticalSelectButton09, "special_button_09" }, + { FocCommandBarComponentIds.TacticalSelectButton10, "special_button_10" }, + { FocCommandBarComponentIds.TacticalSelectButton11, "special_button_11" }, + { FocCommandBarComponentIds.TacticalSelectButton12, "special_button_12" }, + { FocCommandBarComponentIds.TacticalSelectButton13, "special_button_13" }, + { FocCommandBarComponentIds.TacticalSelectButton14, "special_button_14" }, + { FocCommandBarComponentIds.TacticalSelectButton15, "special_button_15" }, + { FocCommandBarComponentIds.TacticalSelectButton16, "special_button_16" }, + { FocCommandBarComponentIds.TacticalSelectButton17, "special_button_17" }, + { FocCommandBarComponentIds.TacticalSelectButton18, "special_button_18" }, + { FocCommandBarComponentIds.TacticalSelectButton19, "special_button_19" }, + { FocCommandBarComponentIds.TacticalSelectButton20, "special_button_20" }, + { FocCommandBarComponentIds.TacticalSelectButton21, "special_button_21" }, + { FocCommandBarComponentIds.TacticalSelectButton22, "special_button_22" }, + { FocCommandBarComponentIds.TacticalSelectButton23, "special_button_23" }, + { FocCommandBarComponentIds.TacticalSelectButton24, "special_button_24" }, + { FocCommandBarComponentIds.TacticalSelectButton25, "special_button_25" }, + { FocCommandBarComponentIds.TacticalSelectButton26, "special_button_26" }, + { FocCommandBarComponentIds.TacticalSelectButton27, "special_button_27" }, + { FocCommandBarComponentIds.TacticalSelectButton28, "special_button_28" }, + { FocCommandBarComponentIds.TacticalSelectButton29, "special_button_29" }, + { FocCommandBarComponentIds.TacticalSelectButton30, "special_button_30" }, + { FocCommandBarComponentIds.TacticalSelectButton31, "special_button_31" }, + { FocCommandBarComponentIds.TacticalSelectButton32, "special_button_32" }, + { FocCommandBarComponentIds.TacticalSelectButton33, "special_button_33" }, + { FocCommandBarComponentIds.TacticalSelectButton34, "special_button_34" }, + { FocCommandBarComponentIds.TacticalSelectButton35, "special_button_35" }, + { FocCommandBarComponentIds.TacticalSelectButton36, "special_button_36" }, + { FocCommandBarComponentIds.TacticalSelectButton37, "special_button_37" }, + { FocCommandBarComponentIds.TacticalSelectButton38, "special_button_38" }, + { FocCommandBarComponentIds.TacticalSelectButton39, "special_button_39" }, + { FocCommandBarComponentIds.TacticalSelectButton40, "special_button_40" }, + { FocCommandBarComponentIds.TacticalSelectButton41, "special_button_41" }, + { FocCommandBarComponentIds.TacticalSelectButton42, "special_button_42" }, + { FocCommandBarComponentIds.TacticalSelectButton43, "special_button_43" }, + { FocCommandBarComponentIds.TacticalSelectButton44, "special_button_44" }, + { FocCommandBarComponentIds.TacticalSelectButton45, "special_button_45" }, + { FocCommandBarComponentIds.TacticalSelectButton46, "special_button_46" }, + { FocCommandBarComponentIds.TacticalSelectButton47, "special_button_47" }, + { FocCommandBarComponentIds.TacticalBuildQueue00, "tqueue00" }, + { FocCommandBarComponentIds.TacticalBuildQueue01, "tqueue01" }, + { FocCommandBarComponentIds.TacticalBuildQueue02, "tqueue02" }, + { FocCommandBarComponentIds.TacticalBuildQueue03, "tqueue03" }, + { FocCommandBarComponentIds.TacticalBuildQueue04, "tqueue04" }, + { FocCommandBarComponentIds.TacticalBuildQueue05, "tqueue05" }, + { FocCommandBarComponentIds.TacticalBuildQueue06, "tqueue06" }, + { FocCommandBarComponentIds.TacticalBuildQueue07, "tqueue07" }, + { FocCommandBarComponentIds.TacticalBuildQueue08, "tqueue08" }, + { FocCommandBarComponentIds.TacticalBuildQueue09, "tqueue09" }, + { FocCommandBarComponentIds.TooltipBack, "tooltip_back" }, + { FocCommandBarComponentIds.TooltipName, "tooltip_name" }, + { FocCommandBarComponentIds.TooltipPrice, "tooltip_price" }, + { FocCommandBarComponentIds.TooltipIcon, "tooltip_icon" }, + { FocCommandBarComponentIds.TooltipIconLand, "tooltip_icon_land" }, + { FocCommandBarComponentIds.TooltipLeftJustified, "tooltip_left_text" }, + { FocCommandBarComponentIds.EncyclopediaBack, "encyclopedia_back" }, + { FocCommandBarComponentIds.EncyclopediaHeaderText, "encyclopedia_header_text" }, + { FocCommandBarComponentIds.EncyclopediaText, "encyclopedia_text" }, + { FocCommandBarComponentIds.EncyclopediaRightText, "encyclopedia_right_text" }, + { FocCommandBarComponentIds.EncyclopediaCenterText, "encyclopedia_center_text" }, + { FocCommandBarComponentIds.EncyclopediaIcon, "encyclopedia_icon" }, + { FocCommandBarComponentIds.EncyclopediaCostText, "encyclopedia_cost_text" }, + { FocCommandBarComponentIds.ZoomedBack, "zoomed_back" }, + { FocCommandBarComponentIds.ZoomedHeaderText, "zoomed_header_text" }, + { FocCommandBarComponentIds.ZoomedText, "zoomed_text" }, + { FocCommandBarComponentIds.ZoomedRightText, "zoomed_right_text" }, + { FocCommandBarComponentIds.ZoomedCenterText, "zoomed_center_text" }, + { FocCommandBarComponentIds.ZoomedCostText, "zoomed_cost_text" }, + { FocCommandBarComponentIds.GPlanetFleet, "g_planet_fleet" }, + { FocCommandBarComponentIds.GPlanetName, "g_planet_name" }, + { FocCommandBarComponentIds.GPlanetValue, "g_planet_value" }, + { FocCommandBarComponentIds.GPoliticalControl, "g_political_control" }, + { FocCommandBarComponentIds.GSpaceLevel, "g_space_level" }, + { FocCommandBarComponentIds.GSpaceIcon, "g_space_icon" }, + { FocCommandBarComponentIds.GSpaceLevelPips, "g_space_level_pips" }, + { FocCommandBarComponentIds.GGroundLevel, "g_ground_level" }, + { FocCommandBarComponentIds.GGroundIcon, "g_ground_icon" }, + { FocCommandBarComponentIds.GGroundLevelPips, "g_ground_level_pips" }, + { FocCommandBarComponentIds.GConflict, "g_conflict" }, + { FocCommandBarComponentIds.GHero, "g_hero" }, + { FocCommandBarComponentIds.GEnemyHero, "g_enemy_hero" }, + { FocCommandBarComponentIds.GBuild, "g_build" }, + { FocCommandBarComponentIds.GSmuggler, "g_smuggler" }, + { FocCommandBarComponentIds.GBountyHunter, "g_bounty_hunter" }, + { FocCommandBarComponentIds.GPlanetLandForces, "g_planet_land_forces" }, + { FocCommandBarComponentIds.GGalacticRadarBlip, "g_radar_blip" }, + { FocCommandBarComponentIds.GGalacticRadarView, "g_radar_view" }, + { FocCommandBarComponentIds.GSmuggled, "g_smuggled" }, + { FocCommandBarComponentIds.GSpecialAbility, "g_special_ability" }, + { FocCommandBarComponentIds.GHeroIcon, "g_hero_icon" }, + { FocCommandBarComponentIds.GPlanetRing, "g_planet_ring" }, + { FocCommandBarComponentIds.GWeather, "g_weather" }, + { FocCommandBarComponentIds.GPlanetAbility, "g_planet_ability" }, + { FocCommandBarComponentIds.GCorruptionText, "g_corruption_text" }, + { FocCommandBarComponentIds.GCorruptionIcon, "g_corruption_icon" }, + { FocCommandBarComponentIds.TutorialText, "tutorial_text" }, + { FocCommandBarComponentIds.TutorialTextBack, "tutorial_text_back" }, + { FocCommandBarComponentIds.RadarBlip, "radar_blip" }, + { FocCommandBarComponentIds.TacticalBuildButtonShell, "i_build_buttons" }, + { FocCommandBarComponentIds.TacticalBuildButton0, "Build_00" }, + { FocCommandBarComponentIds.TacticalBuildButton1, "Build_01" }, + { FocCommandBarComponentIds.TacticalBuildButton2, "Build_02" }, + { FocCommandBarComponentIds.TacticalBuildButton3, "Build_03" }, + { FocCommandBarComponentIds.TacticalBuildButton4, "Build_04" }, + { FocCommandBarComponentIds.TacticalBuildButton5, "Build_05" }, + { FocCommandBarComponentIds.TacticalSellButton, "tactical_sell" }, + { FocCommandBarComponentIds.ReinforcementShell, "i_main_reinforce" }, + { FocCommandBarComponentIds.ReinforcementCancel, "r_close" }, + { FocCommandBarComponentIds.ReinforcementSlot00, "r_0000" }, + { FocCommandBarComponentIds.ReinforcementSlot01, "r_0001" }, + { FocCommandBarComponentIds.ReinforcementSlot02, "r_0002" }, + { FocCommandBarComponentIds.ReinforcementSlot03, "r_0003" }, + { FocCommandBarComponentIds.ReinforcementSlot04, "r_0100" }, + { FocCommandBarComponentIds.ReinforcementSlot05, "r_0101" }, + { FocCommandBarComponentIds.ReinforcementSlot06, "r_0102" }, + { FocCommandBarComponentIds.ReinforcementSlot07, "r_0103" }, + { FocCommandBarComponentIds.ReinforcementSlot08, "r_0200" }, + { FocCommandBarComponentIds.ReinforcementSlot09, "r_0201" }, + { FocCommandBarComponentIds.ReinforcementSlot10, "r_0202" }, + { FocCommandBarComponentIds.ReinforcementSlot11, "r_0203" }, + { FocCommandBarComponentIds.ReinforcementSlot12, "r_0300" }, + { FocCommandBarComponentIds.ReinforcementSlot13, "r_0301" }, + { FocCommandBarComponentIds.ReinforcementSlot14, "r_0302" }, + { FocCommandBarComponentIds.ReinforcementSlot15, "r_0303" }, + { FocCommandBarComponentIds.ReinforcementSlot16, "r_0400" }, + { FocCommandBarComponentIds.ReinforcementSlot17, "r_0401" }, + { FocCommandBarComponentIds.ReinforcementSlot18, "r_0402" }, + { FocCommandBarComponentIds.ReinforcementSlot19, "r_0403" }, + { FocCommandBarComponentIds.ReinforcementCap2, "r_pop_icon" }, + { FocCommandBarComponentIds.ReinforcementCap2Text, "r_pop_text" }, + { FocCommandBarComponentIds.ReinforcementCounter, "reinforcement_counter" }, + { FocCommandBarComponentIds.GarrisonRespawnCounter, "garrison_respawn_counter" }, + { FocCommandBarComponentIds.SkirmishUpgrade, "skirmish_upgrade" }, + { FocCommandBarComponentIds.PendingBattleShell, "pending_battle_shell" }, + { FocCommandBarComponentIds.PendingBattleText, "text_attack_choice" }, + { FocCommandBarComponentIds.PendingBattleButton, "choice_button_left" }, + { FocCommandBarComponentIds.PendingBattleAutoresolve, "choice_button_right" }, + { FocCommandBarComponentIds.PendingBattleGraphLeft, "graph_left" }, + { FocCommandBarComponentIds.PendingBattleGraphRight, "graph_right" }, + { FocCommandBarComponentIds.TatcicalAutoresolveShell, "autoresolve_shell" }, + { FocCommandBarComponentIds.TacticalAutoresolveButton, "resolve_button" }, + { FocCommandBarComponentIds.TacticalAutoresolveGraphLeft, "graph_left_resolve" }, + { FocCommandBarComponentIds.TacticalAutoresolveGraphRight, "graph_right_resolve" }, + { FocCommandBarComponentIds.ObjectiveBack, "objective_back" }, + { FocCommandBarComponentIds.ObjectiveHeaderText, "objective_header_text" }, + { FocCommandBarComponentIds.ObjectiveText, "objective_text" }, + { FocCommandBarComponentIds.ObjectiveIcon, "objective_icon" }, + { FocCommandBarComponentIds.GuiDialogTooltip, "gui_dialog_tooltip" }, + { FocCommandBarComponentIds.TargetUnitTypeShell, "target_type_back" }, + { FocCommandBarComponentIds.TargetUnitTypeTitle, "h_title" }, + { FocCommandBarComponentIds.TargetUnitTypeCancel, "h_close" }, + { FocCommandBarComponentIds.TargetUnitTypeDescription, "text_hero" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot00, "h_0000" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot01, "h_0001" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot02, "h_0100" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot03, "h_0101" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot04, "h_0200" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot05, "h_0201" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot06, "h_0300" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot07, "h_0301" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot08, "h_0400" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot09, "h_0401" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot10, "h_0500" }, + { FocCommandBarComponentIds.TargetUnitTypeSlot11, "h_0501" }, + { FocCommandBarComponentIds.TargetUnitTypeName00, "h_name_00" }, + { FocCommandBarComponentIds.TargetUnitTypeName01, "h_name_01" }, + { FocCommandBarComponentIds.TargetUnitTypeName02, "h_name_02" }, + { FocCommandBarComponentIds.TargetUnitTypeName03, "h_name_03" }, + { FocCommandBarComponentIds.TargetUnitTypeName04, "h_name_04" }, + { FocCommandBarComponentIds.TargetUnitTypeName05, "h_name_05" }, + { FocCommandBarComponentIds.TargetUnitTypeName06, "h_name_06" }, + { FocCommandBarComponentIds.TargetUnitTypeName07, "h_name_07" }, + { FocCommandBarComponentIds.TargetUnitTypeName08, "h_name_08" }, + { FocCommandBarComponentIds.TargetUnitTypeName09, "h_name_09" }, + { FocCommandBarComponentIds.TargetUnitTypeName10, "h_name_10" }, + { FocCommandBarComponentIds.TargetUnitTypeName11, "h_name_11" }, + { FocCommandBarComponentIds.TargetUnitTypePrice00, "h_price_00" }, + { FocCommandBarComponentIds.TargetUnitTypePrice01, "h_price_01" }, + { FocCommandBarComponentIds.TargetUnitTypePrice02, "h_price_02" }, + { FocCommandBarComponentIds.TargetUnitTypePrice03, "h_price_03" }, + { FocCommandBarComponentIds.TargetUnitTypePrice04, "h_price_04" }, + { FocCommandBarComponentIds.TargetUnitTypePrice05, "h_price_05" }, + { FocCommandBarComponentIds.TargetUnitTypePrice06, "h_price_06" }, + { FocCommandBarComponentIds.TargetUnitTypePrice07, "h_price_07" }, + { FocCommandBarComponentIds.TargetUnitTypePrice08, "h_price_08" }, + { FocCommandBarComponentIds.TargetUnitTypePrice09, "h_price_09" }, + { FocCommandBarComponentIds.TargetUnitTypePrice10, "h_price_10" }, + { FocCommandBarComponentIds.TargetUnitTypePrice11, "h_price_11" }, + { FocCommandBarComponentIds.VcrButtonPlayPause, "b_play_pause" }, + { FocCommandBarComponentIds.VcrButtonFastForward, "b_fast_forward" }, + { FocCommandBarComponentIds.VcrButtonFastForwardTactical, "b_fast_forward_t" }, + { FocCommandBarComponentIds.VcrButtonPlayPauseTactical, "b_play_pause_t" }, + { FocCommandBarComponentIds.AdvisorHintPopupGalactic, "text_galactic_help" }, + { FocCommandBarComponentIds.AdvisorHintPopupTactical, "text_tactical_help" }, + { FocCommandBarComponentIds.AdvisorHintBack, "help_back" }, + { FocCommandBarComponentIds.TextOrganizeFleet00, "text_fleet1" }, + { FocCommandBarComponentIds.TextOrganizeFleet01, "text_fleet2" }, + { FocCommandBarComponentIds.TextOrganizeFleet02, "text_fleet3" }, + { FocCommandBarComponentIds.IconOrganizeFleet00, "icon_fleet01" }, + { FocCommandBarComponentIds.IconOrganizeFleet01, "icon_fleet02" }, + { FocCommandBarComponentIds.IconOrganizeFleet02, "icon_fleet03" }, + { FocCommandBarComponentIds.TextOrganizeLandFleet, "Text_Land_Fleet" }, + { FocCommandBarComponentIds.CsAbilityButton, "cs_ability_button" }, + { FocCommandBarComponentIds.CsAbilityText, "cs_ability_text" }, + { FocCommandBarComponentIds.MovieBoneGalactic, "Movie_galactic" }, + { FocCommandBarComponentIds.MovieBoneTactical, "Movie_tactical" }, + { FocCommandBarComponentIds.GenericCollision, "generic_collision" }, + { FocCommandBarComponentIds.GoodHeroShell, "good_hero_frame" }, + { FocCommandBarComponentIds.GoodHeroSlot00, "hero_slot_good00" }, + { FocCommandBarComponentIds.GoodHeroSlot01, "hero_slot_good01" }, + { FocCommandBarComponentIds.GoodHeroSlot02, "hero_slot_good02" }, + { FocCommandBarComponentIds.GoodHeroSlot03, "hero_slot_good03" }, + { FocCommandBarComponentIds.GoodHeroSlot04, "hero_slot_good04" }, + { FocCommandBarComponentIds.GoodHeroSlot05, "hero_slot_good05" }, + { FocCommandBarComponentIds.GoodHeroSlot06, "hero_slot_good06" }, + { FocCommandBarComponentIds.GoodHeroSlot07, "hero_slot_good07" }, + { FocCommandBarComponentIds.GoodHeroSlot08, "hero_slot_good08" }, + { FocCommandBarComponentIds.GoodHeroSlot09, "hero_slot_good09" }, + { FocCommandBarComponentIds.GoodHeroSlot10, "hero_slot_good10" }, + { FocCommandBarComponentIds.GoodHeroHealth00, "hero_hb_good00" }, + { FocCommandBarComponentIds.GoodHeroHealth01, "hero_hb_good01" }, + { FocCommandBarComponentIds.GoodHeroHealth02, "hero_hb_good02" }, + { FocCommandBarComponentIds.GoodHeroHealth03, "hero_hb_good03" }, + { FocCommandBarComponentIds.GoodHeroHealth04, "hero_hb_good04" }, + { FocCommandBarComponentIds.GoodHeroHealth05, "hero_hb_good05" }, + { FocCommandBarComponentIds.GoodHeroHealth06, "hero_hb_good06" }, + { FocCommandBarComponentIds.GoodHeroHealth07, "hero_hb_good07" }, + { FocCommandBarComponentIds.GoodHeroHealth08, "hero_hb_good08" }, + { FocCommandBarComponentIds.GoodHeroHealth09, "hero_hb_good09" }, + { FocCommandBarComponentIds.GoodHeroHealth10, "hero_hb_good10" }, + { FocCommandBarComponentIds.EvilHeroShell, "evil_hero_frame" }, + { FocCommandBarComponentIds.EvilHeroSlot00, "hero_slot_evil00" }, + { FocCommandBarComponentIds.EvilHeroSlot01, "hero_slot_evil01" }, + { FocCommandBarComponentIds.EvilHeroSlot02, "hero_slot_evil02" }, + { FocCommandBarComponentIds.EvilHeroSlot03, "hero_slot_evil03" }, + { FocCommandBarComponentIds.EvilHeroSlot04, "hero_slot_evil04" }, + { FocCommandBarComponentIds.EvilHeroSlot05, "hero_slot_evil05" }, + { FocCommandBarComponentIds.EvilHeroSlot06, "hero_slot_evil06" }, + { FocCommandBarComponentIds.EvilHeroSlot07, "hero_slot_evil07" }, + { FocCommandBarComponentIds.EvilHeroSlot08, "hero_slot_evil08" }, + { FocCommandBarComponentIds.EvilHeroSlot09, "hero_slot_evil09" }, + { FocCommandBarComponentIds.EvilHeroSlot10, "hero_slot_evil10" }, + { FocCommandBarComponentIds.EvilHeroHealth00, "hero_hb_evil00" }, + { FocCommandBarComponentIds.EvilHeroHealth01, "hero_hb_evil01" }, + { FocCommandBarComponentIds.EvilHeroHealth02, "hero_hb_evil02" }, + { FocCommandBarComponentIds.EvilHeroHealth03, "hero_hb_evil03" }, + { FocCommandBarComponentIds.EvilHeroHealth04, "hero_hb_evil04" }, + { FocCommandBarComponentIds.EvilHeroHealth05, "hero_hb_evil05" }, + { FocCommandBarComponentIds.EvilHeroHealth06, "hero_hb_evil06" }, + { FocCommandBarComponentIds.EvilHeroHealth07, "hero_hb_evil07" }, + { FocCommandBarComponentIds.EvilHeroHealth08, "hero_hb_evil08" }, + { FocCommandBarComponentIds.EvilHeroHealth09, "hero_hb_evil09" }, + { FocCommandBarComponentIds.EvilHeroHealth10, "hero_hb_evil10" }, + { FocCommandBarComponentIds.PauseShell, "pause_shell" }, + { FocCommandBarComponentIds.PauseText, "text_attack" }, + { FocCommandBarComponentIds.PauseButton, "attack_button" }, + { FocCommandBarComponentIds.StoryCampaignPendingBattleShell, "story_pending_battle_shell" }, + { FocCommandBarComponentIds.StoryCampaignPendingBattleText, "text_attack_single" }, + { FocCommandBarComponentIds.StoryCampaignPendingBattleButton, "attack_button_single" }, + { FocCommandBarComponentIds.MapActivate, "advanced_planetary" }, + { FocCommandBarComponentIds.MapShell, "map_shell" }, + { FocCommandBarComponentIds.MapOverlayShell, "map_overlay_shell" }, + { FocCommandBarComponentIds.MapGroundForcesText, "ground_forces_text" }, + { FocCommandBarComponentIds.MapDragDropText, "drage_drop_text" }, + { FocCommandBarComponentIds.MapUnitCapText, "unit_cap_text" }, + { FocCommandBarComponentIds.MapUnitCapNumber, "unit_cap_number" }, + { FocCommandBarComponentIds.MapBuildingCapText, "building_cap_text" }, + { FocCommandBarComponentIds.MapBuildingCapNumber, "building_cap_number" }, + { FocCommandBarComponentIds.MapFactionIcon, "Allegiance_A" }, + { FocCommandBarComponentIds.MapPlanetNameText, "Planet_name_text" }, + { FocCommandBarComponentIds.MapPlanetIncomeNumber, "Planetary_Income_number" }, + { FocCommandBarComponentIds.MapDayCounterText, "Day_Counter_text" }, + { FocCommandBarComponentIds.MapDayCounter, "day_clock" }, + { FocCommandBarComponentIds.MapBackButton, "back_button" }, + { FocCommandBarComponentIds.MapWeatherIcon, "weather" }, + { FocCommandBarComponentIds.MapPlanetBonusIcon, "planet_bonus" }, + { FocCommandBarComponentIds.MapRadarMap, "radar_galactic_adv_map" }, + { FocCommandBarComponentIds.MapZoomOut, "b_zoom_adv_map" }, + { FocCommandBarComponentIds.MapEncyclopediaPopup, "Text_adv_help" }, + { FocCommandBarComponentIds.AdvancedMapFilter0, "c_filters0" }, + { FocCommandBarComponentIds.AdvancedMapFilter1, "c_filters1" }, + { FocCommandBarComponentIds.AdvancedMapFilter2, "c_filters2" }, + { FocCommandBarComponentIds.AdvancedMapFilter3, "c_filters3" }, + { FocCommandBarComponentIds.AdvancedMapOptions, "c_option_g" }, + { FocCommandBarComponentIds.MapMainMap, "map_window" }, + { FocCommandBarComponentIds.MapSpaceUnit00, "land_fleet_00" }, + { FocCommandBarComponentIds.MapSpaceUnit01, "land_fleet_01" }, + { FocCommandBarComponentIds.MapSpaceUnit02, "land_fleet_02" }, + { FocCommandBarComponentIds.MapSpaceUnit03, "land_fleet_03" }, + { FocCommandBarComponentIds.MapSpaceUnit04, "land_fleet_04" }, + { FocCommandBarComponentIds.MapSpaceUnit05, "land_fleet_05" }, + { FocCommandBarComponentIds.MapSpaceUnit06, "land_fleet_06" }, + { FocCommandBarComponentIds.MapSpaceUnit07, "land_fleet_07" }, + { FocCommandBarComponentIds.MapSpaceUnit08, "land_fleet_08" }, + { FocCommandBarComponentIds.MapSpaceUnit09, "land_fleet_09" }, + { FocCommandBarComponentIds.MapSpaceUnit10, "land_fleet_10" }, + { FocCommandBarComponentIds.MapSpaceUnit11, "land_fleet_11" }, + { FocCommandBarComponentIds.MapSpaceUnit12, "land_fleet_12" }, + { FocCommandBarComponentIds.MapSpaceUnit13, "land_fleet_13" }, + { FocCommandBarComponentIds.MapSpaceUnit14, "land_fleet_14" }, + { FocCommandBarComponentIds.MapSpaceUnit15, "land_fleet_15" }, + { FocCommandBarComponentIds.MapSpaceUnit16, "land_fleet_16" }, + { FocCommandBarComponentIds.MapSpaceUnit17, "land_fleet_17" }, + { FocCommandBarComponentIds.MapSpaceUnit18, "land_fleet_18" }, + { FocCommandBarComponentIds.MapSpaceUnit19, "land_fleet_19" }, + { FocCommandBarComponentIds.MapBuildPad00, "build_pad00" }, + { FocCommandBarComponentIds.MapBuildPad01, "build_pad01" }, + { FocCommandBarComponentIds.MapBuildPad02, "build_pad02" }, + { FocCommandBarComponentIds.MapBuildPad03, "build_pad03" }, + { FocCommandBarComponentIds.MapBuildPad04, "build_pad04" }, + { FocCommandBarComponentIds.MapBuildPad05, "build_pad05" }, + { FocCommandBarComponentIds.MapBuildPad06, "build_pad06" }, + { FocCommandBarComponentIds.MapBuildPad07, "build_pad07" }, + { FocCommandBarComponentIds.MapBuildPad08, "build_pad08" }, + { FocCommandBarComponentIds.MapBuildPad09, "build_pad09" }, + { FocCommandBarComponentIds.MapBuildPad10, "build_pad10" }, + { FocCommandBarComponentIds.MapBuildPad11, "build_pad11" }, + { FocCommandBarComponentIds.MapBuildPad12, "build_pad12" }, + { FocCommandBarComponentIds.MapBuildPad13, "build_pad13" }, + { FocCommandBarComponentIds.MapBuildPad14, "build_pad14" }, + { FocCommandBarComponentIds.MapBuildPad15, "build_pad15" }, + { FocCommandBarComponentIds.MapBuildPad16, "build_pad16" }, + { FocCommandBarComponentIds.MapBuildPad17, "build_pad17" }, + { FocCommandBarComponentIds.MapBuildPad18, "build_pad18" }, + { FocCommandBarComponentIds.MapBuildPad19, "build_pad19" }, + { FocCommandBarComponentIds.MapBuildPad20, "build_pad20" }, + { FocCommandBarComponentIds.MapBuildPad21, "build_pad21" }, + { FocCommandBarComponentIds.MapBuildPad22, "build_pad22" }, + { FocCommandBarComponentIds.MapBuildPad23, "build_pad23" }, + { FocCommandBarComponentIds.MapBuildPad24, "build_pad24" }, + { FocCommandBarComponentIds.MapBuildPad25, "build_pad25" }, + { FocCommandBarComponentIds.MapBuildPad26, "build_pad26" }, + { FocCommandBarComponentIds.MapBuildPad27, "build_pad27" }, + { FocCommandBarComponentIds.MapBuildPad28, "build_pad28" }, + { FocCommandBarComponentIds.MapBuildPad29, "build_pad29" }, + { FocCommandBarComponentIds.MapBuildPad30, "build_pad30" }, + { FocCommandBarComponentIds.MapBuildPad31, "build_pad31" }, + { FocCommandBarComponentIds.MapBuildPad32, "build_pad32" }, + { FocCommandBarComponentIds.MapReinforcePad00, "build_pad33" }, + { FocCommandBarComponentIds.MapReinforcePad01, "build_pad34" }, + { FocCommandBarComponentIds.MapReinforcePad02, "build_pad35" }, + { FocCommandBarComponentIds.MapReinforcePad03, "build_pad36" }, + { FocCommandBarComponentIds.MapReinforcePad04, "build_pad37" }, + { FocCommandBarComponentIds.MapReinforcePad05, "build_pad38" }, + { FocCommandBarComponentIds.MapReinforcePad06, "build_pad39" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad00, "build_pad40" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad01, "build_pad41" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad02, "build_pad42" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad03, "build_pad43" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad04, "build_pad44" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad05, "build_pad45" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad06, "build_pad46" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad07, "build_pad47" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad08, "build_pad48" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad09, "build_pad49" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad10, "build_pad50" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad11, "build_pad51" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad12, "build_pad52" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad13, "build_pad53" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad14, "build_pad54" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad15, "build_pad55" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad16, "build_pad56" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad17, "build_pad57" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad18, "build_pad58" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad19, "build_pad59" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad20, "build_pad60" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad21, "build_pad61" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad22, "build_pad62" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad23, "build_pad63" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad24, "build_pad64" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad25, "build_pad65" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad26, "build_pad66" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad27, "build_pad67" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad28, "build_pad68" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad29, "build_pad69" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad30, "build_pad70" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad31, "build_pad71" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad32, "build_pad72" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad33, "build_pad73" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad34, "build_pad74" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad35, "build_pad75" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad36, "build_pad76" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad37, "build_pad77" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad38, "build_pad78" }, + { FocCommandBarComponentIds.MapBunkerAndBuildPad39, "build_pad79" }, + { FocCommandBarComponentIds.MapUnitPad00, "unit_pad00" }, + { FocCommandBarComponentIds.MapUnitPad01, "unit_pad01" }, + { FocCommandBarComponentIds.MapUnitPad02, "unit_pad02" }, + { FocCommandBarComponentIds.MapUnitPad03, "unit_pad03" }, + { FocCommandBarComponentIds.MapUnitPad04, "unit_pad04" }, + { FocCommandBarComponentIds.MapUnitPad05, "unit_pad05" }, + { FocCommandBarComponentIds.MapUnitPad06, "unit_pad06" }, + { FocCommandBarComponentIds.MapUnitPad07, "unit_pad07" }, + { FocCommandBarComponentIds.MapUnitPad08, "unit_pad08" }, + { FocCommandBarComponentIds.MapUnitPad09, "unit_pad09" }, + { FocCommandBarComponentIds.BribeDisplay, "bribe_display" }, + { FocCommandBarComponentIds.BlackMarketShell, "market_frame" }, + { FocCommandBarComponentIds.BlackMarketTitle, "market_title00" }, + { FocCommandBarComponentIds.BlackMarketPlanet, "market_title01" }, + { FocCommandBarComponentIds.BlackMarketTechLevelHeader, "market_title02" }, + { FocCommandBarComponentIds.BlackMarketSlotHeader, "market_title03" }, + { FocCommandBarComponentIds.BlackMarketCancel, "market_close" }, + { FocCommandBarComponentIds.BlackMarketEncyclopedia, "i_blackmarket_encycl" }, + { FocCommandBarComponentIds.BlackMarketTechLevel00, "market_tech_level00" }, + { FocCommandBarComponentIds.BlackMarketTechLevel01, "market_tech_level01" }, + { FocCommandBarComponentIds.BlackMarketTechLevel02, "market_tech_level02" }, + { FocCommandBarComponentIds.BlackMarketTechLevel03, "market_tech_level03" }, + { FocCommandBarComponentIds.BlackMarketTechLevel04, "market_tech_level04" }, + { FocCommandBarComponentIds.BlackMarketSlot00, "market_00" }, + { FocCommandBarComponentIds.BlackMarketSlot01, "market_01" }, + { FocCommandBarComponentIds.BlackMarketSlot02, "market_02" }, + { FocCommandBarComponentIds.BlackMarketSlot03, "market_03" }, + { FocCommandBarComponentIds.BlackMarketSlot04, "market_04" }, + { FocCommandBarComponentIds.BlackMarketSlotDescription00, "market_title04" }, + { FocCommandBarComponentIds.BlackMarketSlotDescription01, "market_title06" }, + { FocCommandBarComponentIds.BlackMarketSlotDescription02, "market_title08" }, + { FocCommandBarComponentIds.BlackMarketSlotDescription03, "market_title10" }, + { FocCommandBarComponentIds.BlackMarketSlotDescription04, "market_title12" }, + { FocCommandBarComponentIds.BlackMarketSlotPrice00, "market_title05" }, + { FocCommandBarComponentIds.BlackMarketSlotPrice01, "market_title07" }, + { FocCommandBarComponentIds.BlackMarketSlotPrice02, "market_title09" }, + { FocCommandBarComponentIds.BlackMarketSlotPrice03, "market_title11" }, + { FocCommandBarComponentIds.BlackMarketSlotPrice04, "market_title13" }, + { FocCommandBarComponentIds.SabotageShell, "bm_frame" }, + { FocCommandBarComponentIds.SabotageTitle, "bm_title" }, + { FocCommandBarComponentIds.SabotageTitle2, "bm_title01" }, + { FocCommandBarComponentIds.SabotageTechLevel, "bm_tech_level" }, + { FocCommandBarComponentIds.SabotageCancel, "bm_close" }, + { FocCommandBarComponentIds.SabotageDescription, "bm_text_steal" }, + { FocCommandBarComponentIds.SabotageSlot00, "bm_0000" }, + { FocCommandBarComponentIds.SabotageSlot01, "bm_0001" }, + { FocCommandBarComponentIds.SabotageSlot02, "bm_0100" }, + { FocCommandBarComponentIds.SabotageSlot03, "bm_0101" }, + { FocCommandBarComponentIds.SabotageSlot04, "bm_0200" }, + { FocCommandBarComponentIds.SabotageSlot05, "bm_0201" }, + { FocCommandBarComponentIds.SabotageSlot06, "bm_0300" }, + { FocCommandBarComponentIds.SabotageSlot07, "bm_0301" }, + { FocCommandBarComponentIds.SabotageSlot08, "bm_0400" }, + { FocCommandBarComponentIds.SabotageSlot09, "bm_0401" }, + { FocCommandBarComponentIds.SabotagePrice00, "bm_title_000" }, + { FocCommandBarComponentIds.SabotagePrice01, "bm_title_001" }, + { FocCommandBarComponentIds.SabotagePrice02, "bm_title_100" }, + { FocCommandBarComponentIds.SabotagePrice03, "bm_title_101" }, + { FocCommandBarComponentIds.SabotagePrice04, "bm_title_200" }, + { FocCommandBarComponentIds.SabotagePrice05, "bm_title_201" }, + { FocCommandBarComponentIds.SabotagePrice06, "bm_title_300" }, + { FocCommandBarComponentIds.SabotagePrice07, "bm_title_301" }, + { FocCommandBarComponentIds.SabotagePrice08, "bm_title_400" }, + { FocCommandBarComponentIds.SabotagePrice09, "bm_title_401" }, + { FocCommandBarComponentIds.SabotageName00, "bm_title_402" }, + { FocCommandBarComponentIds.SabotageName01, "bm_title_403" }, + { FocCommandBarComponentIds.SabotageName02, "bm_title_404" }, + { FocCommandBarComponentIds.SabotageName03, "bm_title_405" }, + { FocCommandBarComponentIds.SabotageName04, "bm_title_406" }, + { FocCommandBarComponentIds.SabotageName05, "bm_title_407" }, + { FocCommandBarComponentIds.SabotageName06, "bm_title_408" }, + { FocCommandBarComponentIds.SabotageName07, "bm_title_409" }, + { FocCommandBarComponentIds.SabotageName08, "bm_title_4010" }, + { FocCommandBarComponentIds.SabotageName09, "bm_title_4011" }, + { FocCommandBarComponentIds.PlanetaryBombardment, "pb_switch" }, + { FocCommandBarComponentIds.PlanetaryBombardmentRecharge, "pb_switch_recharge" }, + { FocCommandBarComponentIds.SuperLaser, "sl_switch" }, + { FocCommandBarComponentIds.SuperLaserRecharge, "sl_switch_recharge" }, + { FocCommandBarComponentIds.GenericFlytext, "generic_flytext" }, + { FocCommandBarComponentIds.BribedIcon, "bribed_icon" }, + { FocCommandBarComponentIds.SurfaceModIcon, "surface_mod_icon" }, + { FocCommandBarComponentIds.RemoteBombIcon, "remote_bomb_icon" }, + { FocCommandBarComponentIds.CorruptionShell, "corruption_shell" }, + { FocCommandBarComponentIds.CorruptionTitle, "corrupt_planet_title00" }, + { FocCommandBarComponentIds.CorruptionPlanetName, "corrupt_planet_title01" }, + { FocCommandBarComponentIds.CorruptionEncyclopedia, "i_corrupt_encycl" }, + { FocCommandBarComponentIds.CorruptionClose, "corrupt_close" }, + { FocCommandBarComponentIds.CorruptionPlanetModel, "i_planet_image" }, + { FocCommandBarComponentIds.CorruptionInfo00, "corrupt_planet_defiler_00" }, + { FocCommandBarComponentIds.CorruptionInfo01, "corrupt_planet_defiler_01" }, + { FocCommandBarComponentIds.CorruptionChoiceIcon0, "corrupt_button_icon_a" }, + { FocCommandBarComponentIds.CorruptionChoiceIcon1, "corrupt_button_icon_b" }, + { FocCommandBarComponentIds.CorruptionChoiceIcon2, "corrupt_button_icon_c" }, + { FocCommandBarComponentIds.CorruptionChoiceCost0, "corrupt_cost_a" }, + { FocCommandBarComponentIds.CorruptionChoiceCost1, "corrupt_cost_b" }, + { FocCommandBarComponentIds.CorruptionChoiceCost2, "corrupt_cost_c" }, + { FocCommandBarComponentIds.CorruptionChoiceTitle0, "corrupt_title_a" }, + { FocCommandBarComponentIds.CorruptionChoiceTitle1, "corrupt_title_b" }, + { FocCommandBarComponentIds.CorruptionChoiceTitle2, "corrupt_title_c" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText00, "corrupt_text00a" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText01, "corrupt_text01a" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText02, "corrupt_text02a" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText03, "corrupt_text03a" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText10, "corrupt_text00b" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText11, "corrupt_text01b" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText12, "corrupt_text02b" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText13, "corrupt_text03b" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText20, "corrupt_text00c" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText21, "corrupt_text01c" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText22, "corrupt_text02c" }, + { FocCommandBarComponentIds.CorruptionChoiceBenefitText23, "corrupt_text03c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirement0, "corrupt_text04a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirement1, "corrupt_text04b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirement2, "corrupt_text04c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0001, "corrupt_icon_00a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0002, "corrupt_icon_01a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0003, "corrupt_icon_02a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0000, "corrupt_icon_03a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0100, "corrupt_icon_04a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0101, "corrupt_icon_05a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0102, "corrupt_icon_06a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon0103, "corrupt_icon_07a" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1000, "corrupt_icon_00b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1001, "corrupt_icon_01b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1002, "corrupt_icon_02b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1003, "corrupt_icon_03b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1100, "corrupt_icon_04b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1101, "corrupt_icon_05b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1102, "corrupt_icon_06b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon1103, "corrupt_icon_07b" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2000, "corrupt_icon_00c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2001, "corrupt_icon_01c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2002, "corrupt_icon_02c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2003, "corrupt_icon_03c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2100, "corrupt_icon_04c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2101, "corrupt_icon_05c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2102, "corrupt_icon_06c" }, + { FocCommandBarComponentIds.CorruptionChoiceRequirementIcon2103, "corrupt_icon_07c" }, + { FocCommandBarComponentIds.HackSuperWeaponShell, "hack_shell" }, + { FocCommandBarComponentIds.HackSuperWeaponTitle, "hack_title01" }, + { FocCommandBarComponentIds.HackSuperWeaponText00, "hack_text_00" }, + { FocCommandBarComponentIds.HackSuperWeaponText01, "hack_text_01" }, + { FocCommandBarComponentIds.HackSuperWeaponText02, "hack_text_02" }, + { FocCommandBarComponentIds.HackSuperWeaponText03, "hack_text_03" }, + { FocCommandBarComponentIds.HackSuperWeaponText04, "hack_text_04" }, + { FocCommandBarComponentIds.HackSuperWeaponText05, "hack_text_05" }, + { FocCommandBarComponentIds.HackSuperWeaponText06, "hack_text_06" }, + { FocCommandBarComponentIds.HackSuperWeaponCost, "hack_cost" }, + { FocCommandBarComponentIds.HackSuperWeaponAccept, "hack_hack" }, + { FocCommandBarComponentIds.HackSuperWeaponCancel, "hack_close" }, }; } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs index 32359ef..1893f81 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs @@ -29,9 +29,7 @@ public void Report(XmlError error) public void Report(InitializationError error) { InitializationError?.Invoke(this, error); - if (_errorReporter is null) - return; - _errorReporter.Report(error); + _errorReporter?.Report(error); } public void Assert(EngineAssert assert) @@ -44,7 +42,7 @@ public override void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs if (_errorReporter is null) return; - _logger?.LogWarning($"Xml parser '{parser}' reported error: {error.Message}"); + _logger?.LogWarning($"Xml parser '{parser}' reported error for file '{error.Location.XmlFile}': {error.Message}"); Report(new XmlError { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs index 06305b7..22f4949 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/EffectsRepository.cs @@ -24,7 +24,7 @@ private protected override FileFoundInfo MultiPassAction( ref ValueStringBuilder destination, bool megFileOnly) { - var strippedName = PGPathUtilities.StripFileName(filePath); + var strippedName = StripFileName(filePath); if (strippedName.Length > PGConstants.MaxEffectFileName) return default; @@ -84,4 +84,21 @@ private FileFoundInfo FindEffect( return BaseRepository.FindFile(multiPassStringBuilder.AsSpan(), ref filePathStringBuilder); } + + private static ReadOnlySpan StripFileName(ReadOnlySpan src) + { + var destination = src; + + for (var i = src.Length - 1; i >= 0; --i) + { + + if (src[i] == '.') + destination = src.Slice(0, i); + + if (src[i] == '/' || src[i] == '\\') + break; + } + + return destination; + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs index 928754e..1b6cb6d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/ModelRepository.cs @@ -26,7 +26,7 @@ private protected override FileFoundInfo MultiPassAction( destination.Length = 0; - var stripped = PGPathUtilities.StripFileName(filePath); + var stripped = StripFileName(filePath); var path = FileSystem.Path.GetDirectoryName(filePath); FileSystem.Path.Join(path, stripped, ref reusableStringBuilder); @@ -43,4 +43,20 @@ private static bool IsValidSize(ReadOnlySpan path) { return path.Length != 0 && path.Length < PGConstants.MaxModelFileName; } + + private static ReadOnlySpan StripFileName(ReadOnlySpan src) + { + var tmp = src; + + for (var i = src.Length - 1; i >= 0; --i) + { + if (src[i] == '.') + tmp = src.Slice(0, i); + + if (src[i] == '/' || src[i] == '\\') + return tmp.Slice(i + 1); + } + + return tmp; + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs index 8a6f221..471b5a8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/PathExtensions.cs @@ -27,24 +27,4 @@ public static void Join(this IPath _, ReadOnlySpan path1, ReadOnlySpan StripFileName(ReadOnlySpan src) - { - var destination = src; - - for (var i = src.Length - 1; i >= 0; --i) - { - - if (src[i] == '.') - destination = src.Slice(0, i); - - if (src[i] == '/' || src[i] == '\\') - break; - } - - return destination; - } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs similarity index 66% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs index 1c286bb..03916fe 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/AnimationCollection.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/AnimationCollection.cs @@ -1,14 +1,18 @@ using System; +using System.Collections; +using System.Collections.Generic; using AnakinRaW.CommonUtilities; using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Collections; using PG.Commons.Hashing; using PG.StarWarsGame.Files.ALO.Files.Animations; -namespace PG.StarWarsGame.Engine.Rendering; +namespace PG.StarWarsGame.Engine.Rendering.Animations; -public sealed class AnimationCollection : DisposableObject +public sealed class AnimationCollection : DisposableObject, IEnumerable { + public static readonly AnimationCollection Empty = new(); + private readonly ValueListDictionary _animations = new(); private readonly ValueListDictionary _animationCrc = new(); @@ -44,6 +48,19 @@ public IAloAnimationFile GetAnimation(ModelAnimationType type, int subIndex) return animations[subIndex]; } + public bool TryGetAnimation(ModelAnimationType type, int subIndex, out IAloAnimationFile? animation) + { + animation = null; + if (subIndex < 0) + throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be negative."); + if (!TryGetAnimations(type, out var animations)) + return false; + if (subIndex >= animations.Count) + throw new ArgumentOutOfRangeException(nameof(subIndex), "subIndex cannot be larger than stored animations."); + animation = animations[subIndex]; + return true; + } + internal void AddAnimation(ModelAnimationType type, IAloAnimationFile animation, Crc32 crc) { _animations.Add(type, animation); @@ -57,4 +74,14 @@ protected override void DisposeResources() _animationCrc.Clear(); _animations.Clear(); } + + public IEnumerator GetEnumerator() + { + return _animations.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs new file mode 100644 index 0000000..f1a89c3 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/EawModelAnimationTypes.cs @@ -0,0 +1,109 @@ +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 new file mode 100644 index 0000000..743491a --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/FocModelAnimationTypes.cs @@ -0,0 +1,124 @@ +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 new file mode 100644 index 0000000..867ba08 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/ModelAnimationType.cs @@ -0,0 +1,47 @@ +using System; + +namespace PG.StarWarsGame.Engine.Rendering.Animations; + +public readonly struct ModelAnimationType : IEquatable +{ + 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 diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs new file mode 100644 index 0000000..2a7aa3f --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Animations/SupportedModelAnimationTypes.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace PG.StarWarsGame.Engine.Rendering.Animations; + +public static class SupportedModelAnimationTypes +{ + public static IReadOnlyDictionary GetAnimationTypesForEngine(GameEngineType engineType) + { + return engineType switch + { + GameEngineType.Eaw => EawSupportedAnimations, + GameEngineType.Foc => FocSupportedAnimations, + _ => 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"} + }; +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs index 2b67ed4..3eab2ad 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/IPGRender.cs @@ -1,14 +1,32 @@ -using System; -using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.Binary; +using System; +using PG.StarWarsGame.Engine.Rendering.Animations; namespace PG.StarWarsGame.Engine.Rendering; public interface IPGRender { - IAloFile? Load3DAsset(string path, bool metadataOnly = true); + IAloFile? Load3DAsset( + string path, + bool metadataOnly = true, + bool throwsException = false); - IAloFile? Load3DAsset(ReadOnlySpan path, bool metadataOnly = true); + IAloFile? Load3DAsset( + ReadOnlySpan path, + bool metadataOnly = true, + bool throwsException = false); - ModelClass? LoadModelAndAnimations(ReadOnlySpan path, string? animOverrideName, bool metadataOnly = true); + ModelClass? LoadModelAndAnimations( + ReadOnlySpan path, + string? animOverrideName, + bool metadataOnly = true, + bool throwsException = false); + + public AnimationCollection LoadAnimations( + ReadOnlySpan fileName, + ReadOnlySpan dirPath, + bool metadataOnly = true, + Action? corruptedAnimationHandler = null); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs deleted file mode 100644 index d21c1da..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelAnimationType.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace PG.StarWarsGame.Engine.Rendering; - -public enum ModelAnimationType -{ - 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, - 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/ModelClass.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs index f46cd3b..18940ac 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/ModelClass.cs @@ -1,32 +1,34 @@ using System; using System.Diagnostics.CodeAnalysis; using AnakinRaW.CommonUtilities; -using PG.StarWarsGame.Files; +using PG.StarWarsGame.Engine.Rendering.Animations; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; namespace PG.StarWarsGame.Engine.Rendering; -public sealed class ModelClass : DisposableObject +public sealed class ModelClass( + IAloFile aloFile, + AnimationCollection animations) + : DisposableObject { - public IAloDataContent RenderableContent { get; } + public IAloDataContent RenderableContent { get; } = aloFile.Content; - public IPetroglyphFileHolder File { get; } + public IAloFile File { get; } = + aloFile ?? throw new ArgumentNullException(nameof(aloFile)); - public AlamoModel? Model { get; } + public AlamoModel? Model { get; } = aloFile.Content as AlamoModel; [MemberNotNullWhen(true, nameof(Model))] public bool IsModel => Model is not null; - public AnimationCollection Animations { get; } = new(); + public AnimationCollection Animations { get; } = animations ?? throw new ArgumentNullException(nameof(animations)); - public ModelClass(IAloFile aloFile) + public ModelClass(IAloFile aloFile) : this(aloFile, AnimationCollection.Empty) { - File = aloFile; - RenderableContent = aloFile.Content; - Model = aloFile.Content as AlamoModel; } + public int IndexOfBone(string boneName) { if (!IsModel) @@ -37,6 +39,7 @@ public int IndexOfBone(string boneName) if (bones[i].Equals(boneName, StringComparison.OrdinalIgnoreCase)) return i; } + return -1; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index e04870b..f7b2efe 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -1,25 +1,26 @@ using AnakinRaW.CommonUtilities.FileSystem; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.Commons.Hashing; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Utilities; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.ALO.Files.Animations; using PG.StarWarsGame.Files.ALO.Services; +using PG.StarWarsGame.Files.Binary; using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions; -using Microsoft.Extensions.Logging; -using PG.Commons.Hashing; -using PG.StarWarsGame.Files.ALO.Files.Animations; -using PG.StarWarsGame.Files.Binary; +using PG.StarWarsGame.Engine.Rendering.Animations; namespace PG.StarWarsGame.Engine.Rendering; -internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) - : IPGRender +internal class PGRender( + GameRepository gameRepository, + GameErrorReporterWrapper errorReporter, + IServiceProvider serviceProvider) : IPGRender { private readonly IAloFileService _aloFileService = serviceProvider.GetRequiredService(); private readonly IRepository _modelRepository = gameRepository.ModelRepository; @@ -27,136 +28,18 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper private readonly ICrc32HashingService _hashingService = serviceProvider.GetRequiredService(); private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(PGRender)); - [SuppressMessage("ReSharper", "StringLiteralTypo")] - private static readonly Dictionary AnimationTypeToName = new() + public IAloFile? Load3DAsset( + string path, + bool metadataOnly = true, + bool throwsException = false) { - { 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"}, - { 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"}, - }; - - public IAloFile? Load3DAsset(string path, bool metadataOnly = true) - { - return Load3DAsset(path.AsSpan(), metadataOnly); + return Load3DAsset(path.AsSpan(), metadataOnly, throwsException); } - public IAloFile? Load3DAsset(ReadOnlySpan path, bool metadataOnly = true) + public IAloFile? Load3DAsset( + ReadOnlySpan path, + bool metadataOnly = true, + bool throwsException = false) { if (path.IsEmpty) errorReporter.Assert(EngineAssert.FromNullOrEmpty(null, "Model path is null or empty.")); @@ -173,6 +56,9 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper } catch (BinaryCorruptedException e) { + if (throwsException) + throw; + var pathString = path.ToString(); var errorMessage = $"Unable to load 3D asset '{pathString}': {e.Message}"; _logger?.LogWarning(e, errorMessage); @@ -181,68 +67,101 @@ internal class PGRender(GameRepository gameRepository, GameErrorReporterWrapper } } - public ModelClass? LoadModelAndAnimations(ReadOnlySpan path, string? animOverrideName, bool metadataOnly = true) + public ModelClass? LoadModelAndAnimations( + ReadOnlySpan path, + string? animOverrideName, + bool metadataOnly = true, + bool throwsException = false) { - var aloFile = Load3DAsset(path, metadataOnly); + var aloFile = Load3DAsset(path, metadataOnly, throwsException); if (aloFile is null) return null; - var modelClass = new ModelClass(aloFile); - - if (!modelClass.IsModel) - return modelClass; + if (!aloFile.FileInformation.IsModel) + return new ModelClass(aloFile); - var dir = _fileSystem.Path.GetDirectoryName(path); + var dirPath = _fileSystem.Path.GetDirectoryName(path); var fileName = _fileSystem.Path.GetFileNameWithoutExtension(path); if (!string.IsNullOrEmpty(animOverrideName)) fileName = _fileSystem.Path.GetFileNameWithoutExtension(animOverrideName.AsSpan()); + var animations = LoadAnimations(fileName, dirPath, metadataOnly, throwsException ? AnimationCorruptedHandler : null); + + return new ModelClass(aloFile, animations); + } + + private void AnimationCorruptedHandler(BinaryCorruptedException e) + { + throw e; + } + + public AnimationCollection LoadAnimations( + ReadOnlySpan fileName, + ReadOnlySpan dirPath, + bool metadataOnly = true, + Action? corruptedAnimationHandler = null) + { + fileName = _fileSystem.Path.GetFileNameWithoutExtension(fileName); + + var animations = new AnimationCollection(); + Span stringBuffer = stackalloc char[256]; - - foreach (ModelAnimationType animType in Enum.GetValues(typeof(ModelAnimationType))) + + foreach (var animationData in SupportedModelAnimationTypes.GetAnimationTypesForEngine(gameRepository.EngineType)) { var subIndex = 0; var loadingNumberedAnimations = true; - var animName = AnimationTypeToName[animType]; + var throwsOnLoad = corruptedAnimationHandler is not null; while (loadingNumberedAnimations) { var stringBuilder = new ValueStringBuilder(stringBuffer); - - CreateAnimationFilePath(ref stringBuilder, fileName, animName, subIndex); + + CreateAnimationFilePath(ref stringBuilder, fileName, animationData.Value, subIndex); var animationFilenameWithoutExtension = _fileSystem.Path.GetFileNameWithoutExtension(stringBuilder.AsSpan()); - InsertPath(ref stringBuilder, dir); + InsertPath(ref stringBuilder, dirPath); if (stringBuilder.Length > PGConstants.MaxAnimationFileName) { var animFile = stringBuilder.AsSpan().ToString(); - var model = path.ToString(); errorReporter.Assert( - EngineAssert.Create(EngineAssertKind.ValueOutOfRange, animFile, model, - $"Cannot get animation file '{animFile}' for model '{model}', because animation file path is too long.")); - return null; + EngineAssert.Create(EngineAssertKind.ValueOutOfRange, animFile, null, + $"Cannot get animation file '{animFile}' , because animation file path is too long.")); + continue; } - var animationAsset = Load3DAsset(stringBuilder.AsSpan(), metadataOnly); - if (animationAsset is IAloAnimationFile animationFile) + + try { - loadingNumberedAnimations = true; - var crc = _hashingService.GetCrc32(animationFilenameWithoutExtension, PGConstants.DefaultPGEncoding); - modelClass.Animations.AddAnimation(animType, animationFile, crc); + var animationAsset = Load3DAsset(stringBuilder.AsSpan(), metadataOnly, throwsOnLoad); + if (animationAsset is IAloAnimationFile animationFile) + { + loadingNumberedAnimations = true; + var crc = _hashingService.GetCrc32(animationFilenameWithoutExtension, + PGConstants.DefaultPGEncoding); + animations.AddAnimation(animationData.Key, animationFile, crc); + } + else + { + loadingNumberedAnimations = false; + } } - else + catch (BinaryCorruptedException e) { + corruptedAnimationHandler?.Invoke(e); loadingNumberedAnimations = false; } - - stringBuilder.Dispose(); - subIndex++; + finally + { + stringBuilder.Dispose(); + subIndex++; + } } } - return modelClass; + return animations; } private void InsertPath(ref ValueStringBuilder stringBuilder, ReadOnlySpan directory) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs index 50aabc0..20672e5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.ALO/Services/AloLoadOptions.cs @@ -21,7 +21,7 @@ public enum AloLoadOptions /// Bones = 2, /// - /// Extracts only metadata from the model/particle + /// Extracts only metadata from the model/particle/animation. /// MetadataOnly = Assets | Bones } \ No newline at end of file From 27a92836126fb7a6b190834a2d8ec2ec2ce3fae5 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 29 Mar 2025 11:04:20 +0100 Subject: [PATCH 25/34] reorganize alot of code --- .../GameVerifierFactory.cs} | 10 +- src/ModVerify.CliApp/IGameVerifierFactory.cs | 15 ++ .../ModSelectors/ConsoleModSelector.cs | 21 ++- src/ModVerify.CliApp/ModVerifyApp.cs | 88 +++++++--- src/ModVerify.CliApp/ModVerifyPipeline.cs | 23 --- .../BaseModVerifyOptions.cs} | 36 +---- .../CommandLine/CreateBaselineVerbOption.cs | 10 ++ .../Options/CommandLine/VerifyVerbOption.cs | 26 +++ .../Options/ModVerifyAppSettings.cs | 16 +- .../Options/VerifyPipelineSettings.cs | 14 ++ .../Pipeline/GameVerifierPipelineStep.cs | 25 +++ .../Pipeline/VerifyGamePipeline.cs | 124 ++++++++++++++ src/ModVerify.CliApp/Program.cs | 98 ++++++----- .../Properties/launchSettings.json | 8 +- src/ModVerify.CliApp/SettingsBuilder.cs | 152 ++++++++++++------ .../VerifyGameInstallationData.cs | 2 +- src/ModVerify.CliApp/VerifyGamePipeline.cs | 122 -------------- src/ModVerify/GameVerificationException.cs | 40 +++-- src/ModVerify/IGameVerifier.cs | 18 --- src/ModVerify/IVerificationProvider.cs | 11 -- src/ModVerify/ModVerifyServiceContribution.cs | 11 -- .../IncompatibleBaselineException.cs | 8 + .../Reporting/Json/JsonSuppressionFilter.cs | 7 +- .../Json/JsonVerificationBaseline.cs | 16 +- .../Reporting/Json/JsonVerificationError.cs | 24 ++- .../Reporting/Reporters/ConsoleReporter.cs | 27 +++- .../Engine/EngineErrorReporterBase.cs | 13 +- .../Engine/GameAssertErrorReporter.cs | 16 +- .../Engine/InitializationErrorReporter.cs | 2 +- .../Reporters/Engine/XmlParseErrorReporter.cs | 15 +- .../Reporting/Reporters/ReporterBase.cs | 2 +- .../VerificationReportersExtensions.cs | 20 ++- .../Settings/FileBasedReporterSettings.cs | 2 +- ...tings.cs => GlobalVerifyReportSettings.cs} | 4 +- ...ortSettings.cs => VerifyReportSettings.cs} | 2 +- src/ModVerify/Reporting/SuppressionFilter.cs | 48 ++---- src/ModVerify/Reporting/SuppressionList.cs | 8 +- .../Reporting/VerificationBaseline.cs | 40 ++++- src/ModVerify/Reporting/VerificationError.cs | 56 ++++--- ...rificationErrorContextEqualityComparer.cs} | 6 +- .../Reporting/VerificationReportBroker.cs | 31 +--- src/ModVerify/Settings/GameVerifySettings.cs | 16 +- .../Settings/VerificationAbortSettings.cs | 12 -- .../Utilities/VerificationErrorExtensions.cs | 28 ++++ .../Verifiers/AlreadyVerifiedCache.cs | 20 +++ src/ModVerify/Verifiers/AudioFilesVerifier.cs | 2 +- .../CommandBar/CommandBarVerifier.Base.cs | 8 +- .../Commons/ReferencedTexturesVerifier.cs | 2 +- ...r.cs => SharedReferencedModelsVerifier.cs} | 105 +++--------- .../Verifiers/DuplicateNameFinder.cs | 23 +-- .../Verifiers/GameEngineErrorCollector.cs | 4 +- src/ModVerify/Verifiers/GameVerifier.cs | 26 +++ src/ModVerify/Verifiers/GameVerifierBase.cs | 52 +++--- src/ModVerify/Verifiers/IGameVerifierInfo.cs | 10 ++ .../Verifiers/ReferencedModelsVerifier.cs | 41 +++++ .../ReferencedTexturesVerifier.GUI.cs | 2 +- .../CommandBar/CommandBarGameManager.cs | 25 +-- .../Components/CommandBarBaseComponent.cs | 2 +- .../ErrorReporting/EngineAssert.cs | 29 ++-- .../GuiDialogGameManager_Initialization.cs | 2 +- .../Rendering/Font/FontManager.cs | 4 +- .../Rendering/Font/WindowsFontManager.cs | 7 +- .../Rendering/PGRender.cs | 6 +- .../XmlLocationInfo.cs | 2 +- 64 files changed, 963 insertions(+), 682 deletions(-) rename src/{ModVerify/VerificationProvider.cs => ModVerify.CliApp/GameVerifierFactory.cs} (68%) create mode 100644 src/ModVerify.CliApp/IGameVerifierFactory.cs delete mode 100644 src/ModVerify.CliApp/ModVerifyPipeline.cs rename src/ModVerify.CliApp/Options/{ModVerifyOptions.cs => CommandLine/BaseModVerifyOptions.cs} (63%) create mode 100644 src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs create mode 100644 src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs create mode 100644 src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs create mode 100644 src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs create mode 100644 src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs delete mode 100644 src/ModVerify.CliApp/VerifyGamePipeline.cs delete mode 100644 src/ModVerify/IGameVerifier.cs delete mode 100644 src/ModVerify/IVerificationProvider.cs delete mode 100644 src/ModVerify/ModVerifyServiceContribution.cs create mode 100644 src/ModVerify/Reporting/IncompatibleBaselineException.cs rename src/ModVerify/Reporting/Settings/{GlobalVerificationReportSettings.cs => GlobalVerifyReportSettings.cs} (62%) rename src/ModVerify/Reporting/Settings/{VerificationReportSettings.cs => VerifyReportSettings.cs} (79%) rename src/ModVerify/Reporting/{AssetsEqualityComparer.cs => VerificationErrorContextEqualityComparer.cs} (63%) delete mode 100644 src/ModVerify/Settings/VerificationAbortSettings.cs create mode 100644 src/ModVerify/Utilities/VerificationErrorExtensions.cs create mode 100644 src/ModVerify/Verifiers/AlreadyVerifiedCache.cs rename src/ModVerify/Verifiers/Commons/{ReferencedModelsVerifier.cs => SharedReferencedModelsVerifier.cs} (78%) create mode 100644 src/ModVerify/Verifiers/GameVerifier.cs create mode 100644 src/ModVerify/Verifiers/IGameVerifierInfo.cs create mode 100644 src/ModVerify/Verifiers/ReferencedModelsVerifier.cs diff --git a/src/ModVerify/VerificationProvider.cs b/src/ModVerify.CliApp/GameVerifierFactory.cs similarity index 68% rename from src/ModVerify/VerificationProvider.cs rename to src/ModVerify.CliApp/GameVerifierFactory.cs index 9d14187..8ff1bcb 100644 --- a/src/ModVerify/VerificationProvider.cs +++ b/src/ModVerify.CliApp/GameVerifierFactory.cs @@ -2,14 +2,16 @@ using System.Collections.Generic; using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; -using AET.ModVerify.Verifiers.Commons; using PG.StarWarsGame.Engine.Database; -namespace AET.ModVerify; +namespace AET.ModVerifyTool; -internal class VerificationProvider(IServiceProvider serviceProvider) : IVerificationProvider +internal class GameVerifierFactory : IGameVerifierFactory { - public IEnumerable GetAllDefaultVerifiers(IGameDatabase database, GameVerifySettings settings) + public IEnumerable GetVerifiers( + IGameDatabase database, + GameVerifySettings settings, + IServiceProvider serviceProvider) { yield return new ReferencedModelsVerifier(database, settings, serviceProvider); yield return new DuplicateNameFinder(database, settings, serviceProvider); diff --git a/src/ModVerify.CliApp/IGameVerifierFactory.cs b/src/ModVerify.CliApp/IGameVerifierFactory.cs new file mode 100644 index 0000000..36cb424 --- /dev/null +++ b/src/ModVerify.CliApp/IGameVerifierFactory.cs @@ -0,0 +1,15 @@ +using AET.ModVerify.Settings; +using AET.ModVerify.Verifiers; +using PG.StarWarsGame.Engine.Database; +using System; +using System.Collections.Generic; + +namespace AET.ModVerifyTool; + +public interface IGameVerifierFactory +{ + IEnumerable GetVerifiers( + IGameDatabase database, + GameVerifySettings settings, + IServiceProvider serviceProvider); +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index 670beee..fcc2838 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -93,15 +93,22 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin Console.WriteLine(); Console.WriteLine("================="); - while (true) + try { - Console.Write("Select a game or mod: "); - var numberString = Console.ReadLine(); + while (true) + { + Console.Write("Select a game or mod: "); + var numberString = Console.ReadLine(); - if (!int.TryParse(numberString, out var number)) - continue; - if (number < list.Count) - return list[number]; + if (!int.TryParse(numberString, out var number)) + continue; + if (number < list.Count) + return list[number]; + } + } + finally + { + Console.WriteLine(); } } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApp.cs index ec49d86..2093da0 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApp.cs @@ -1,14 +1,16 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Abstractions; -using System.Threading.Tasks; -using AET.ModVerify; +using AET.ModVerify; using AET.ModVerify.Reporting; using AET.ModVerifyTool.ModSelectors; using AET.ModVerifyTool.Options; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using System.Threading.Tasks; +using AET.ModVerifyTool.Pipeline; namespace AET.ModVerifyTool; @@ -19,53 +21,87 @@ internal class ModVerifyApp(ModVerifyAppSettings settings, IServiceProvider serv public async Task RunApplication() { - var returnCode = 0; - var installData = new SettingsBasedModSelector(services) .CreateInstallationDataFromSettings(settings.GameInstallationsSettings); - Console.WriteLine(); _logger?.LogDebug($"Verify install data: {installData}"); + _logger?.LogTrace($"Verify settings: {settings}"); + + var allErrors = await Verify(installData).ConfigureAwait(false); + + try + { + await ReportErrors(allErrors).ConfigureAwait(false); + } + catch (GameVerificationException e) + { + return e.HResult; + } + + if (!settings.CreateNewBaseline) + return 0; + + await WriteBaseline(allErrors, settings.NewBaselinePath).ConfigureAwait(false); + _logger?.LogInformation("Baseline successfully created."); + + return 0; + } - var verifyPipeline = new ModVerifyPipeline(installData.EngineType, installData.GameLocations, settings.GameVerifySettings, services); + private async Task> Verify(VerifyGameInstallationData installData) + { + var verifyPipeline = new VerifyGamePipeline( + installData.EngineType, + installData.GameLocations, + settings.VerifyPipelineSettings, + settings.GlobalReportSettings, + services); try { _logger?.LogInformation($"Verifying '{installData.Name}'..."); await verifyPipeline.RunAsync().ConfigureAwait(false); } - catch (GameVerificationException e) + catch (OperationCanceledException) + { + _logger?.LogWarning("Verification stopped due to enabled failFast setting."); + } + catch (Exception e) { - _logger?.LogError($"There is at least one verification error with severity " + - $"'{settings.GameVerifySettings.GlobalReportSettings.MinimumReportSeverity}' or greater."); - returnCode = e.HResult; + _logger?.LogError(e, $"Verification failed: {e.Message}"); + throw; } finally { - var message = $"Verification finished with code: {returnCode}"; - if (returnCode == 0) - _logger?.LogInformation(message); - else - _logger?.LogError(message); + _logger?.LogInformation("Finished verification"); } - if (settings.CreateNewBaseline) - await WriteBaseline(verifyPipeline.Errors, settings.NewBaselinePath).ConfigureAwait(false); + return verifyPipeline.FilteredErrors; + } + + private async Task ReportErrors(IReadOnlyCollection errors) + { + _logger?.LogInformation("Reporting Errors..."); + + var reportBroker = new VerificationReportBroker(services); + + await reportBroker.ReportAsync(errors); - return returnCode; + if (errors.Any(x => x.Severity >= settings.AppThrowsOnMinimumSeverity)) + throw new GameVerificationException(errors); } + private async Task WriteBaseline(IEnumerable errors, string baselineFile) { - var currentBaseline = settings.GameVerifySettings.GlobalReportSettings.Baseline; - - var newBaseline = currentBaseline.MergeWith(errors); + var baseline = new VerificationBaseline(settings.GlobalReportSettings.MinimumReportSeverity, errors); var fullPath = _fileSystem.Path.GetFullPath(baselineFile); + _logger?.LogInformation($"Writing Baseline to '{fullPath}'"); + #if NET await #endif using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); - await newBaseline.ToJsonAsync(fs); + await baseline.ToJsonAsync(fs); } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModVerifyPipeline.cs b/src/ModVerify.CliApp/ModVerifyPipeline.cs deleted file mode 100644 index 1fca580..0000000 --- a/src/ModVerify.CliApp/ModVerifyPipeline.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using AET.ModVerify; -using AET.ModVerify.Settings; -using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Database; - -namespace AET.ModVerifyTool; - -internal class ModVerifyPipeline( - GameEngineType targetType, - GameLocations gameLocations, - GameVerifySettings settings, - IServiceProvider serviceProvider) - : VerifyGamePipeline(targetType, gameLocations, settings, serviceProvider) -{ - protected override IEnumerable CreateVerificationSteps(IGameDatabase database) - { - var verifyProvider = ServiceProvider.GetRequiredService(); - return verifyProvider.GetAllDefaultVerifiers(database, Settings); - } -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/ModVerifyOptions.cs b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs similarity index 63% rename from src/ModVerify.CliApp/Options/ModVerifyOptions.cs rename to src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs index c058a57..ca1f358 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs @@ -3,48 +3,30 @@ using CommandLine; using PG.StarWarsGame.Engine; -namespace AET.ModVerifyTool.Options; +namespace AET.ModVerifyTool.Options.CommandLine; -internal class ModVerifyOptions +internal abstract class BaseModVerifyOptions { - [Option('o', "output", Required = false, HelpText = "directory where result files shall be stored to.")] - public string? Output { get; set; } - - [Option('v', "verbose", Required = false, HelpText = "Set output to verbose messages.")] + [Option('v', "verbose", Required = false, HelpText = "Sets output to verbose messages.")] public bool Verbose { get; set; } - [Option("baseline", Required = false, HelpText = "Path to a JSON baseline file.")] - public string? Baseline { get; set; } + [Option("minSeverity", Required = false, Default = VerificationSeverity.Information, + HelpText = "When set, only findings with at least the specified severity value are processed.")] + public VerificationSeverity MinimumSeverity { get; set; } [Option("suppressions", Required = false, HelpText = "Path to a JSON suppression file.")] public string? Suppressions { get; set; } - [Option("minFailSeverity", Required = false, Default = null, - HelpText = "When set, the application return with an error, if any finding has at least the specified severity value.")] - public VerificationSeverity? MinimumFailureSeverity { get; set; } - - [Option("failFast", Required = false, Default = false, - HelpText = "When set, the application will abort on the first failure. The option also recognized the 'MinimumFailureSeverity' setting.")] - public bool FailFast { get; set; } - - [Option("createBaseline", Required = false, - HelpText = "When a path is specified, the tools creates a new baseline file. " + - "An existing file will be overwritten. Previous baselines are merged into the new baseline.")] - public string? NewBaselineFile { get; set; } - - [Option("path", SetName = "autoDetection", Required = false, Default = null, HelpText = "Specifies the path to verify. The path may be a game or mod. The application will try to find all necessary submods or base games itself. " + "The argument cannot be combined with any of --mods, --game or --fallbackGame")] public string? AutoPath { get; set; } - [Option("mods", SetName = "manualPaths", Required = false, Default = null, Separator = ';', HelpText = "The path of the mod to verify. To support submods, multiple paths can be separated using the ';' (semicolon) character. " + "Leave empty, if you want to verify a game. If you want to use the interactive mode, leave this, --game and --fallbackGame empty.")] public IList? ModPaths { get; set; } - [Option("game", SetName = "manualPaths", Required = false, Default = null, HelpText = "The path of the base game. For FoC mods this points to the FoC installation, for EaW mods this points to the EaW installation. " + "Leave empty, if you want to auto-detect games. If you want to use the interactive mode, leave this, --mods and --fallbackGame empty. " + @@ -57,7 +39,7 @@ internal class ModVerifyOptions [Option("type", Required = false, Default = null, - HelpText = "The game type of the mod that shall be verified. Leave this value to auto-determine the type. Valid values are 'Eaw' and 'Foc'. " + + HelpText = "The game type of the mod that shall be verified. Skip this value to auto-determine the type. Valid values are 'Eaw' and 'Foc'. " + "This argument is required, if the first mod of '--mods' points to a directory outside of the common folder hierarchy (e.g, /MODS/MOD_NAME or /32470/WORKSHOP_ID")] public GameEngineType? GameType { get; set; } @@ -66,8 +48,4 @@ internal class ModVerifyOptions HelpText = "Additional fallback paths, which may contain assets that shall be included when doing the verification. Do not add EaW here. " + "Multiple paths can be separated using the ';' (semicolon) character.")] public IList? AdditionalFallbackPath { get; set; } - - [Option("ignoreAsserts", Required = false, - HelpText = "When this flag is present, the application will not report engine assertions.")] - public bool IgnoreAsserts { get; set; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs b/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs new file mode 100644 index 0000000..78132cc --- /dev/null +++ b/src/ModVerify.CliApp/Options/CommandLine/CreateBaselineVerbOption.cs @@ -0,0 +1,10 @@ +using CommandLine; + +namespace AET.ModVerifyTool.Options.CommandLine; + +[Verb("createBaseline", HelpText = "Verifies the specified game and creates a new baseline file at the specified location.")] +internal class CreateBaselineVerbOption : BaseModVerifyOptions +{ + [Option('o', "outFile", Required = true, HelpText = "The file path of the new baseline file.")] + public string OutputFile { get; set; } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs b/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs new file mode 100644 index 0000000..517ceda --- /dev/null +++ b/src/ModVerify.CliApp/Options/CommandLine/VerifyVerbOption.cs @@ -0,0 +1,26 @@ +using AET.ModVerify.Reporting; +using CommandLine; + +namespace AET.ModVerifyTool.Options.CommandLine; + +[Verb("verify", true, HelpText = "Verifies the specified game and reports the findings.")] +internal class VerifyVerbOption : BaseModVerifyOptions +{ + [Option('o', "outDir", Required = false, HelpText = "Directory where result files shall be stored to.")] + public string? OutputDirectory { get; set; } + + [Option("failFast", Required = false, Default = false, + HelpText = "When set, the application will abort on the first failure. The option also recognized the 'MinimumFailureSeverity' setting.")] + public bool FailFast { get; set; } + + [Option("minFailSeverity", Required = false, Default = null, + HelpText = "When set, the application return with an error, if any finding has at least the specified severity value.")] + public VerificationSeverity? MinimumFailureSeverity { get; set; } + + [Option("ignoreAsserts", Required = false, + HelpText = "When this flag is present, the application will not report engine assertions.")] + public bool IgnoreAsserts { get; set; } + + [Option("baseline", Required = false, HelpText = "Path to a JSON baseline file.")] + public string? Baseline { get; set; } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs index 6c99bf4..ff9c4d6 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs @@ -1,16 +1,20 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using AET.ModVerify.Settings; +using System.Diagnostics.CodeAnalysis; +using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; namespace AET.ModVerifyTool.Options; -internal record ModVerifyAppSettings +internal sealed class ModVerifyAppSettings { - public required GameVerifySettings GameVerifySettings { get; init; } + public required VerifyPipelineSettings VerifyPipelineSettings { get; init; } + + public required GlobalVerifyReportSettings GlobalReportSettings { get; init; } public required GameInstallationsSettings GameInstallationsSettings { get; init; } - public string Output { get; init; } = Environment.CurrentDirectory; + public VerificationSeverity? AppThrowsOnMinimumSeverity { get; init; } + + public string? ReportOutput { get; init; } [MemberNotNullWhen(true, nameof(NewBaselinePath))] public bool CreateNewBaseline => !string.IsNullOrEmpty(NewBaselinePath); diff --git a/src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs b/src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs new file mode 100644 index 0000000..1f09876 --- /dev/null +++ b/src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs @@ -0,0 +1,14 @@ +using AET.ModVerify.Settings; + +namespace AET.ModVerifyTool.Options; + +internal sealed class VerifyPipelineSettings +{ + public required GameVerifySettings GameVerifySettings { get; init; } + + public required IGameVerifierFactory VerifierFactory { get; init; } + + public bool FailFast { get; init; } + + public int ParallelVerifiers { get; init; } = 4; +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs new file mode 100644 index 0000000..232fb62 --- /dev/null +++ b/src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; +using AET.ModVerify.Verifiers; +using AnakinRaW.CommonUtilities.SimplePipeline.Steps; +using Microsoft.Extensions.Logging; + +namespace AET.ModVerifyTool.Pipeline; + +internal sealed class GameVerifierPipelineStep(GameVerifier verifier, IServiceProvider serviceProvider) : PipelineStep(serviceProvider) +{ + private readonly GameVerifier _gameVerifier = verifier ?? throw new ArgumentNullException(nameof(verifier)); + + protected override void RunCore(CancellationToken token) + { + Logger?.LogDebug($"Running verifier '{_gameVerifier.FriendlyName}'..."); + try + { + _gameVerifier.Verify(token); + } + finally + { + Logger?.LogDebug($"Finished verifier '{_gameVerifier.FriendlyName}'"); + } + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs b/src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs new file mode 100644 index 0000000..5358734 --- /dev/null +++ b/src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AET.ModVerify; +using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Settings; +using AET.ModVerify.Utilities; +using AET.ModVerify.Verifiers; +using AET.ModVerifyTool.Options; +using AnakinRaW.CommonUtilities.SimplePipeline; +using AnakinRaW.CommonUtilities.SimplePipeline.Runners; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Engine.Database; + +namespace AET.ModVerifyTool.Pipeline; + +internal sealed class VerifyGamePipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline +{ + private readonly List _verificationSteps = new(); + private readonly GameEngineType _targetType; + private readonly GameLocations _gameLocations; + private readonly ParallelStepRunner _verifyRunner; + + private readonly VerifyPipelineSettings _pipelineSettings; + private readonly GlobalVerifyReportSettings _reportSettings; + + protected override bool FailFast { get; } + + public IReadOnlyCollection FilteredErrors { get; private set; } = []; + + public VerifyGamePipeline( + GameEngineType targetType, + GameLocations gameLocations, + VerifyPipelineSettings pipelineSettings, + GlobalVerifyReportSettings reportSettings, + IServiceProvider serviceProvider) : base(serviceProvider) + { + _targetType = targetType; + _gameLocations = gameLocations ?? throw new ArgumentNullException(nameof(gameLocations)); + _pipelineSettings = pipelineSettings ?? throw new ArgumentNullException(nameof(pipelineSettings)); + _reportSettings = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings)); + + if (pipelineSettings.ParallelVerifiers is < 0 or > 64) + throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", nameof(pipelineSettings)); + + _verifyRunner = new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider); + + FailFast = pipelineSettings.FailFast; + } + + protected override Task PrepareCoreAsync() + { + _verificationSteps.Clear(); + return Task.FromResult(true); + } + + protected override async Task RunCoreAsync(CancellationToken token) + { + var databaseService = ServiceProvider.GetRequiredService(); + + var initializationErrorListener = new ConcurrentGameGameErrorReporter(); + var initOptions = new GameInitializationOptions + { + Locations = _gameLocations, + TargetEngineType = _targetType, + GameErrorReporter = initializationErrorListener + + }; + var database = await databaseService.InitializeGameAsync(initOptions, token); + + AddStep(new GameEngineErrorCollector(initializationErrorListener, database, _pipelineSettings.GameVerifySettings, ServiceProvider)); + + foreach (var gameVerificationStep in CreateVerificationSteps(database)) + AddStep(gameVerificationStep); + + try + { + Logger?.LogInformation("Running game verifiers..."); + _verifyRunner.Error += OnError; + await _verifyRunner.RunAsync(token); + } + finally + { + _verifyRunner.Error -= OnError; + Logger?.LogDebug("Game verifiers finished."); + } + + FilteredErrors = GetReportableErrors(_verificationSteps.SelectMany(s => s.VerifyErrors)).ToList(); + } + + protected override void OnError(object sender, StepRunnerErrorEventArgs e) + { + if (FailFast && e.Exception is GameVerificationException v) + { + if (v.Errors.All(error => _reportSettings.Baseline.Contains(error) || _reportSettings.Suppressions.Suppresses(error))) + return; + } + base.OnError(sender, e); + } + + private IEnumerable CreateVerificationSteps(IGameDatabase database) + { + return _pipelineSettings.VerifierFactory.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); + } + + private void AddStep(GameVerifier verifier) + { + _verifyRunner.AddStep(new GameVerifierPipelineStep(verifier, ServiceProvider)); + _verificationSteps.Add(verifier); + } + + private IEnumerable GetReportableErrors(IEnumerable errors) + { + Logger?.LogDebug("Applying baseline and suppressions."); + // NB: We don't filter for severity here, as the individual reporters handle that. + // This allows better control over what gets reported. + return errors.ApplyBaseline(_reportSettings.Baseline) + .ApplySuppressions(_reportSettings.Suppressions); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 2488fcb..3ee4778 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -1,8 +1,4 @@ -using System; -using System.IO.Abstractions; -using System.Threading.Tasks; -using AET.ModVerify; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Reporters; using AET.ModVerify.Reporting.Reporters.JSON; using AET.ModVerify.Reporting.Reporters.Text; @@ -13,6 +9,7 @@ using AnakinRaW.CommonUtilities.Registry; using AnakinRaW.CommonUtilities.Registry.Windows; using CommandLine; +using CommandLine.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.Commons; @@ -28,8 +25,14 @@ using Serilog.Events; using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; +using System; +using System.IO.Abstractions; +using System.Threading.Tasks; +using AET.ModVerifyTool.Options.CommandLine; +using AET.ModVerifyTool.Pipeline; using Testably.Abstractions; using ILogger = Serilog.ILogger; +using VerifyVerbOption = AET.ModVerifyTool.Options.CommandLine.VerifyVerbOption; namespace AET.ModVerifyTool; @@ -38,6 +41,7 @@ internal class Program private const string EngineParserNamespace = "PG.StarWarsGame.Engine.Xml.Parsers"; private const string ParserNamespace = "PG.StarWarsGame.Files.XML.Parsers"; private const string GameInfrastructureNamespace = "PG.StarWarsGame.Infrastructure"; + private static readonly string GameVerifierStepNamespace = typeof(GameVerifierPipelineStep).FullName!; private static async Task Main(string[] args) { @@ -45,29 +49,31 @@ private static async Task Main(string[] args) var result = 0; - var parseResult = Parser.Default.ParseArguments(args); + Type[] programVerbs = + [ + typeof(VerifyVerbOption), + typeof(CreateBaselineVerbOption), + ]; - ModVerifyOptions? verifyOptions = null!; - await parseResult.WithParsedAsync(o => - { - verifyOptions = o; - return Task.CompletedTask; - }).ConfigureAwait(false); + var parseResult = Parser.Default.ParseArguments(args, programVerbs); + await parseResult.WithParsedAsync(async o => + { + result = await Run((BaseModVerifyOptions)o); + }); await parseResult.WithNotParsedAsync(e => { + Console.WriteLine(HelpText.AutoBuild(parseResult).ToString()); result = 0xA0; return Task.CompletedTask; - }).ConfigureAwait(false); + }); - if (verifyOptions is null) - { - if (result != 0) - return result; - throw new InvalidOperationException("Mod verify was executed with the wrong arguments."); - } + return result; + } - var coreServiceCollection = CreateCoreServices(verifyOptions.Verbose); + private static async Task Run(BaseModVerifyOptions options) + { + var coreServiceCollection = CreateCoreServices(options.Verbose); var coreServices = coreServiceCollection.BuildServiceProvider(); var logger = coreServices.GetService()?.CreateLogger(typeof(Program)); @@ -75,18 +81,15 @@ await parseResult.WithNotParsedAsync(e => try { - var settings = new SettingsBuilder(coreServices) - .BuildSettings(verifyOptions); - + var settings = new SettingsBuilder(coreServices).BuildSettings(options); var services = CreateAppServices(coreServiceCollection, settings); - var verifier = new ModVerifyApp(settings, services); return await verifier.RunApplication().ConfigureAwait(false); } catch (Exception e) { - Console.WriteLine($"Error: {e.Message}"); + PrintApplicationFailure(); logger?.LogCritical(e, e.Message); return e.HResult; } @@ -99,7 +102,7 @@ await parseResult.WithNotParsedAsync(e => #endif } } - + private static IServiceCollection CreateCoreServices(bool verboseLogging) { var fileSystem = new RealFileSystem(); @@ -113,7 +116,6 @@ private static IServiceCollection CreateCoreServices(bool verboseLogging) return serviceCollection; } - private static IServiceProvider CreateAppServices(IServiceCollection serviceCollection, ModVerifyAppSettings settings) { serviceCollection.AddSingleton(sp => new HashingService(sp)); @@ -129,9 +131,7 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl PetroglyphEngineServiceContribution.ContributeServices(serviceCollection); - ModVerifyServiceContribution.ContributeServices(serviceCollection); - - SetupReporting(serviceCollection, settings); + SetupVerifyReporting(serviceCollection, settings); serviceCollection.AddSingleton(sp => new OnlineModNameResolver(sp)); serviceCollection.AddSingleton(sp => new OnlineModGameTypeResolver(sp)); @@ -139,23 +139,27 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl return serviceCollection.BuildServiceProvider(); } - private static void SetupReporting(IServiceCollection serviceCollection, ModVerifyAppSettings settings) + private static void SetupVerifyReporting(IServiceCollection serviceCollection, ModVerifyAppSettings settings) { - serviceCollection.RegisterConsoleReporter(new VerificationReportSettings + var printOnlySummary = settings.CreateNewBaseline; + serviceCollection.RegisterConsoleReporter(new VerifyReportSettings { MinimumReportSeverity = VerificationSeverity.Error - }); + }, printOnlySummary); + + if (string.IsNullOrEmpty(settings.ReportOutput)) + return; serviceCollection.RegisterJsonReporter(new JsonReporterSettings { - OutputDirectory = settings.Output, - MinimumReportSeverity = settings.GameVerifySettings.GlobalReportSettings.MinimumReportSeverity + OutputDirectory = settings.ReportOutput!, + MinimumReportSeverity = settings.GlobalReportSettings.MinimumReportSeverity }); serviceCollection.RegisterTextFileReporter(new TextFileReporterSettings { - OutputDirectory = settings.Output, - MinimumReportSeverity = settings.GameVerifySettings.GlobalReportSettings.MinimumReportSeverity + OutputDirectory = settings.ReportOutput!, + MinimumReportSeverity = settings.GlobalReportSettings.MinimumReportSeverity }); } @@ -181,7 +185,10 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem var cLogger = new LoggerConfiguration() .WriteTo.Async(c => { - c.Console(logLevel, theme: AnsiConsoleTheme.Code); + c.Console( + logLevel, + theme: AnsiConsoleTheme.Code, + outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}"); }) .Filter.ByExcluding(x => { @@ -195,6 +202,8 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem return true; if (source.StartsWith(GameInfrastructureNamespace.AsSpan())) return true; + if (source.StartsWith(GameVerifierStepNamespace.AsSpan())) + return true; return false; }) .CreateLogger(); @@ -235,4 +244,17 @@ private static void PrintHeader() Console.WriteLine(); Console.WriteLine(); } + + private static void PrintApplicationFailure() + { + Console.WriteLine(); + Console.WriteLine("**************"); + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine(" App Failure! "); + Console.ResetColor(); + Console.WriteLine("**************"); + Console.WriteLine(); + Console.WriteLine("The application encountered an unexpected error and will terminate now!"); + Console.WriteLine(); + } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index d84156a..f4d0f3a 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -1,8 +1,12 @@ { "profiles": { - "Interactive": { + "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "-o verifyResults --minFailSeverity Information --baseline c:/test/focBaseline.json" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --failFast --baseline focBaseline.json" + }, + "Interactive Baseline": { + "commandName": "Project", + "commandLineArgs": "createBaseline -o focBaseline.json" }, "FromModPath": { diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index c4babfd..e395449 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -1,83 +1,145 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Abstractions; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; using AET.ModVerifyTool.Options; using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using AET.ModVerifyTool.Options.CommandLine; +using Microsoft.Extensions.Logging; namespace AET.ModVerifyTool; -internal class SettingsBuilder(IServiceProvider services) +internal sealed class SettingsBuilder(IServiceProvider services) { private readonly IFileSystem _fileSystem = services.GetRequiredService(); + private readonly ILogger? _logger = + services.GetRequiredService()?.CreateLogger(typeof(SettingsBuilder)); - public ModVerifyAppSettings BuildSettings(ModVerifyOptions options) + public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) { - var output = Environment.CurrentDirectory; - if (!string.IsNullOrEmpty(options.Output)) - output = _fileSystem.Path.GetFullPath(_fileSystem.Path.Combine(Environment.CurrentDirectory, options.Output)); + switch (options) + { + case VerifyVerbOption verifyVerb: + return BuildFromVerifyVerb(verifyVerb); + case CreateBaselineVerbOption baselineVerb: + return BuildFromCreateBaselineVerb(baselineVerb); + } + throw new NotSupportedException($"The option '{options.GetType().Name}' is not supported!"); + } + + private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) + { + var output = Environment.CurrentDirectory; + var outDir = verifyOptions.OutputDirectory; + if (!string.IsNullOrEmpty(outDir)) + output = _fileSystem.Path.GetFullPath(_fileSystem.Path.Combine(Environment.CurrentDirectory, outDir!)); + return new ModVerifyAppSettings { - GameVerifySettings = BuildGameVerifySettings(options), - GameInstallationsSettings = BuildInstallationSettings(options), - Output = output, - NewBaselinePath = options.NewBaselineFile + VerifyPipelineSettings = new VerifyPipelineSettings + { + VerifierFactory = new GameVerifierFactory(), + FailFast = verifyOptions.FailFast, + GameVerifySettings = new GameVerifySettings + { + IgnoreAsserts = verifyOptions.IgnoreAsserts, + ThrowsOnMinimumSeverity = GetVerifierMinimumThrowSeverity() + } + }, + AppThrowsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity, + GameInstallationsSettings = BuildInstallationSettings(verifyOptions), + GlobalReportSettings = BuilderGlobalReportSettings(verifyOptions), + ReportOutput = output }; + + VerificationSeverity? GetVerifierMinimumThrowSeverity() + { + var minFailSeverity = verifyOptions.MinimumFailureSeverity; + if (verifyOptions.FailFast) + { + if (minFailSeverity == null) + { + _logger?.LogWarning($"Verification is configured to fail fast but 'minFailSeverity' is not specified. " + + $"Using severity '{VerificationSeverity.Information}'."); + minFailSeverity = VerificationSeverity.Information; + } + + return minFailSeverity; + } + + // Only in a failFast scenario we want the verifier to throw. + // In a normal run, the verifier should simply store the error. + return null; + } } - private GameVerifySettings BuildGameVerifySettings(ModVerifyOptions options) + private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOption baselineVerb) { - var settings = GameVerifySettings.Default; - - return settings with + return new ModVerifyAppSettings { - GlobalReportSettings = BuilderGlobalReportSettings(options), - AbortSettings = BuildAbortSettings(options) + VerifyPipelineSettings = new VerifyPipelineSettings + { + GameVerifySettings = new GameVerifySettings + { + IgnoreAsserts = false, + ThrowsOnMinimumSeverity = null, + }, + VerifierFactory = new GameVerifierFactory(), + FailFast = false, + }, + AppThrowsOnMinimumSeverity = null, + GameInstallationsSettings = BuildInstallationSettings(baselineVerb), + GlobalReportSettings = BuilderGlobalReportSettings(baselineVerb), + NewBaselinePath = baselineVerb.OutputFile, + ReportOutput = null, }; } - private VerificationAbortSettings BuildAbortSettings(ModVerifyOptions options) + private GlobalVerifyReportSettings BuilderGlobalReportSettings(BaseModVerifyOptions options) { - return new VerificationAbortSettings + return new GlobalVerifyReportSettings { - FailFast = options.FailFast, - MinimumAbortSeverity = options.MinimumFailureSeverity ?? VerificationSeverity.Information, - ThrowsGameVerificationException = options.MinimumFailureSeverity.HasValue || options.FailFast + Baseline = CreateBaseline(), + Suppressions = CreateSuppressions(), + MinimumReportSeverity = options.MinimumSeverity, }; - } - private GlobalVerificationReportSettings BuilderGlobalReportSettings(ModVerifyOptions options) - { - var baseline = VerificationBaseline.Empty; - var suppressions = SuppressionList.Empty; - - if (options.Baseline is not null) + VerificationBaseline CreateBaseline() { - using var fs = _fileSystem.FileStream.New(options.Baseline, FileMode.Open, FileAccess.Read); - baseline = VerificationBaseline.FromJson(fs); + // It does not make sense to create a baseline on another baseline. + if (options is not VerifyVerbOption verifyOptions || string.IsNullOrEmpty(verifyOptions.Baseline)) + return VerificationBaseline.Empty; + + using var fs = _fileSystem.FileStream.New(verifyOptions.Baseline!, FileMode.Open, FileAccess.Read); + + try + { + return VerificationBaseline.FromJson(fs); + } + catch (IncompatibleBaselineException) + { + Console.WriteLine($"The baseline '{verifyOptions.Baseline}' is not compatible with with version of ModVerify." + + $"{Environment.NewLine}Please generate a new baseline file or download the latest version." + + $"{Environment.NewLine}"); + throw; + } } - if (options.Suppressions is not null) + SuppressionList CreateSuppressions() { + if (options.Suppressions is null) + return SuppressionList.Empty; using var fs = _fileSystem.FileStream.New(options.Suppressions, FileMode.Open, FileAccess.Read); - suppressions = SuppressionList.FromJson(fs); + return SuppressionList.FromJson(fs); } - - return new GlobalVerificationReportSettings - { - Baseline = baseline, - Suppressions = suppressions, - MinimumReportSeverity = VerificationSeverity.Information, - ReportAsserts = !options.IgnoreAsserts - }; } - private GameInstallationsSettings BuildInstallationSettings(ModVerifyOptions options) + private GameInstallationsSettings BuildInstallationSettings(BaseModVerifyOptions options) { var modPaths = new List(); if (options.ModPaths is not null) diff --git a/src/ModVerify.CliApp/VerifyGameInstallationData.cs b/src/ModVerify.CliApp/VerifyGameInstallationData.cs index 69513ca..d873788 100644 --- a/src/ModVerify.CliApp/VerifyGameInstallationData.cs +++ b/src/ModVerify.CliApp/VerifyGameInstallationData.cs @@ -3,7 +3,7 @@ namespace AET.ModVerifyTool; -internal class VerifyGameInstallationData +internal sealed class VerifyGameInstallationData { public required string Name { get; init; } diff --git a/src/ModVerify.CliApp/VerifyGamePipeline.cs b/src/ModVerify.CliApp/VerifyGamePipeline.cs deleted file mode 100644 index 5d3ac3e..0000000 --- a/src/ModVerify.CliApp/VerifyGamePipeline.cs +++ /dev/null @@ -1,122 +0,0 @@ -using AET.ModVerify.Reporting; -using AET.ModVerify.Settings; -using AET.ModVerify.Verifiers; -using AnakinRaW.CommonUtilities.SimplePipeline; -using AnakinRaW.CommonUtilities.SimplePipeline.Runners; -using AnakinRaW.CommonUtilities.SimplePipeline.Steps; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Database; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AET.ModVerify; - -public sealed class GameVerifierPipelineStep(IGameVerifier verifier, IServiceProvider serviceProvider) : PipelineStep(serviceProvider) -{ - private readonly IGameVerifier _gameVerifier = verifier ?? throw new ArgumentNullException(nameof(verifier)); - - protected override void RunCore(CancellationToken token) - { - Logger?.LogDebug($"Running verifier '{_gameVerifier.FriendlyName}'..."); - try - { - _gameVerifier.Verify(token); - } - finally - { - Logger?.LogDebug($"Finished verifier '{_gameVerifier.FriendlyName}'"); - } - } -} - -public abstract class VerifyGamePipeline : Pipeline -{ - private readonly List _verificationSteps = new(); - private readonly GameEngineType _targetType; - private readonly GameLocations _gameLocations; - private readonly ParallelStepRunner _verifyRunner; - - protected GameVerifySettings Settings { get; } - - protected override bool FailFast { get; } - - public IReadOnlyCollection Errors { get; private set; } = Array.Empty(); - - protected VerifyGamePipeline(GameEngineType targetType, GameLocations gameLocations, GameVerifySettings settings, IServiceProvider serviceProvider) - : base(serviceProvider) - { - _targetType = targetType; - _gameLocations = gameLocations ?? throw new ArgumentNullException(nameof(gameLocations)); - Settings = settings ?? throw new ArgumentNullException(nameof(settings)); - - if (settings.ParallelVerifiers is < 0 or > 64) - throw new ArgumentException("Settings has invalid parallel worker number.", nameof(settings)); - - _verifyRunner = new ParallelStepRunner(settings.ParallelVerifiers, serviceProvider); - FailFast = settings.AbortSettings.FailFast; - } - - protected sealed override Task PrepareCoreAsync() - { - _verificationSteps.Clear(); - return Task.FromResult(true); - } - - protected sealed override async Task RunCoreAsync(CancellationToken token) - { - var databaseService = ServiceProvider.GetRequiredService(); - - var initializationErrorListener = new ConcurrentGameGameErrorReporter(); - var initOptions = new GameInitializationOptions - { - Locations = _gameLocations, - TargetEngineType = _targetType, - GameErrorReporter = initializationErrorListener - - }; - var database = await databaseService.InitializeGameAsync(initOptions, token); - - AddStep(new GameEngineErrorCollector(initializationErrorListener, database, Settings, ServiceProvider)); - - foreach (var gameVerificationStep in CreateVerificationSteps(database)) - AddStep(gameVerificationStep); - - try - { - Logger?.LogInformation("Verifying..."); - _verifyRunner.Error += OnError; - await _verifyRunner.RunAsync(token); - } - finally - { - _verifyRunner.Error -= OnError; - Logger?.LogInformation("Finished verification"); - } - - - Logger?.LogInformation("Reporting Errors"); - - var reportBroker = new VerificationReportBroker(Settings.GlobalReportSettings, ServiceProvider); - var errors = await reportBroker.ReportAsync(_verificationSteps); - - Errors = errors; - - if (Settings.AbortSettings.ThrowsGameVerificationException && - errors.Any(x => x.Severity >= Settings.AbortSettings.MinimumAbortSeverity)) - throw new GameVerificationException(errors); - - } - - protected abstract IEnumerable CreateVerificationSteps(IGameDatabase database); - - private void AddStep(IGameVerifier verifier) - { - _verifyRunner.AddStep(new GameVerifierPipelineStep(verifier, ServiceProvider)); - _verificationSteps.Add(verifier); - } -} \ No newline at end of file diff --git a/src/ModVerify/GameVerificationException.cs b/src/ModVerify/GameVerificationException.cs index 64415d9..1096179 100644 --- a/src/ModVerify/GameVerificationException.cs +++ b/src/ModVerify/GameVerificationException.cs @@ -5,29 +5,37 @@ namespace AET.ModVerify; -public sealed class GameVerificationException(IEnumerable errors) : Exception +public sealed class GameVerificationException : Exception { - private readonly string? _error = null; - private readonly IEnumerable _errors = errors ?? throw new ArgumentNullException(nameof(errors)); - - public GameVerificationException(VerificationError error) : this([error]) - { - } - - /// - public override string Message => Error; - - private string Error + private readonly string? _errorMessage = null; + + public IReadOnlyCollection Errors { get; } + + private string ErrorMessage { get { - if (_error != null) - return _error; + if (_errorMessage != null) + return _errorMessage; var stringBuilder = new StringBuilder(); - foreach (var error in _errors) - stringBuilder.AppendLine($"Verification error: {error.Id}:{error.Message};"); + foreach (var error in Errors) + stringBuilder.AppendLine($"Verification error: {error.Id}: {error.Message};"); return stringBuilder.ToString().TrimEnd(';'); } } + + /// + public override string Message => ErrorMessage; + + public GameVerificationException(VerificationError error) : this([error]) + { + } + + public GameVerificationException(IEnumerable errors) + { + if (errors is null) + throw new ArgumentNullException(nameof(errors)); + Errors = [..errors]; + } } \ No newline at end of file diff --git a/src/ModVerify/IGameVerifier.cs b/src/ModVerify/IGameVerifier.cs deleted file mode 100644 index 42ae278..0000000 --- a/src/ModVerify/IGameVerifier.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using AET.ModVerify.Reporting; - -namespace AET.ModVerify; - -public interface IGameVerifier -{ - IGameVerifier? Parent { get; } - - string Name { get; } - - string FriendlyName { get; } - - IReadOnlyCollection VerifyErrors { get; } - - void Verify(CancellationToken token); -} \ No newline at end of file diff --git a/src/ModVerify/IVerificationProvider.cs b/src/ModVerify/IVerificationProvider.cs deleted file mode 100644 index 2d87ab5..0000000 --- a/src/ModVerify/IVerificationProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using AET.ModVerify.Settings; -using AET.ModVerify.Verifiers; -using PG.StarWarsGame.Engine.Database; - -namespace AET.ModVerify; - -public interface IVerificationProvider -{ - IEnumerable GetAllDefaultVerifiers(IGameDatabase database, GameVerifySettings settings); -} \ No newline at end of file diff --git a/src/ModVerify/ModVerifyServiceContribution.cs b/src/ModVerify/ModVerifyServiceContribution.cs deleted file mode 100644 index d223d2c..0000000 --- a/src/ModVerify/ModVerifyServiceContribution.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace AET.ModVerify; - -public static class ModVerifyServiceContribution -{ - public static void ContributeServices(IServiceCollection serviceCollection) - { - serviceCollection.AddSingleton(sp => new VerificationProvider(sp)); - } -} \ No newline at end of file diff --git a/src/ModVerify/Reporting/IncompatibleBaselineException.cs b/src/ModVerify/Reporting/IncompatibleBaselineException.cs new file mode 100644 index 0000000..c9a9eb1 --- /dev/null +++ b/src/ModVerify/Reporting/IncompatibleBaselineException.cs @@ -0,0 +1,8 @@ +using System; + +namespace AET.ModVerify.Reporting; + +public sealed class IncompatibleBaselineException : Exception +{ + public override string Message => "The specified baseline is not compatible to this version of the application."; +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonSuppressionFilter.cs b/src/ModVerify/Reporting/Json/JsonSuppressionFilter.cs index 92047d8..6d232c7 100644 --- a/src/ModVerify/Reporting/Json/JsonSuppressionFilter.cs +++ b/src/ModVerify/Reporting/Json/JsonSuppressionFilter.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace AET.ModVerify.Reporting.Json; @@ -13,7 +12,7 @@ internal class JsonSuppressionFilter(SuppressionFilter filter) [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Verifier { get; } = filter.Verifier; - [JsonPropertyName("assets")] + [JsonPropertyName("asset")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public IEnumerable? Assets { get; } = filter.Assets; + public string? Asset { get; } = filter.Asset; } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs b/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs index 303e168..4688c98 100644 --- a/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs +++ b/src/ModVerify/Reporting/Json/JsonVerificationBaseline.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; @@ -6,17 +7,28 @@ namespace AET.ModVerify.Reporting.Json; internal class JsonVerificationBaseline { + [JsonPropertyName("version")] + public Version? Version { get; } + + [JsonPropertyName("minSeverity")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public VerificationSeverity MinimumSeverity { get; } + [JsonPropertyName("errors")] public IEnumerable Errors { get; } public JsonVerificationBaseline(VerificationBaseline baseline) { Errors = baseline.Select(x => new JsonVerificationError(x)); + Version = baseline.Version; + MinimumSeverity = baseline.MinimumSeverity; } [JsonConstructor] - private JsonVerificationBaseline(IEnumerable errors) + private JsonVerificationBaseline(Version version, VerificationSeverity minimumSeverity, IEnumerable errors) { Errors = errors; + Version = version; + MinimumSeverity = minimumSeverity; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Json/JsonVerificationError.cs b/src/ModVerify/Reporting/Json/JsonVerificationError.cs index 5303944..ac80810 100644 --- a/src/ModVerify/Reporting/Json/JsonVerificationError.cs +++ b/src/ModVerify/Reporting/Json/JsonVerificationError.cs @@ -15,19 +15,30 @@ internal class JsonVerificationError public string Message { get; } [JsonPropertyName("severity")] + [JsonConverter(typeof(JsonStringEnumConverter))] public VerificationSeverity Severity { get; } - [JsonPropertyName("assets")] - public IEnumerable Assets { get; } + [JsonPropertyName("context")] + public IEnumerable ContextEntries { get; } + + [JsonPropertyName("asset")] + public string Asset { get; } [JsonConstructor] - private JsonVerificationError(string id, IReadOnlyList verifierChain, string message, VerificationSeverity severity, IEnumerable assets) + private JsonVerificationError( + string id, + IReadOnlyList? verifierChain, + string message, + VerificationSeverity severity, + IEnumerable? contextEntries, + string asset) { Id = id; - VerifierChain = verifierChain; + VerifierChain = verifierChain ?? []; Message = message; Severity = severity; - Assets = assets; + ContextEntries = contextEntries ?? []; + Asset = asset ?? string.Empty; } public JsonVerificationError(VerificationError error) @@ -36,6 +47,7 @@ public JsonVerificationError(VerificationError error) VerifierChain = error.VerifierChain; Message = error.Message; Severity = error.Severity; - Assets = error.AffectedAssets; + ContextEntries = error.ContextEntries; + Asset = error.Asset; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs index 145f2a8..7ee435d 100644 --- a/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs +++ b/src/ModVerify/Reporting/Reporters/ConsoleReporter.cs @@ -6,7 +6,11 @@ namespace AET.ModVerify.Reporting.Reporters; -internal class ConsoleReporter(VerificationReportSettings settings, IServiceProvider serviceProvider) : ReporterBase(settings, serviceProvider) +internal class ConsoleReporter( + VerifyReportSettings settings, + bool summaryOnly, + IServiceProvider serviceProvider) : + ReporterBase(settings, serviceProvider) { public override Task ReportAsync(IReadOnlyCollection errors) { @@ -26,8 +30,16 @@ private void PrintErrorStats(IReadOnlyCollection errors, List Console.WriteLine(); if (errors.Count == 0) { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("No errors! Well done :)"); + if (summaryOnly) + { + Console.WriteLine("No errors found."); + } + else + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("No errors! Well done :)"); + } + Console.ResetColor(); return; } @@ -35,10 +47,8 @@ private void PrintErrorStats(IReadOnlyCollection errors, List Console.WriteLine($"TOTAL Verification Errors: {errors.Count}"); var groupedBySeverity = errors.GroupBy(x => x.Severity); - foreach (var group in groupedBySeverity) - { + foreach (var group in groupedBySeverity) Console.WriteLine($" Severity {group.Key}: {group.Count()}"); - } Console.WriteLine(); if (filteredErrors.Count == 0) @@ -48,7 +58,10 @@ private void PrintErrorStats(IReadOnlyCollection errors, List return; } - Console.WriteLine($"Below the list of error with minimum severity {Settings.MinimumReportSeverity}:"); + if (summaryOnly) + return; + + Console.WriteLine($"Below the list of errors with severity '{Settings.MinimumReportSeverity}' or higher:"); foreach (var error in filteredErrors) Console.WriteLine($"[{error.Severity}] [{error.Id}] Message={error.Message}"); diff --git a/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs b/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs index 839256e..5ecbb6f 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/EngineErrorReporterBase.cs @@ -17,7 +17,8 @@ public IEnumerable GetErrors(IEnumerable errors) foreach (var error in errors) { var errorData = CreateError(error); - yield return new VerificationError(errorData.Identifier, errorData.Message, [Name], errorData.Assets, errorData.Severity); + yield return new VerificationError( + errorData.Identifier, errorData.Message, [Name], errorData.Context, errorData.Asset, errorData.Severity); } } @@ -27,20 +28,22 @@ protected readonly ref struct ErrorData { public string Identifier { get; } public string Message { get; } - public IEnumerable Assets { get; } + public IEnumerable Context { get; } + public string Asset { get; } public VerificationSeverity Severity { get; } - public ErrorData(string identifier, string message, IEnumerable assets, VerificationSeverity severity) + public ErrorData(string identifier, string message, IEnumerable context, string asset, VerificationSeverity severity) { ThrowHelper.ThrowIfNullOrEmpty(identifier); ThrowHelper.ThrowIfNullOrEmpty(message); Identifier = identifier; Message = message; - Assets = assets; + Context = context; + Asset = asset; Severity = severity; } - public ErrorData(string identifier, string message, VerificationSeverity severity) : this(identifier, message, [], severity) + public ErrorData(string identifier, string message, string asset, VerificationSeverity severity) : this(identifier, message, [], asset, severity) { } } diff --git a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs index 7d9aac4..93587b5 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs @@ -14,15 +14,17 @@ internal sealed class GameAssertErrorReporter(IGameRepository gameRepository, IS protected override ErrorData CreateError(EngineAssert assert) { - var assets = new List - { - GetLocation(assert) - }; + var context = new List(); + if (assert.Value is not null) - assets.Add($"value='{assert.Value}'"); + context.Add($"value='{assert.Value}'"); if (assert.Context is not null) - assets.Add($"context='{assert.Context}'"); - return new ErrorData(GetIdFromError(assert.Kind), assert.Message, assets, VerificationSeverity.Warning); + context.Add($"context='{assert.Context}'"); + + // The location is the only identifiable thing of an assert. 'Value' might be null, thus we cannot use it. + var asset = GetLocation(assert); + + return new ErrorData(GetIdFromError(assert.Kind), assert.Message, asset, VerificationSeverity.Warning); } private static string GetLocation(EngineAssert assert) diff --git a/src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs index e4903f6..fc44c66 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/InitializationErrorReporter.cs @@ -15,7 +15,7 @@ protected override ErrorData CreateError(InitializationError error) return new ErrorData( VerifierErrorCodes.InitializationError, error.Message, - [error.GameManager], + error.GameManager, VerificationSeverity.Critical); } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs index c9ce55b..05743a1 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs @@ -24,7 +24,10 @@ protected override ErrorData CreateError(XmlError error) var strippedFileName = _fileSystem.Path .GetGameStrippedPath(GameRepository.Path.AsSpan(), error.FileLocation.XmlFile.ToUpperInvariant().AsSpan()).ToString(); - var assets = new List + + var asset = strippedFileName; + + var context = new List { strippedFileName }; @@ -33,20 +36,22 @@ protected override ErrorData CreateError(XmlError error) if (xmlElement is not null) { - assets.Add(xmlElement.Name.LocalName); + var localName = xmlElement.Name.LocalName; + context.Add(localName); + + asset = localName; var parent = xmlElement.Parent; if (parent != null) { var parentName = parent.Attribute("Name"); - assets.Add(parentName != null ? $"parentName='{parentName.Value}'" : $"parentTag='{parent.Name.LocalName}'"); + context.Add(parentName != null ? $"parentName='{parentName.Value}'" : $"parentTag='{parent.Name.LocalName}'"); } - } var errorMessage = CreateErrorMessage(error, strippedFileName); - return new ErrorData(id, errorMessage, assets, severity); + return new ErrorData(id, errorMessage, context, asset, severity); } private static string CreateErrorMessage(XmlError error, string strippedFileName) diff --git a/src/ModVerify/Reporting/Reporters/ReporterBase.cs b/src/ModVerify/Reporting/Reporters/ReporterBase.cs index ff71507..42b0441 100644 --- a/src/ModVerify/Reporting/Reporters/ReporterBase.cs +++ b/src/ModVerify/Reporting/Reporters/ReporterBase.cs @@ -6,7 +6,7 @@ namespace AET.ModVerify.Reporting.Reporters; -public abstract class ReporterBase(T settings, IServiceProvider serviceProvider) : IVerificationReporter where T : VerificationReportSettings +public abstract class ReporterBase(T settings, IServiceProvider serviceProvider) : IVerificationReporter where T : VerifyReportSettings { protected IServiceProvider ServiceProvider { get; } = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs index 41c2938..25b21a6 100644 --- a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs +++ b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs @@ -23,26 +23,32 @@ public static IServiceCollection RegisterTextFileReporter(this IServiceCollectio }); } - public static IServiceCollection RegisterConsoleReporter(this IServiceCollection serviceCollection) + public static IServiceCollection RegisterConsoleReporter(this IServiceCollection serviceCollection, bool summaryOnly = false) { - return RegisterConsoleReporter(serviceCollection, new VerificationReportSettings + return RegisterConsoleReporter(serviceCollection, new VerifyReportSettings { MinimumReportSeverity = VerificationSeverity.Error - }); + }, summaryOnly); } - public static IServiceCollection RegisterJsonReporter(this IServiceCollection serviceCollection, JsonReporterSettings settings) + public static IServiceCollection RegisterJsonReporter( + this IServiceCollection serviceCollection, + JsonReporterSettings settings) { return serviceCollection.AddSingleton(sp => new JsonReporter(settings, sp)); } - public static IServiceCollection RegisterTextFileReporter(this IServiceCollection serviceCollection, TextFileReporterSettings settings) + public static IServiceCollection RegisterTextFileReporter( + this IServiceCollection serviceCollection, + TextFileReporterSettings settings) { return serviceCollection.AddSingleton(sp => new TextFileReporter(settings, sp)); } - public static IServiceCollection RegisterConsoleReporter(this IServiceCollection serviceCollection, VerificationReportSettings settings) + public static IServiceCollection RegisterConsoleReporter(this IServiceCollection serviceCollection, + VerifyReportSettings settings, + bool summaryOnly = false) { - return serviceCollection.AddSingleton(sp => new ConsoleReporter(settings, sp)); + return serviceCollection.AddSingleton(sp => new ConsoleReporter(settings, summaryOnly, sp)); } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs b/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs index fef047c..c6233a1 100644 --- a/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs +++ b/src/ModVerify/Reporting/Settings/FileBasedReporterSettings.cs @@ -2,7 +2,7 @@ namespace AET.ModVerify.Reporting.Settings; -public record FileBasedReporterSettings : VerificationReportSettings +public record FileBasedReporterSettings : VerifyReportSettings { private readonly string _outputDirectory = Environment.CurrentDirectory; diff --git a/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs b/src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs similarity index 62% rename from src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs rename to src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs index bc3bdf7..b376fe3 100644 --- a/src/ModVerify/Reporting/Settings/GlobalVerificationReportSettings.cs +++ b/src/ModVerify/Reporting/Settings/GlobalVerifyReportSettings.cs @@ -1,10 +1,8 @@ namespace AET.ModVerify.Reporting.Settings; -public record GlobalVerificationReportSettings : VerificationReportSettings +public record GlobalVerifyReportSettings : VerifyReportSettings { public VerificationBaseline Baseline { get; init; } = VerificationBaseline.Empty; public SuppressionList Suppressions { get; init; } = SuppressionList.Empty; - - public bool ReportAsserts { get; init; } = true; } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Settings/VerificationReportSettings.cs b/src/ModVerify/Reporting/Settings/VerifyReportSettings.cs similarity index 79% rename from src/ModVerify/Reporting/Settings/VerificationReportSettings.cs rename to src/ModVerify/Reporting/Settings/VerifyReportSettings.cs index 6be2905..1289822 100644 --- a/src/ModVerify/Reporting/Settings/VerificationReportSettings.cs +++ b/src/ModVerify/Reporting/Settings/VerifyReportSettings.cs @@ -1,6 +1,6 @@ namespace AET.ModVerify.Reporting.Settings; -public record VerificationReportSettings +public record VerifyReportSettings { public VerificationSeverity MinimumReportSeverity { get; init; } = VerificationSeverity.Information; } \ No newline at end of file diff --git a/src/ModVerify/Reporting/SuppressionFilter.cs b/src/ModVerify/Reporting/SuppressionFilter.cs index d2f3a94..efac588 100644 --- a/src/ModVerify/Reporting/SuppressionFilter.cs +++ b/src/ModVerify/Reporting/SuppressionFilter.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using AET.ModVerify.Reporting.Json; @@ -7,33 +6,26 @@ namespace AET.ModVerify.Reporting; public sealed class SuppressionFilter : IEquatable { - private static readonly AssetsEqualityComparer AssetComparer = AssetsEqualityComparer.Instance; - - private readonly HashSet? _assets; - public string? Id { get; } public string? Verifier { get; } - public IReadOnlyCollection? Assets { get; } + public string? Asset { get; } - public SuppressionFilter(string? id, string? verifier, ICollection? assets) + public bool IsDisabled => Id == null && Verifier == null && Asset == null; + + public SuppressionFilter(string? id, string? verifier, string? asset) { Id = id; Verifier = verifier; - if (assets is not null) - _assets = [..assets]; - Assets = _assets?.ToList() ?? null; + Asset = asset; } internal SuppressionFilter(JsonSuppressionFilter filter) { Id = filter.Id; Verifier = filter.Verifier; - - if (filter.Assets is not null) - _assets = [..filter.Assets]; - Assets = _assets?.ToList() ?? null; + Asset = filter.Asset; } public bool Suppresses(VerificationError error) @@ -56,25 +48,17 @@ public bool Suppresses(VerificationError error) return false; } - if (_assets is not null) + if (Asset is not null) { - if (_assets.Count != error.AffectedAssets.Count) - return false; - - if (!_assets.SetEquals(error.AffectedAssets)) + if (error.Asset.Equals(Asset)) + suppresses = true; + else return false; - - suppresses = true; } return suppresses; } - public bool IsDisabled() - { - return Id == null && Verifier == null && (Assets == null || !Assets.Any()); - } - public int Specificity() { var specificity = 0; @@ -82,7 +66,7 @@ public int Specificity() specificity++; if (Verifier != null) specificity++; - if (Assets != null && Assets.Any()) + if (Asset != null) specificity++; return specificity; } @@ -95,7 +79,7 @@ public bool IsSupersededBy(SuppressionFilter other) if (Verifier != null && other.Verifier != null && other.Verifier != Verifier) return false; - if (Assets != null && other.Assets != null && !AssetComparer.Equals(_assets, other._assets)) + if (Asset != null && other.Asset != null) return false; return other.Specificity() < Specificity(); @@ -112,8 +96,7 @@ public bool Equals(SuppressionFilter? other) return false; if (Verifier != other.Verifier) return false; - - return AssetComparer.Equals(_assets, other._assets); + return Asset == other.Asset; } public override bool Equals(object? obj) @@ -126,10 +109,7 @@ public override int GetHashCode() var hashCode = new HashCode(); hashCode.Add(Id); hashCode.Add(Verifier); - if (_assets is not null) - hashCode.Add(_assets, AssetComparer); - else - hashCode.Add((HashSet)null!); + hashCode.Add(Asset); return hashCode.ToHashCode(); } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/SuppressionList.cs b/src/ModVerify/Reporting/SuppressionList.cs index 23d1563..12ebab4 100644 --- a/src/ModVerify/Reporting/SuppressionList.cs +++ b/src/ModVerify/Reporting/SuppressionList.cs @@ -22,7 +22,7 @@ public SuppressionList(IEnumerable suppressionFilters) if (suppressionFilters == null) throw new ArgumentNullException(nameof(suppressionFilters)); - _filters = new List(suppressionFilters); + _filters = [..suppressionFilters]; _minimizedFilters = MinimizeSuppressions(_filters); } @@ -48,6 +48,10 @@ public static SuppressionList FromJson(Stream stream) return new SuppressionList(baselineJson); } + public IEnumerable Apply(IEnumerable errors) + { + return Count == 0 ? errors : errors.Where(e => !Suppresses(e)); + } public bool Suppresses(VerificationError error) { @@ -64,7 +68,7 @@ public bool Suppresses(VerificationError error) private static IReadOnlyCollection MinimizeSuppressions(IEnumerable filters) { - var sortedFilters = filters.Where(f => !f.IsDisabled()) + var sortedFilters = filters.Where(f => !f.IsDisabled) .OrderBy(x => x.Specificity()); var result = new List(); diff --git a/src/ModVerify/Reporting/VerificationBaseline.cs b/src/ModVerify/Reporting/VerificationBaseline.cs index 0eea20a..55a8973 100644 --- a/src/ModVerify/Reporting/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/VerificationBaseline.cs @@ -11,21 +11,41 @@ namespace AET.ModVerify.Reporting; public sealed class VerificationBaseline : IReadOnlyCollection { - public static readonly VerificationBaseline Empty = new([]); + private static readonly Version LatestVersion = new(2, 0); + + public static readonly VerificationBaseline Empty = new(VerificationSeverity.Information, []); private readonly HashSet _errors; + public Version? Version { get; } + + public VerificationSeverity MinimumSeverity { get; } + /// public int Count => _errors.Count; internal VerificationBaseline(JsonVerificationBaseline baseline) { _errors = [..baseline.Errors.Select(x => new VerificationError(x))]; + Version = baseline.Version; + MinimumSeverity = baseline.MinimumSeverity; } - public VerificationBaseline(IEnumerable errors) + public VerificationBaseline(VerificationSeverity minimumSeverity, IEnumerable errors) { _errors = [..errors]; + Version = LatestVersion; + MinimumSeverity = minimumSeverity; + } + + public bool Contains(VerificationError error) + { + return _errors.Contains(error); + } + + public IEnumerable Apply(IEnumerable errors) + { + return Count == 0 ? errors : errors.Where(e => !_errors.Contains(e)); } public void ToJson(Stream stream) @@ -42,13 +62,12 @@ public static VerificationBaseline FromJson(Stream stream) { var baselineJson = JsonSerializer.Deserialize(stream, JsonSerializerOptions.Default); if (baselineJson is null) - throw new InvalidOperationException("Unable to deserialize baseline"); - return new VerificationBaseline(baselineJson); - } + throw new InvalidOperationException("Unable to deserialize baseline."); - public VerificationBaseline MergeWith(IEnumerable errors) - { - return new VerificationBaseline(this.Concat(errors)); + if (baselineJson.Version is null || baselineJson.Version != LatestVersion) + throw new IncompatibleBaselineException(); + + return new VerificationBaseline(baselineJson); } /// @@ -61,4 +80,9 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + public override string ToString() + { + return $"Baseline [Version={Version}, MinSeverity={MinimumSeverity}, NumErrors={Count}]"; + } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/VerificationError.cs b/src/ModVerify/Reporting/VerificationError.cs index 5bdb62c..871e3b7 100644 --- a/src/ModVerify/Reporting/VerificationError.cs +++ b/src/ModVerify/Reporting/VerificationError.cs @@ -2,15 +2,16 @@ using System.Collections.Generic; using System.Linq; using AET.ModVerify.Reporting.Json; +using AET.ModVerify.Verifiers; using AnakinRaW.CommonUtilities; namespace AET.ModVerify.Reporting; public sealed class VerificationError : IEquatable { - private static readonly AssetsEqualityComparer AssetComparer = AssetsEqualityComparer.Instance; + private static readonly VerificationErrorContextEqualityComparer ContextComparer = VerificationErrorContextEqualityComparer.Instance; - private readonly HashSet _assets; + private readonly HashSet _contextEntries; public string Id { get; } @@ -18,22 +19,33 @@ public sealed class VerificationError : IEquatable public IReadOnlyList VerifierChain { get; } - public IReadOnlyCollection AffectedAssets { get; } + public IReadOnlyCollection ContextEntries { get; } public VerificationSeverity Severity { get; } - public VerificationError(string id, string message, IReadOnlyList verifiers, IEnumerable affectedAssets, + public string Asset { get; } + + public VerificationError( + string id, + string message, + IReadOnlyList verifiers, + IEnumerable contextEntries, + string asset, VerificationSeverity severity) { - if (affectedAssets == null) - throw new ArgumentNullException(nameof(affectedAssets)); + if (contextEntries == null) + throw new ArgumentNullException(nameof(contextEntries)); + if (asset is null) + throw new ArgumentNullException(nameof(asset)); ThrowHelper.ThrowIfNullOrEmpty(id); + Id = id; Message = message ?? throw new ArgumentNullException(nameof(message)); VerifierChain = verifiers; Severity = severity; - _assets = [..affectedAssets]; - AffectedAssets = _assets.ToList(); + _contextEntries = [.. contextEntries]; + ContextEntries = _contextEntries.ToList(); + Asset = asset; } internal VerificationError(JsonVerificationError error) @@ -41,28 +53,30 @@ internal VerificationError(JsonVerificationError error) Id = error.Id; Message = error.Message; VerifierChain = error.VerifierChain; - _assets = [..error.Assets]; - AffectedAssets = _assets.ToList(); + _contextEntries = [..error.ContextEntries]; + ContextEntries = _contextEntries.ToList(); + Asset = error.Asset; } public static VerificationError Create( - IReadOnlyList verifiers, + IReadOnlyList verifiers, string id, string message, VerificationSeverity severity, - IEnumerable assets) + IEnumerable context, + string asset) { - return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), assets, severity); + return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), context, asset, severity); } public static VerificationError Create( - IReadOnlyList verifiers, + IReadOnlyList verifiers, string id, string message, VerificationSeverity severity, - params string[] assets) + string asset) { - return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), assets, severity); + return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), [], asset, severity); } public bool Equals(VerificationError? other) @@ -75,7 +89,10 @@ public bool Equals(VerificationError? other) if (!Id.Equals(other.Id)) return false; - return AssetComparer.Equals(_assets, other._assets); + if (!Asset.Equals(other.Asset)) + return false; + + return ContextComparer.Equals(_contextEntries, other._contextEntries); } public override bool Equals(object? obj) @@ -87,12 +104,13 @@ public override int GetHashCode() { var hashCode = new HashCode(); hashCode.Add(Id); - hashCode.Add(_assets, AssetComparer); + hashCode.Add(_contextEntries, ContextComparer); + hashCode.Add(Asset); return hashCode.ToHashCode(); } public override string ToString() { - return $"[{Severity}] [{string.Join(" --> ", VerifierChain)}] {Id}: Message={Message}; Affected Assets=[{string.Join(",", AffectedAssets)}];"; + return $"[{Severity}] [{string.Join(" --> ", VerifierChain)}] {Id}: Message={Message}; Asset='{Asset}'; Context=[{string.Join(",", ContextEntries)}];"; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/AssetsEqualityComparer.cs b/src/ModVerify/Reporting/VerificationErrorContextEqualityComparer.cs similarity index 63% rename from src/ModVerify/Reporting/AssetsEqualityComparer.cs rename to src/ModVerify/Reporting/VerificationErrorContextEqualityComparer.cs index 8f058bc..773b260 100644 --- a/src/ModVerify/Reporting/AssetsEqualityComparer.cs +++ b/src/ModVerify/Reporting/VerificationErrorContextEqualityComparer.cs @@ -2,13 +2,13 @@ namespace AET.ModVerify.Reporting; -internal class AssetsEqualityComparer : IEqualityComparer> +internal class VerificationErrorContextEqualityComparer : IEqualityComparer> { readonly IEqualityComparer> _setComparer = HashSet.CreateSetComparer(); - public static AssetsEqualityComparer Instance { get; } = new(); + public static VerificationErrorContextEqualityComparer Instance { get; } = new(); - private AssetsEqualityComparer() + private VerificationErrorContextEqualityComparer() { } diff --git a/src/ModVerify/Reporting/VerificationReportBroker.cs b/src/ModVerify/Reporting/VerificationReportBroker.cs index 26712a1..5c1e8a3 100644 --- a/src/ModVerify/Reporting/VerificationReportBroker.cs +++ b/src/ModVerify/Reporting/VerificationReportBroker.cs @@ -1,21 +1,17 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; -using AET.ModVerify.Reporting.Settings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace AET.ModVerify.Reporting; -public sealed class VerificationReportBroker(GlobalVerificationReportSettings reportSettings, IServiceProvider serviceProvider) +public sealed class VerificationReportBroker(IServiceProvider serviceProvider) { private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(VerificationReportBroker)); - public async Task> ReportAsync(IEnumerable steps) + public async Task ReportAsync(IReadOnlyCollection errors) { - var suppressions = new SuppressionList(reportSettings.Suppressions); - var errors = GetReportableErrors(steps, suppressions); var reporters = serviceProvider.GetServices(); foreach (var reporter in reporters) @@ -29,28 +25,5 @@ public async Task> ReportAsync(IEnumerabl _logger?.LogError(e, "Exception while reporting verification error"); } } - - return errors; - } - - private IReadOnlyCollection GetReportableErrors(IEnumerable steps, SuppressionList suppressions) - { - var allErrors = steps.SelectMany(s => s.VerifyErrors); - - var errorsToReport = new List(); - foreach (var error in allErrors) - { - if (reportSettings.Baseline.Contains(error)) - continue; - - if (suppressions.Suppresses(error)) - continue; - - errorsToReport.Add(error); - } - - // NB: We don't filter for severity here, as that is something the individual reporters should handle. - // This allows better control over what gets reported. - return errorsToReport; } } \ No newline at end of file diff --git a/src/ModVerify/Settings/GameVerifySettings.cs b/src/ModVerify/Settings/GameVerifySettings.cs index 5e67f46..3f84670 100644 --- a/src/ModVerify/Settings/GameVerifySettings.cs +++ b/src/ModVerify/Settings/GameVerifySettings.cs @@ -1,4 +1,4 @@ -using AET.ModVerify.Reporting.Settings; +using AET.ModVerify.Reporting; namespace AET.ModVerify.Settings; @@ -6,16 +6,14 @@ public record GameVerifySettings { public static readonly GameVerifySettings Default = new() { - AbortSettings = new(), - GlobalReportSettings = new(), - LocalizationOption = VerifyLocalizationOption.English + LocalizationOption = VerifyLocalizationOption.English, + IgnoreAsserts = false, + ThrowsOnMinimumSeverity = null }; - public int ParallelVerifiers { get; init; } = 4; - - public VerificationAbortSettings AbortSettings { get; init; } - - public GlobalVerificationReportSettings GlobalReportSettings { get; init; } + public VerificationSeverity? ThrowsOnMinimumSeverity { get; init; } public VerifyLocalizationOption LocalizationOption { get; init; } + + public bool IgnoreAsserts { get; init; } } \ No newline at end of file diff --git a/src/ModVerify/Settings/VerificationAbortSettings.cs b/src/ModVerify/Settings/VerificationAbortSettings.cs deleted file mode 100644 index 767cef4..0000000 --- a/src/ModVerify/Settings/VerificationAbortSettings.cs +++ /dev/null @@ -1,12 +0,0 @@ -using AET.ModVerify.Reporting; - -namespace AET.ModVerify.Settings; - -public record VerificationAbortSettings -{ - public bool FailFast { get; init; } = false; - - public VerificationSeverity MinimumAbortSeverity { get; init; } = VerificationSeverity.Warning; - - public bool ThrowsGameVerificationException { get; init; } = false; -} \ No newline at end of file diff --git a/src/ModVerify/Utilities/VerificationErrorExtensions.cs b/src/ModVerify/Utilities/VerificationErrorExtensions.cs new file mode 100644 index 0000000..1244b45 --- /dev/null +++ b/src/ModVerify/Utilities/VerificationErrorExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using AET.ModVerify.Reporting; + +namespace AET.ModVerify.Utilities; + +public static class VerificationErrorExtensions +{ + public static IEnumerable ApplyBaseline(this IEnumerable errors, + VerificationBaseline baseline) + { + if (errors == null) + throw new ArgumentNullException(nameof(errors)); + if (baseline == null) + throw new ArgumentNullException(nameof(baseline)); + return baseline.Apply(errors); + } + + public static IEnumerable ApplySuppressions(this IEnumerable errors, + SuppressionList suppressions) + { + if (errors == null) + throw new ArgumentNullException(nameof(errors)); + if (suppressions == null) + throw new ArgumentNullException(nameof(suppressions)); + return suppressions.Apply(errors); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs b/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs new file mode 100644 index 0000000..9595a6d --- /dev/null +++ b/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Concurrent; + +namespace AET.ModVerify.Verifiers; + +internal sealed class AlreadyVerifiedCache +{ + internal static readonly AlreadyVerifiedCache Instance = new(); + + private readonly ConcurrentDictionary _cachedModels = new(StringComparer.OrdinalIgnoreCase); + + private AlreadyVerifiedCache() + { + } + + public bool TryAddModel(string fileName) + { + return _cachedModels.TryAdd(fileName, 0); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/AudioFilesVerifier.cs b/src/ModVerify/Verifiers/AudioFilesVerifier.cs index 62a3297..85ada7a 100644 --- a/src/ModVerify/Verifiers/AudioFilesVerifier.cs +++ b/src/ModVerify/Verifiers/AudioFilesVerifier.cs @@ -23,7 +23,7 @@ namespace AET.ModVerify.Verifiers; -public class AudioFilesVerifier : GameVerifierBase +public class AudioFilesVerifier : GameVerifier { private static readonly PathNormalizeOptions SampleNormalizerOptions = new() { diff --git a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs index b15e7a4..4a1f852 100644 --- a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs +++ b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs @@ -11,7 +11,7 @@ namespace AET.ModVerify.Verifiers; public partial class CommandBarVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(null, gameDatabase, settings, serviceProvider) + : GameVerifier(null, gameDatabase, settings, serviceProvider) { public const string CommandBarNoShellsGroup = "CMDBAR00"; public const string CommandBarManyShellsGroup = "CMDBAR01"; @@ -55,7 +55,7 @@ private void VerifyCommandBarModel(CommandBarBaseComponent component) { AddError(VerificationError.Create(VerifierChain, CommandBarShellNoModel, $"Could not find model '{shellComponent.ModelPath}' for CommandBarShellComponent '{component.Name}'.", - VerificationSeverity.Error, shellComponent.Name, shellComponent.ModelPath)); + VerificationSeverity.Error, [shellComponent.Name], shellComponent.ModelPath)); return; } } @@ -141,7 +141,7 @@ private void VerifyCommandBarShellsGroups() CommandBarManyShellsGroup, $"Found more than one Shells CommandBarGroup. Mind that group names are case-sensitive. Correct name is '{CommandBarConstants.ShellGroupName}'", VerificationSeverity.Warning, - shellGroups.Concat(["GameCommandBar"]))); + shellGroups, "GameCommandBar")); } private void VerifyShellGroup(CommandBarComponentGroup shellGroup) @@ -154,7 +154,7 @@ private void VerifyShellGroup(CommandBarComponentGroup shellGroup) AddError(VerificationError.Create(VerifierChain, CommandBarNoShellsComponentInShellGroup, $"The CommandBar component '{component.Name}' is not a shell component, but part of the '{CommandBarConstants.ShellGroupName}' group.", - VerificationSeverity.Warning, shellGroup.Name, component.Name)); + VerificationSeverity.Warning, component.Name)); } } } diff --git a/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs b/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs index c9fe207..26020a3 100644 --- a/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs +++ b/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs @@ -10,7 +10,7 @@ public sealed partial class ReferencedTexturesVerifier( GameVerifySettings settings, IServiceProvider serviceProvider) : - GameVerifierBase(null, gameDatabase, settings, serviceProvider) + GameVerifier(null, gameDatabase, settings, serviceProvider) { public const string MtdNotFound = "TEX00"; public const string TexutreNotFound = "TEX01"; diff --git a/src/ModVerify/Verifiers/Commons/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs similarity index 78% rename from src/ModVerify/Verifiers/Commons/ReferencedModelsVerifier.cs rename to src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs index c29dd91..ae56992 100644 --- a/src/ModVerify/Verifiers/Commons/ReferencedModelsVerifier.cs +++ b/src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs @@ -1,47 +1,28 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Threading; using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AET.ModVerify.Utilities; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Files.ALO.Files.Models; -using PG.StarWarsGame.Files.ALO.Files.Particles; -using PG.StarWarsGame.Files.ChunkFiles.Data; using AnakinRaW.CommonUtilities.FileSystem; +using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Files; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.ALO.Files.Models; +using PG.StarWarsGame.Files.ALO.Files.Particles; using PG.StarWarsGame.Files.Binary; +using PG.StarWarsGame.Files.ChunkFiles.Data; namespace AET.ModVerify.Verifiers.Commons; -internal sealed class AlreadyVerifiedCache -{ - internal static readonly AlreadyVerifiedCache Instance = new(); - - private readonly ConcurrentDictionary _cachedModels = new(StringComparer.OrdinalIgnoreCase); - - private AlreadyVerifiedCache() - { - } - - public bool TryAddModel(string fileName) - { - return _cachedModels.TryAdd(fileName, 0); - } -} - public sealed class SharedReferencedModelsVerifier( - IGameVerifier? parent, + IGameVerifierInfo? parent, IEnumerable modelSource, IGameDatabase database, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(parent, database, settings, serviceProvider) + : GameVerifier(parent, database, settings, serviceProvider) { private const string ProxyAltIdentifier = "_ALT"; @@ -94,7 +75,7 @@ public override void Verify(CancellationToken token) } finally { - aloFile?.Dispose(); + aloFile?.Dispose(); } } } @@ -122,15 +103,13 @@ private void VerifyParticle(IAloParticleFile file) e => e is ArgumentException, _ => { - var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + var particlePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( VerifierChain, VerifierErrorCodes.InvalidTexture, - $"Invalid texture file name" + - $" '{texture}' in particle 'modelFilePath'", + $"Invalid texture file name '{texture}' in particle '{particlePath}'", VerificationSeverity.Error, - texture, - modelFilePath)); + [particlePath], texture)); }); } @@ -139,13 +118,13 @@ private void VerifyParticle(IAloParticleFile file) if (!fileName.Equals(name, StringComparison.OrdinalIgnoreCase)) { - var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + var particlePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( VerifierChain, VerifierErrorCodes.InvalidParticleName, - $"The particle name '{file.Content.Name}' does not match file name '{modelFilePath}'", + $"The particle name '{file.Content.Name}' does not match file name '{particlePath}'", VerificationSeverity.Error, - modelFilePath)); + particlePath)); } } @@ -165,7 +144,7 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) VerifierErrorCodes.InvalidTexture, $"Invalid texture file name '{texture}' in model '{modelFilePath}'", VerificationSeverity.Error, - texture, modelFilePath)); + [modelFilePath], texture)); }); } @@ -175,14 +154,15 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) e => e is ArgumentException, _ => { - var shaderPath = + var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( VerifierChain, VerifierErrorCodes.InvalidShader, - $"Invalid texture file name '{shader}' in model '{shaderPath}'", + $"Invalid texture file name '{shader}' in model '{modelFilePath}'", VerificationSeverity.Error, - shader, shaderPath)); + [modelFilePath], + shader)); }); } @@ -193,14 +173,15 @@ private void VerifyModel(IAloModelFile file, Queue workingQueue) e => e is ArgumentException, _ => { - var proxyPath = FileSystem.Path + var modelFilePath = FileSystem.Path .GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); AddError(VerificationError.Create( VerifierChain, VerifierErrorCodes.InvalidProxy, - $"Invalid proxy file name '{proxy}' in model '{proxyPath}'", + $"Invalid proxy file name '{proxy}' in model '{modelFilePath}'", VerificationSeverity.Error, - proxy, proxyPath)); + [modelFilePath], + proxy)); }); } } @@ -215,7 +196,7 @@ private void VerifyTextureExists(IPetroglyphFileHolder "Referenced Models"; - - public override void Verify(CancellationToken token) - { - var models = Database.GameObjectTypeManager.Entries - .SelectMany(x => x.Models) - .Concat(FocHardcodedConstants.HardcodedModels); - - var inner = new SharedReferencedModelsVerifier(this, models, Database, Settings, Services); - try - { - inner.Error += OnModelError; - inner.Verify(token); - } - finally - { - inner.Error -= OnModelError; - } - } - - private void OnModelError(object sender, VerificationErrorEventArgs e) - { - AddError(e.Error); - } -} +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 6db240a..47c2bf8 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -17,7 +17,7 @@ public sealed class DuplicateNameFinder( IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(null, gameDatabase, settings, serviceProvider) + : GameVerifier(null, gameDatabase, settings, serviceProvider) { public override string FriendlyName => "Duplicates"; @@ -27,12 +27,12 @@ public override void Verify(CancellationToken token) CheckXmlObjectsForDuplicates("SFXEvent", Database.SfxGameManager); if (Database.GuiDialogManager.MtdFile is not null) - CheckXmlObjectsForDuplicates(Database.GuiDialogManager.MtdFile); + CheckMtdForDuplicates(Database.GuiDialogManager.MtdFile); if (Database.CommandBar.MegaTextureFile is not null) { if (!Database.CommandBar.MegaTextureFile.FilePath.Equals(Database.GuiDialogManager.MtdFile?.FileName)) - CheckXmlObjectsForDuplicates(Database.CommandBar.MegaTextureFile); + CheckMtdForDuplicates(Database.CommandBar.MegaTextureFile); } } @@ -41,7 +41,8 @@ private void CheckForDuplicateCrcEntries( TSource source, Func> crcSelector, Func> entrySelector, - Func, IEnumerable> entryToStringSelector, + Func entryToStringSelector, + Func, IEnumerable> contextSelector, Func, string, string> errorMessageCreator) { foreach (var crc32 in crcSelector(source)) @@ -49,25 +50,28 @@ private void CheckForDuplicateCrcEntries( var entries = entrySelector(source, crc32); if (entries.Count > 1) { - var entryNames = entryToStringSelector(entries); + var entryNames = entryToStringSelector(entries.First()); + var context = contextSelector(entries); AddError(VerificationError.Create( VerifierChain, VerifierErrorCodes.DuplicateFound, errorMessageCreator(entries, sourceName), VerificationSeverity.Error, + context, entryNames)); } } } - private void CheckXmlObjectsForDuplicates(IMtdFile mtdFile) + private void CheckMtdForDuplicates(IMtdFile mtdFile) { CheckForDuplicateCrcEntries( mtdFile.FileName, mtdFile, mtd => mtd.Content.Select(x => x.Crc32), - (mtd, crc32) => mtd.Content.EntriesWithCrc(crc32), - list => list.Select(x => x.FileName), + (mtd, crc32) => mtd.Content.EntriesWithCrc(crc32), + entry => entry.FileName, + entries => entries.Select(x => $"'{x.FileName}' (CRC: {x.Crc32})"), CreateDuplicateMtdErrorMessage); } @@ -78,7 +82,8 @@ private void CheckXmlObjectsForDuplicates(string databaseName, IGameManager manager.EntryKeys, (manager, crc32) => manager.GetEntries(crc32), - list => list.Select(x => x.Name), + entry => entry.Name, + entries => entries.Select(x => $"'{x.Name}' - {x.Location}"), CreateDuplicateXmlErrorMessage); } diff --git a/src/ModVerify/Verifiers/GameEngineErrorCollector.cs b/src/ModVerify/Verifiers/GameEngineErrorCollector.cs index 1e3fbd2..e165c01 100644 --- a/src/ModVerify/Verifiers/GameEngineErrorCollector.cs +++ b/src/ModVerify/Verifiers/GameEngineErrorCollector.cs @@ -12,7 +12,7 @@ public sealed class GameEngineErrorCollector( IDatabaseErrorCollection errorCollection, IGameDatabase gameDatabase, GameVerifySettings settings, - IServiceProvider serviceProvider) : GameVerifierBase(null, gameDatabase, settings, serviceProvider) + IServiceProvider serviceProvider) : GameVerifier(null, gameDatabase, settings, serviceProvider) { public override string FriendlyName => "Reporting Game Engine Errors"; @@ -20,7 +20,7 @@ public override void Verify(CancellationToken token) { AddErrors(new InitializationErrorReporter(Repository, Services).GetErrors(errorCollection.InitializationErrors)); AddErrors(new XmlParseErrorReporter(Repository, Services).GetErrors(errorCollection.XmlErrors)); - if (Settings.GlobalReportSettings.ReportAsserts) + if (!Settings.IgnoreAsserts) AddErrors(new GameAssertErrorReporter(Repository, Services).GetErrors(errorCollection.Asserts)); } diff --git a/src/ModVerify/Verifiers/GameVerifier.cs b/src/ModVerify/Verifiers/GameVerifier.cs new file mode 100644 index 0000000..d2c737d --- /dev/null +++ b/src/ModVerify/Verifiers/GameVerifier.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; +using AET.ModVerify.Settings; +using PG.StarWarsGame.Engine.Database; + +namespace AET.ModVerify.Verifiers; + +public abstract class GameVerifier( + IGameVerifierInfo? parent, + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) + : GameVerifierBase(parent, gameDatabase, settings, serviceProvider) where T : notnull +{ + public abstract void Verify(T toVerify, CancellationToken token); +} + +public abstract class GameVerifier( + IGameVerifierInfo? parent, + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) + : GameVerifierBase(parent, gameDatabase, settings, serviceProvider) +{ + public abstract void Verify(CancellationToken token); +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 8d12f55..34eaeff 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -7,62 +7,64 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; -using System.Threading; namespace AET.ModVerify.Verifiers; -public abstract class GameVerifierBase : IGameVerifier +public abstract class GameVerifierBase : IGameVerifierInfo { public event EventHandler? Error; + private readonly IGameDatabase _gameDatabase; private readonly ConcurrentDictionary _verifyErrors = new(); protected readonly IFileSystem FileSystem; protected readonly IServiceProvider Services; - private readonly IGameDatabase _gameDatabase; + protected readonly GameVerifySettings Settings; - protected GameVerifierBase(IGameVerifier? parent, - IGameDatabase gameDatabase, - GameVerifySettings settings, - IServiceProvider serviceProvider) - { - _gameDatabase = gameDatabase; - FileSystem = serviceProvider.GetRequiredService(); - Services = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - Parent = parent; - Settings = settings; - Database = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); - VerifierChain = CreateVerifierChain(); - } - public IReadOnlyCollection VerifyErrors => [.._verifyErrors.Keys]; + public IReadOnlyCollection VerifyErrors => [.. _verifyErrors.Keys]; public virtual string FriendlyName => GetType().Name; public string Name => GetType().FullName; - public IGameVerifier? Parent { get; } - - protected GameVerifySettings Settings { get; } + public IGameVerifierInfo? Parent { get; } protected IGameDatabase Database { get; } protected IGameRepository Repository => _gameDatabase.GameRepository; - protected IReadOnlyList VerifierChain { get; } + protected IReadOnlyList VerifierChain { get; } - public abstract void Verify(CancellationToken token); + protected GameVerifierBase( + IGameVerifierInfo? parent, + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) + { + if (serviceProvider == null) + throw new ArgumentNullException(nameof(serviceProvider)); + FileSystem = serviceProvider.GetRequiredService(); + Services = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _gameDatabase = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); + Parent = parent; + Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + Database = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); + VerifierChain = CreateVerifierChain(); + } protected void AddError(VerificationError error) { if (_verifyErrors.TryAdd(error, 0)) { Error?.Invoke(this, new VerificationErrorEventArgs(error)); - if (Settings.AbortSettings.FailFast && error.Severity >= Settings.AbortSettings.MinimumAbortSeverity) + + if (error.Severity >= Settings.ThrowsOnMinimumSeverity) throw new GameVerificationException(error); } } + protected void GuardedVerify(Action action, Predicate exceptionFilter, Action exceptionHandler) { try @@ -75,9 +77,9 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter } } - private IReadOnlyList CreateVerifierChain() + private IReadOnlyList CreateVerifierChain() { - var verifierChain = new List { this }; + var verifierChain = new List { this }; var parent = Parent; while (parent != null) diff --git a/src/ModVerify/Verifiers/IGameVerifierInfo.cs b/src/ModVerify/Verifiers/IGameVerifierInfo.cs new file mode 100644 index 0000000..cba43d1 --- /dev/null +++ b/src/ModVerify/Verifiers/IGameVerifierInfo.cs @@ -0,0 +1,10 @@ +namespace AET.ModVerify.Verifiers; + +public interface IGameVerifierInfo +{ + IGameVerifierInfo? Parent { get; } + + string Name { get; } + + string FriendlyName { get; } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs new file mode 100644 index 0000000..502fa85 --- /dev/null +++ b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq; +using System.Threading; +using AET.ModVerify.Settings; +using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Engine.Database; +using AET.ModVerify.Verifiers.Commons; + +namespace AET.ModVerify.Verifiers; + +public sealed class ReferencedModelsVerifier( + IGameDatabase database, + GameVerifySettings settings, + IServiceProvider serviceProvider) + : GameVerifier(null, database, settings, serviceProvider) +{ + public override string FriendlyName => "Referenced Models"; + + public override void Verify(CancellationToken token) + { + var models = Database.GameObjectTypeManager.Entries + .SelectMany(x => x.Models) + .Concat(FocHardcodedConstants.HardcodedModels); + + var inner = new SharedReferencedModelsVerifier(this, models, Database, Settings, Services); + try + { + inner.Error += OnModelError; + inner.Verify(token); + } + finally + { + inner.Error -= OnModelError; + } + } + + private void OnModelError(object sender, VerificationErrorEventArgs e) + { + AddError(e.Error); + } +} diff --git a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs b/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs index 6704003..15ee468 100644 --- a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs +++ b/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs @@ -115,7 +115,7 @@ componentType is not GuiComponentType.Scanlines && AddError(VerificationError.Create(VerifierChain, TexutreNotFound, message, VerificationSeverity.Error, - texture.Texture, component, origin.ToString())); + [component, origin.ToString()], texture.Texture)); } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 75de497..7863880 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -159,8 +159,8 @@ private bool LinkToShell( if (shell is null) { ErrorReporter.Assert( - EngineAssert.FromNullOrEmpty(component, - $"Cannot link component '{component}' because shell component is null.")); + EngineAssert.FromNullOrEmpty( + [component.Name], $"Cannot link component '{component}' because shell component is null.")); return false; } @@ -181,7 +181,8 @@ private bool LinkToShell( if (model is null) { ErrorReporter.Assert( - EngineAssert.FromNullOrEmpty(new ComponentLinkTuple(component, shell), + 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; } @@ -189,7 +190,8 @@ private bool LinkToShell( if (!model.IsModel) { ErrorReporter.Assert( - EngineAssert.FromNullOrEmpty(new ComponentLinkTuple(component, shell), + 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; } @@ -216,7 +218,7 @@ private void SetDefaultFont() int size = 11; var font = fontManager.CreateFont(fontName, size, true, false, false, 1.0f); if (font is null) - ErrorReporter.Assert(EngineAssert.FromNullOrEmpty(this, $"Unable to create Default from name {fontName}")); + ErrorReporter.Assert(EngineAssert.FromNullOrEmpty([ToString()], $"Unable to create Default from name {fontName}")); DefaultFont = font; } } @@ -238,7 +240,7 @@ private void SetMegaTexture() { var message = $"Failed to load MTD file '{mtdPath}': {e.Message}"; Logger?.LogError(e, message); - ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, null, message)); + ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, [], message)); } _megaTextureExists = GameRepository.TextureRepository.FileExists($"{CommandBarConstants.MegaTextureBaseName}.tga"); } @@ -289,15 +291,4 @@ private void VerifyFilePathLength(string filePath) }); } } - - private sealed class ComponentLinkTuple(CommandBarBaseComponent component, CommandBarShellComponent shell) - { - public CommandBarBaseComponent Component { get; } = component; - public CommandBarShellComponent Shell { get; } = shell; - - public override string ToString() - { - return $"component='{Component.Name}' - shell='{Shell.Name}'"; - } - } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs index 78e340b..ddfb52d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs @@ -48,7 +48,7 @@ public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) // TODO: Verifier for any invalid type value errorReporter.Assert( - EngineAssert.Create(EngineAssertKind.InvalidValue, type, xmlData.Name, + EngineAssert.Create(EngineAssertKind.InvalidValue, type, [xmlData.Name], $"Invalid type value '{xmlData.Type}' for CommandbarComponent '{xmlData.Name}')")); return null; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs index 636701e..5cc4c49 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssert.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; @@ -9,42 +10,52 @@ namespace PG.StarWarsGame.Engine.ErrorReporting; public sealed class EngineAssert { private static readonly string ThisNameSpace = typeof(EngineAssert).Namespace!; + private const string NullLiteral = "NULL"; - public object? Value { get; } + public string Value { get; } - public object? Context { get; } + public IReadOnlyCollection Context { get; } public string Message { get; } public string Method { get; } + public int MethodOffset { get; } + public string? TypeName { get; } public EngineAssertKind Kind { get; } - internal EngineAssert(EngineAssertKind kind, object? value, object? context, string? type, string method, string message) + private EngineAssert(EngineAssertKind kind, object? value, IEnumerable context, string? type, string method, int methodOffset, string message) { Kind = kind; - Value = value; - Context = context; + Value = value?.ToString() ?? NullLiteral; + Context = [..context]; Message = message ?? throw new ArgumentNullException(nameof(message)); TypeName = type ?? throw new ArgumentNullException(nameof(type)); Method = method ?? throw new ArgumentNullException(nameof(method)); + MethodOffset = methodOffset; + } + + internal static EngineAssert FromNullOrEmpty(string? message = null) + { + return FromNullOrEmpty([], message); } - internal static EngineAssert FromNullOrEmpty(object? context = null, string? message = null) + internal static EngineAssert FromNullOrEmpty(IEnumerable context, string? message = null) { return Create(EngineAssertKind.NullOrEmptyValue, null, context, message ?? "Expected value to be not null or empty"); } - internal static EngineAssert Create(EngineAssertKind kind, object? value, object? context, string message) + internal static EngineAssert Create(EngineAssertKind kind, object? value, IEnumerable context, string message) { var frame = GetCausingFrame(new StackTrace()); if (frame is null) - return new EngineAssert(kind, value, context, null, "UNKNOWN SOURCE", message); + return new EngineAssert(kind, value, context, null, "UNKNOWN SOURCE", -1, message); + var offset = frame.GetNativeOffset(); var method = frame.GetMethod(); var methodInfo = GetMethodInfo(method); - return new EngineAssert(kind, value, context, methodInfo.type, methodInfo.method, message); + return new EngineAssert(kind, value, context, methodInfo.type, methodInfo.method, offset, message); } private static StackFrame? GetCausingFrame(StackTrace trace) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs index 3890e5d..c3814ea 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager_Initialization.cs @@ -155,7 +155,7 @@ private void InitializeMegaTextures(GuiDialogsXmlTextureData guiDialogs) { var message = $"Failed to load MTD file '{mtdPath}': {e.Message}"; Logger?.LogError(e, message); - ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, null, message)); + ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, mtdPath, [], message)); } } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs index a9ec312..0e43ede 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs @@ -33,7 +33,7 @@ public FontManager(GameRepository repository, GameErrorReporterWrapper errorRepo if (size <= 0) { - ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.ValueOutOfRange, size, fontName, + ErrorReporter.Assert(EngineAssert.Create(EngineAssertKind.ValueOutOfRange, size, [fontName], $"Font size '{size}' for '{fontName}' must be greater than 0.")); } @@ -42,7 +42,7 @@ public FontManager(GameRepository repository, GameErrorReporterWrapper errorRepo if (!_fontNames.Contains(fontName)) { - ErrorReporter.Assert(EngineAssert.FromNullOrEmpty(fontName, $"Unable to find font '{fontName}'")); + ErrorReporter.Assert(EngineAssert.FromNullOrEmpty([fontName], $"Unable to find font '{fontName}'")); return null; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs index 1bf53c9..f11374e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/WindowsFontManager.cs @@ -15,7 +15,7 @@ public IEnumerable GetFontFamilies() var fonts = new HashSet(StringComparer.OrdinalIgnoreCase); try { - foreach (var windowsFontData in Gdi32.EnumFontFamiliesEx(hdc, CharacterSet.DEFAULT_CHARSET)) + foreach (var windowsFontData in GetFonts(hdc)) { var fontName = windowsFontData.lpelfe.elfEnumLogfontEx.elfFullName; fonts.Add(fontName); @@ -27,4 +27,9 @@ public IEnumerable GetFontFamilies() } return fonts; } + + static IEnumerable<(Gdi32.ENUMLOGFONTEXDV lpelfe, Gdi32.ENUMTEXTMETRIC lpntme, Gdi32.FontType FontType)> GetFonts(Gdi32.SafeHDC hdc) + { + return Gdi32.EnumFontFamiliesEx(hdc, CharacterSet.DEFAULT_CHARSET); + } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index f7b2efe..8a003a1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -42,7 +42,7 @@ internal class PGRender( bool throwsException = false) { if (path.IsEmpty) - errorReporter.Assert(EngineAssert.FromNullOrEmpty(null, "Model path is null or empty.")); + errorReporter.Assert(EngineAssert.FromNullOrEmpty([], "Model path is null or empty.")); using var aloStream = _modelRepository.TryOpenFile(path); if (aloStream is null) @@ -62,7 +62,7 @@ internal class PGRender( var pathString = path.ToString(); var errorMessage = $"Unable to load 3D asset '{pathString}': {e.Message}"; _logger?.LogWarning(e, errorMessage); - errorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, pathString, null, errorMessage)); + errorReporter.Assert(EngineAssert.Create(EngineAssertKind.CorruptBinary, pathString, [], errorMessage)); return null; } } @@ -129,7 +129,7 @@ public AnimationCollection LoadAnimations( { var animFile = stringBuilder.AsSpan().ToString(); errorReporter.Assert( - EngineAssert.Create(EngineAssertKind.ValueOutOfRange, animFile, null, + EngineAssert.Create(EngineAssertKind.ValueOutOfRange, animFile, [], $"Cannot get animation file '{animFile}' , because animation file path is too long.")); continue; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlLocationInfo.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlLocationInfo.cs index e92cc63..6a2cb89 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlLocationInfo.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/XmlLocationInfo.cs @@ -24,6 +24,6 @@ public override string ToString() { if (string.IsNullOrEmpty(XmlFile)) return "(n/a)"; - return Line is null ? XmlFile : $"{XmlFile} at line: {Line}"; + return Line is null ? $"'{XmlFile}'" : $"'{XmlFile}':#{Line}"; } } \ No newline at end of file From 5d78d2b904db746b2924bd3ba588cabcb21df296 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 29 Mar 2025 11:31:12 +0100 Subject: [PATCH 26/34] update deps --- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 12 ++++++------ src/ModVerify/ModVerify.csproj | 2 +- .../PG.StarWarsGame.Engine.csproj | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 1d9ec6c..3e7b7c5 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -27,18 +27,18 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index c2852dd..a2bb7c0 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -32,7 +32,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index dc0cc13..d5752e7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -33,7 +33,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From ca1dcf7f2e045d63fad18d73178d594f1efa0cf1 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 29 Mar 2025 11:51:38 +0100 Subject: [PATCH 27/34] move pipeline to lib --- src/ModVerify.CliApp/ModVerifyApp.cs | 4 ++-- .../Options/ModVerifyAppSettings.cs | 1 + .../Options/VerifyPipelineSettings.cs | 14 -------------- src/ModVerify.CliApp/Program.cs | 2 +- src/ModVerify.CliApp/SettingsBuilder.cs | 5 +++-- .../Pipeline/DefaultGameVerifiersProvider.cs} | 4 ++-- .../Pipeline/GameVerifierPipelineStep.cs | 4 ++-- .../Pipeline/GameVerifyPipeline.cs} | 11 +++++------ .../Pipeline/IGameVerifiersProvider.cs} | 10 +++++----- src/ModVerify/Settings/VerifyPipelineSettings.cs | 14 ++++++++++++++ 10 files changed, 35 insertions(+), 34 deletions(-) delete mode 100644 src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs rename src/{ModVerify.CliApp/GameVerifierFactory.cs => ModVerify/Pipeline/DefaultGameVerifiersProvider.cs} (87%) rename src/{ModVerify.CliApp => ModVerify}/Pipeline/GameVerifierPipelineStep.cs (78%) rename src/{ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs => ModVerify/Pipeline/GameVerifyPipeline.cs} (92%) rename src/{ModVerify.CliApp/IGameVerifierFactory.cs => ModVerify/Pipeline/IGameVerifiersProvider.cs} (68%) create mode 100644 src/ModVerify/Settings/VerifyPipelineSettings.cs diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApp.cs index 2093da0..f8ff9d8 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApp.cs @@ -10,7 +10,7 @@ using System.IO.Abstractions; using System.Linq; using System.Threading.Tasks; -using AET.ModVerifyTool.Pipeline; +using AET.ModVerify.Pipeline; namespace AET.ModVerifyTool; @@ -49,7 +49,7 @@ public async Task RunApplication() private async Task> Verify(VerifyGameInstallationData installData) { - var verifyPipeline = new VerifyGamePipeline( + var verifyPipeline = new GameVerifyPipeline( installData.EngineType, installData.GameLocations, settings.VerifyPipelineSettings, diff --git a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs index ff9c4d6..eaa46d9 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; +using AET.ModVerify.Settings; namespace AET.ModVerifyTool.Options; diff --git a/src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs b/src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs deleted file mode 100644 index 1f09876..0000000 --- a/src/ModVerify.CliApp/Options/VerifyPipelineSettings.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AET.ModVerify.Settings; - -namespace AET.ModVerifyTool.Options; - -internal sealed class VerifyPipelineSettings -{ - public required GameVerifySettings GameVerifySettings { get; init; } - - public required IGameVerifierFactory VerifierFactory { get; init; } - - public bool FailFast { get; init; } - - public int ParallelVerifiers { get; init; } = 4; -} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 3ee4778..478e794 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -28,8 +28,8 @@ using System; using System.IO.Abstractions; using System.Threading.Tasks; +using AET.ModVerify.Pipeline; using AET.ModVerifyTool.Options.CommandLine; -using AET.ModVerifyTool.Pipeline; using Testably.Abstractions; using ILogger = Serilog.ILogger; using VerifyVerbOption = AET.ModVerifyTool.Options.CommandLine.VerifyVerbOption; diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index e395449..580c166 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Abstractions; +using AET.ModVerify.Pipeline; using AET.ModVerifyTool.Options.CommandLine; using Microsoft.Extensions.Logging; @@ -43,7 +44,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) { VerifyPipelineSettings = new VerifyPipelineSettings { - VerifierFactory = new GameVerifierFactory(), + VerifiersProvider = new DefaultGameVerifiersProvider(), FailFast = verifyOptions.FailFast, GameVerifySettings = new GameVerifySettings { @@ -89,7 +90,7 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio IgnoreAsserts = false, ThrowsOnMinimumSeverity = null, }, - VerifierFactory = new GameVerifierFactory(), + VerifiersProvider = new DefaultGameVerifiersProvider(), FailFast = false, }, AppThrowsOnMinimumSeverity = null, diff --git a/src/ModVerify.CliApp/GameVerifierFactory.cs b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs similarity index 87% rename from src/ModVerify.CliApp/GameVerifierFactory.cs rename to src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs index 8ff1bcb..63be7cc 100644 --- a/src/ModVerify.CliApp/GameVerifierFactory.cs +++ b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs @@ -4,9 +4,9 @@ using AET.ModVerify.Verifiers; using PG.StarWarsGame.Engine.Database; -namespace AET.ModVerifyTool; +namespace AET.ModVerify.Pipeline; -internal class GameVerifierFactory : IGameVerifierFactory +public sealed class DefaultGameVerifiersProvider : IGameVerifiersProvider { public IEnumerable GetVerifiers( IGameDatabase database, diff --git a/src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs similarity index 78% rename from src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs rename to src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index 232fb62..52ba258 100644 --- a/src/ModVerify.CliApp/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -4,9 +4,9 @@ using AnakinRaW.CommonUtilities.SimplePipeline.Steps; using Microsoft.Extensions.Logging; -namespace AET.ModVerifyTool.Pipeline; +namespace AET.ModVerify.Pipeline; -internal sealed class GameVerifierPipelineStep(GameVerifier verifier, IServiceProvider serviceProvider) : PipelineStep(serviceProvider) +public sealed class GameVerifierPipelineStep(GameVerifier verifier, IServiceProvider serviceProvider) : PipelineStep(serviceProvider) { private readonly GameVerifier _gameVerifier = verifier ?? throw new ArgumentNullException(nameof(verifier)); diff --git a/src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs similarity index 92% rename from src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs rename to src/ModVerify/Pipeline/GameVerifyPipeline.cs index 5358734..59afca6 100644 --- a/src/ModVerify.CliApp/Pipeline/VerifyGamePipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -3,12 +3,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using AET.ModVerify; using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; +using AET.ModVerify.Settings; using AET.ModVerify.Utilities; using AET.ModVerify.Verifiers; -using AET.ModVerifyTool.Options; using AnakinRaW.CommonUtilities.SimplePipeline; using AnakinRaW.CommonUtilities.SimplePipeline.Runners; using Microsoft.Extensions.DependencyInjection; @@ -16,9 +15,9 @@ using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; -namespace AET.ModVerifyTool.Pipeline; +namespace AET.ModVerify.Pipeline; -internal sealed class VerifyGamePipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline +public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline { private readonly List _verificationSteps = new(); private readonly GameEngineType _targetType; @@ -32,7 +31,7 @@ internal sealed class VerifyGamePipeline : AnakinRaW.CommonUtilities.SimplePipel public IReadOnlyCollection FilteredErrors { get; private set; } = []; - public VerifyGamePipeline( + public GameVerifyPipeline( GameEngineType targetType, GameLocations gameLocations, VerifyPipelineSettings pipelineSettings, @@ -104,7 +103,7 @@ protected override void OnError(object sender, StepRunnerErrorEventArgs e) private IEnumerable CreateVerificationSteps(IGameDatabase database) { - return _pipelineSettings.VerifierFactory.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); + return _pipelineSettings.VerifiersProvider.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); } private void AddStep(GameVerifier verifier) diff --git a/src/ModVerify.CliApp/IGameVerifierFactory.cs b/src/ModVerify/Pipeline/IGameVerifiersProvider.cs similarity index 68% rename from src/ModVerify.CliApp/IGameVerifierFactory.cs rename to src/ModVerify/Pipeline/IGameVerifiersProvider.cs index 36cb424..be70bb0 100644 --- a/src/ModVerify.CliApp/IGameVerifierFactory.cs +++ b/src/ModVerify/Pipeline/IGameVerifiersProvider.cs @@ -1,12 +1,12 @@ -using AET.ModVerify.Settings; +using System; +using System.Collections.Generic; +using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; using PG.StarWarsGame.Engine.Database; -using System; -using System.Collections.Generic; -namespace AET.ModVerifyTool; +namespace AET.ModVerify.Pipeline; -public interface IGameVerifierFactory +public interface IGameVerifiersProvider { IEnumerable GetVerifiers( IGameDatabase database, diff --git a/src/ModVerify/Settings/VerifyPipelineSettings.cs b/src/ModVerify/Settings/VerifyPipelineSettings.cs new file mode 100644 index 0000000..2e11c74 --- /dev/null +++ b/src/ModVerify/Settings/VerifyPipelineSettings.cs @@ -0,0 +1,14 @@ +using AET.ModVerify.Pipeline; + +namespace AET.ModVerify.Settings; + +public sealed class VerifyPipelineSettings +{ + public required GameVerifySettings GameVerifySettings { get; init; } + + public required IGameVerifiersProvider VerifiersProvider { get; init; } + + public bool FailFast { get; init; } + + public int ParallelVerifiers { get; init; } = 4; +} \ No newline at end of file From 559279038fac13e8f1b93c8b67d0558aae5a939c Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 30 Mar 2025 15:04:43 +0200 Subject: [PATCH 28/34] reorganize modifiers --- .../CommandLine/BaseModVerifyOptions.cs | 3 + .../Options/ModVerifyAppSettings.cs | 2 + src/ModVerify.CliApp/Program.cs | 15 +- .../Properties/launchSettings.json | 4 +- src/ModVerify.CliApp/SettingsBuilder.cs | 4 +- src/ModVerify/ModVerifyServiceExtensions.cs | 12 + .../Pipeline/DefaultGameVerifiersProvider.cs | 3 +- .../Reporters/Engine/XmlParseErrorReporter.cs | 2 +- src/ModVerify/Reporting/VerificationError.cs | 10 +- .../Verifiers/AlreadyVerifiedCache.cs | 20 +- .../Verifiers/Commons/ModelVerifier.cs | 288 ++++++++++++++++++ .../Commons/ReferencedTexturesVerifier.cs | 32 -- .../Commons/SharedReferencedModelsVerifier.cs | 258 ---------------- .../Verifiers/Commons/TextureVeifier.cs | 62 ++++ src/ModVerify/Verifiers/GameVerifier.cs | 3 +- src/ModVerify/Verifiers/GameVerifierBase.cs | 2 - .../GuiDialogsVerifier.cs} | 80 +++-- .../Verifiers/IAlreadyVerifiedCache.cs | 11 + .../Verifiers/ReferencedModelsVerifier.cs | 8 +- src/ModVerify/Verifiers/VerifierErrorCodes.cs | 17 +- .../GuiDialog/GuiDialogGameManager.cs | 2 +- .../IO/FileFoundInfo.cs | 2 + .../PG.StarWarsGame.Engine/IO/IRepository.cs | 1 + .../IO/MultiPassRepository.cs | 13 +- .../IO/Repositories/GameRepository.Files.cs | 6 + .../IO/Repositories/TextureRepository.cs | 9 +- 26 files changed, 513 insertions(+), 356 deletions(-) create mode 100644 src/ModVerify/ModVerifyServiceExtensions.cs create mode 100644 src/ModVerify/Verifiers/Commons/ModelVerifier.cs delete mode 100644 src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs delete mode 100644 src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs create mode 100644 src/ModVerify/Verifiers/Commons/TextureVeifier.cs rename src/ModVerify/Verifiers/{ReferencedTexturesVerifier.GUI.cs => GuiDialogs/GuiDialogsVerifier.cs} (69%) create mode 100644 src/ModVerify/Verifiers/IAlreadyVerifiedCache.cs diff --git a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs index ca1f358..1997e54 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs @@ -10,6 +10,9 @@ internal abstract class BaseModVerifyOptions [Option('v', "verbose", Required = false, HelpText = "Sets output to verbose messages.")] public bool Verbose { get; set; } + [Option("offline", Default = false, HelpText = "When set, the application will work in offline mode and does not need an Internet connection.")] + public bool OfflineMode { get; set; } + [Option("minSeverity", Required = false, Default = VerificationSeverity.Information, HelpText = "When set, only findings with at least the specified severity value are processed.")] public VerificationSeverity MinimumSeverity { get; set; } diff --git a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs index eaa46d9..d95f144 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs @@ -21,4 +21,6 @@ internal sealed class ModVerifyAppSettings public bool CreateNewBaseline => !string.IsNullOrEmpty(NewBaselinePath); public string? NewBaselinePath { get; init; } + + public bool Offline { get; init; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 478e794..38ee4c9 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -26,8 +26,10 @@ using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; using System; +using System.Dynamic; using System.IO.Abstractions; using System.Threading.Tasks; +using AET.ModVerify; using AET.ModVerify.Pipeline; using AET.ModVerifyTool.Options.CommandLine; using Testably.Abstractions; @@ -130,11 +132,20 @@ private static IServiceProvider CreateAppServices(IServiceCollection serviceColl PetroglyphCommons.ContributeServices(serviceCollection); PetroglyphEngineServiceContribution.ContributeServices(serviceCollection); + serviceCollection.RegisterVerifierCache(); SetupVerifyReporting(serviceCollection, settings); - serviceCollection.AddSingleton(sp => new OnlineModNameResolver(sp)); - serviceCollection.AddSingleton(sp => new OnlineModGameTypeResolver(sp)); + if (settings.Offline) + { + serviceCollection.AddSingleton(sp => new OfflineModNameResolver(sp)); + serviceCollection.AddSingleton(sp => new OfflineModGameTypeResolver(sp)); + } + else + { + serviceCollection.AddSingleton(sp => new OnlineModNameResolver(sp)); + serviceCollection.AddSingleton(sp => new OnlineModGameTypeResolver(sp)); + } return serviceCollection.BuildServiceProvider(); } diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index f4d0f3a..f5990c3 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,11 +2,11 @@ "profiles": { "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --failFast --baseline focBaseline.json" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --baseline focBaseline.json --offline" }, "Interactive Baseline": { "commandName": "Project", - "commandLineArgs": "createBaseline -o focBaseline.json" + "commandLineArgs": "createBaseline -o focBaseline.json --offline" }, "FromModPath": { diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index 580c166..7102d57 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -55,7 +55,8 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) AppThrowsOnMinimumSeverity = verifyOptions.MinimumFailureSeverity, GameInstallationsSettings = BuildInstallationSettings(verifyOptions), GlobalReportSettings = BuilderGlobalReportSettings(verifyOptions), - ReportOutput = output + ReportOutput = output, + Offline = verifyOptions.OfflineMode }; VerificationSeverity? GetVerifierMinimumThrowSeverity() @@ -98,6 +99,7 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio GlobalReportSettings = BuilderGlobalReportSettings(baselineVerb), NewBaselinePath = baselineVerb.OutputFile, ReportOutput = null, + Offline = baselineVerb.OfflineMode }; } diff --git a/src/ModVerify/ModVerifyServiceExtensions.cs b/src/ModVerify/ModVerifyServiceExtensions.cs new file mode 100644 index 0000000..8b46960 --- /dev/null +++ b/src/ModVerify/ModVerifyServiceExtensions.cs @@ -0,0 +1,12 @@ +using AET.ModVerify.Verifiers; +using Microsoft.Extensions.DependencyInjection; + +namespace AET.ModVerify; + +public static class ModVerifyServiceExtensions +{ + public static IServiceCollection RegisterVerifierCache(this IServiceCollection serviceCollection) + { + return serviceCollection.AddSingleton(sp => new AlreadyVerifiedCache(sp)); + } +} \ No newline at end of file diff --git a/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs index 63be7cc..e916333 100644 --- a/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs +++ b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; +using AET.ModVerify.Verifiers.GuiDialogs; using PG.StarWarsGame.Engine.Database; namespace AET.ModVerify.Pipeline; @@ -16,7 +17,7 @@ public IEnumerable GetVerifiers( yield return new ReferencedModelsVerifier(database, settings, serviceProvider); yield return new DuplicateNameFinder(database, settings, serviceProvider); yield return new AudioFilesVerifier(database, settings, serviceProvider); - yield return new ReferencedTexturesVerifier(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/Reporting/Reporters/Engine/XmlParseErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs index 05743a1..094a701 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/XmlParseErrorReporter.cs @@ -84,7 +84,7 @@ private static string GetIdFromError(XmlParseErrorKind xmlErrorErrorKind) return xmlErrorErrorKind switch { XmlParseErrorKind.EmptyRoot => VerifierErrorCodes.EmptyXmlRoot, - XmlParseErrorKind.MissingFile => VerifierErrorCodes.MissingXmlFile, + XmlParseErrorKind.MissingFile => VerifierErrorCodes.FileNotFound, XmlParseErrorKind.InvalidValue => VerifierErrorCodes.InvalidXmlValue, XmlParseErrorKind.MalformedValue => VerifierErrorCodes.MalformedXmlValue, XmlParseErrorKind.MissingAttribute => VerifierErrorCodes.MissingXmlAttribute, diff --git a/src/ModVerify/Reporting/VerificationError.cs b/src/ModVerify/Reporting/VerificationError.cs index 871e3b7..4174f32 100644 --- a/src/ModVerify/Reporting/VerificationError.cs +++ b/src/ModVerify/Reporting/VerificationError.cs @@ -28,7 +28,7 @@ public sealed class VerificationError : IEquatable public VerificationError( string id, string message, - IReadOnlyList verifiers, + IReadOnlyList verifiers, IEnumerable contextEntries, string asset, VerificationSeverity severity) @@ -76,7 +76,13 @@ public static VerificationError Create( VerificationSeverity severity, string asset) { - return new VerificationError(id, message, verifiers.Select(x => x.Name).ToList(), [], asset, severity); + return new VerificationError( + id, + message, + verifiers.Select(x => x.Name).ToList(), + [], + asset, + severity); } public bool Equals(VerificationError? other) diff --git a/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs b/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs index 9595a6d..530ca4e 100644 --- a/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs +++ b/src/ModVerify/Verifiers/AlreadyVerifiedCache.cs @@ -1,20 +1,28 @@ using System; using System.Collections.Concurrent; +using Microsoft.Extensions.DependencyInjection; +using PG.Commons.Hashing; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Verifiers; -internal sealed class AlreadyVerifiedCache +internal sealed class AlreadyVerifiedCache(IServiceProvider serviceProvider) : IAlreadyVerifiedCache { - internal static readonly AlreadyVerifiedCache Instance = new(); + private readonly ICrc32HashingService _crc32Hashing = serviceProvider.GetRequiredService(); + private readonly ConcurrentDictionary _cachedChecksums = new(); - private readonly ConcurrentDictionary _cachedModels = new(StringComparer.OrdinalIgnoreCase); + public bool TryAddEntry(string entry) + { + return TryAddEntry(entry.AsSpan()); + } - private AlreadyVerifiedCache() + public bool TryAddEntry(ReadOnlySpan entry) { + return TryAddEntry(_crc32Hashing.GetCrc32Upper(entry, PGConstants.DefaultPGEncoding)); } - public bool TryAddModel(string fileName) + public bool TryAddEntry(Crc32 checksum) { - return _cachedModels.TryAdd(fileName, 0); + return _cachedChecksums.TryAdd(checksum, 0); } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/Commons/ModelVerifier.cs b/src/ModVerify/Verifiers/Commons/ModelVerifier.cs new file mode 100644 index 0000000..f79f436 --- /dev/null +++ b/src/ModVerify/Verifiers/Commons/ModelVerifier.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using AET.ModVerify.Reporting; +using AET.ModVerify.Settings; +using AET.ModVerify.Utilities; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Files; +using PG.StarWarsGame.Files.ALO.Data; +using PG.StarWarsGame.Files.ALO.Files; +using PG.StarWarsGame.Files.ALO.Files.Models; +using PG.StarWarsGame.Files.ALO.Files.Particles; +using PG.StarWarsGame.Files.Binary; +#if NETSTANDARD2_0 || NETFRAMEWORK +using AnakinRaW.CommonUtilities.FileSystem; +#endif + +namespace AET.ModVerify.Verifiers.Commons; + +public sealed class SingleModelVerifier : GameVerifier +{ + private const string ProxyAltIdentifier = "_ALT"; + + private readonly TextureVeifier _textureVerifier; + private readonly IAlreadyVerifiedCache? _cache; + + public SingleModelVerifier(IGameVerifierInfo? parent, + IGameDatabase database, + GameVerifySettings settings, + IServiceProvider serviceProvider) : base(parent, database, settings, serviceProvider) + { + _textureVerifier = new TextureVeifier(this, database, settings, serviceProvider); + _cache = serviceProvider.GetService(); + } + + public override void Verify(string modelName, IReadOnlyCollection contextInfo, CancellationToken token) + { + try + { + _textureVerifier.Error += OnTextureError; + + var modelPath = BuildModelPath(modelName); + VerifyAlamoFile(modelPath, contextInfo, token); + } + finally + { + _textureVerifier.Error -= OnTextureError; + } + } + + private void OnTextureError(object sender, VerificationErrorEventArgs e) + { + AddError(e.Error); + } + + private void VerifyAlamoFile(string modelPath, IReadOnlyCollection contextInfo, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + var modelName = FileSystem.Path.GetFileName(modelPath.AsSpan()); + + if (_cache?.TryAddEntry(modelName) == false) + return; + + IAloFile? aloFile = null; + try + { + try + { + aloFile = Database.PGRender.Load3DAsset(modelPath, true, true); + } + catch (BinaryCorruptedException e) + { + var aloFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), modelPath.AsSpan()).ToString(); + var message = $"'{aloFilePath}' is corrupted: {e.Message}"; + AddError(VerificationError.Create(VerifierChain, VerifierErrorCodes.FileCorrupt, message, + VerificationSeverity.Critical, contextInfo, aloFilePath)); + return; + } + + if (aloFile is null) + { + var modelNameString = modelName.ToString(); + var error = VerificationError.Create( + VerifierChain, + VerifierErrorCodes.FileNotFound, + $"Unable to find .ALO file '{modelNameString}'", + VerificationSeverity.Error, + contextInfo, + modelNameString); + AddError(error); + return; + } + + VerifyModelOrParticle(aloFile, contextInfo, token); + } + finally + { + aloFile?.Dispose(); + } + } + + private void VerifyModelOrParticle( + IAloFile aloFile, + IReadOnlyCollection contextInfo, + CancellationToken token) + { + switch (aloFile) + { + case IAloModelFile model: + VerifyModel(model, contextInfo, token); + break; + case IAloParticleFile particle: + VerifyParticle(particle, contextInfo); + break; + default: + throw new InvalidOperationException("The data stream is neither a model nor particle."); + } + } + + private void VerifyParticle(IAloParticleFile file, IReadOnlyCollection contextInfo) + { + foreach (var texture in file.Content.Textures) + { + GuardedVerify(() => VerifyTextureExists(file, texture, contextInfo), + e => e is ArgumentException, + _ => + { + var particlePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + AddError(VerificationError.Create( + VerifierChain, + VerifierErrorCodes.InvalidFilePath, + $"Invalid texture file name '{texture}' in particle '{particlePath}'", + VerificationSeverity.Error, + [particlePath], texture)); + }); + } + + var fileName = FileSystem.Path.GetFileNameWithoutExtension(file.FilePath.AsSpan()); + var name = file.Content.Name.AsSpan(); + + if (!fileName.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + var particlePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + AddError(VerificationError.Create( + VerifierChain, + VerifierErrorCodes.InvalidParticleName, + $"The particle name '{file.Content.Name}' does not match file name '{particlePath}'", + VerificationSeverity.Error, + particlePath)); + } + + } + + private void VerifyModel(IAloModelFile file, IReadOnlyCollection contextInfo, CancellationToken token) + { + foreach (var texture in file.Content.Textures) + { + GuardedVerify(() => VerifyTextureExists(file, texture, contextInfo), + e => e is ArgumentException, + _ => + { + var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + AddError(VerificationError.Create( + VerifierChain, + VerifierErrorCodes.InvalidFilePath, + $"Invalid texture file name '{texture}' in model '{modelFilePath}'", + VerificationSeverity.Error, + [modelFilePath], + texture)); + }); + } + + foreach (var shader in file.Content.Shaders) + { + GuardedVerify(() => VerifyShaderExists(file, shader, contextInfo), + e => e is ArgumentException, + _ => + { + var modelFilePath = + FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + AddError(VerificationError.Create( + VerifierChain, + VerifierErrorCodes.InvalidFilePath, + $"Invalid shader file name '{shader}' in model '{modelFilePath}'", + VerificationSeverity.Error, + [modelFilePath], + shader)); + }); + } + + + foreach (var proxy in file.Content.Proxies) + { + GuardedVerify(() => VerifyProxyExists(file, proxy, contextInfo, token), + e => e is ArgumentException, + _ => + { + var modelFilePath = FileSystem.Path + .GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); + AddError(VerificationError.Create( + VerifierChain, + VerifierErrorCodes.InvalidFilePath, + $"Invalid proxy file name '{proxy}' for model '{modelFilePath}'", + VerificationSeverity.Error, + [..contextInfo, modelFilePath], + proxy)); + }); + } + } + + private void VerifyTextureExists(IPetroglyphFileHolder model, string texture, IReadOnlyCollection contextInfo) + { + if (texture == "None") + return; + _textureVerifier.Verify(texture, [..contextInfo, model.FileName], CancellationToken.None); + } + + private void VerifyProxyExists(IPetroglyphFileHolder model, string proxy, IReadOnlyCollection contextInfo, CancellationToken token) + { + var proxyName = ProxyNameWithoutAlt(proxy); + var proxyPath = BuildModelPath(proxyName); + + var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), model.FilePath.AsSpan()).ToString(); + + if (!Repository.ModelRepository.FileExists(proxyPath)) + { + var message = $"Proxy particle '{proxyName}' not found for model '{modelFilePath}'"; + var error = VerificationError.Create( + VerifierChain, + VerifierErrorCodes.FileNotFound, + message, + VerificationSeverity.Error, + [..contextInfo, modelFilePath], + proxyName); + AddError(error); + return; + } + + VerifyAlamoFile(proxyPath, [..contextInfo, modelFilePath], token); + } + + private void VerifyShaderExists(IPetroglyphFileHolder model, string shader, IReadOnlyCollection contextInfo) + { + if (shader is "alDefault.fx" or "alDefault.fxo") + return; + + if (!Repository.EffectsRepository.FileExists(shader)) + { + var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), model.FilePath.AsSpan()).ToString(); + var message = $"Shader effect '{shader}' not found for model '{modelFilePath}'."; + var error = VerificationError.Create( + VerifierChain, + VerifierErrorCodes.FileNotFound, + message, + VerificationSeverity.Error, + [..contextInfo, modelFilePath], + shader); + AddError(error); + } + } + + private string BuildModelPath(string fileName) + { + return FileSystem.Path.Combine("DATA\\ART\\MODELS", fileName); + } + + private static string ProxyNameWithoutAlt(string proxy) + { + var proxyName = proxy.AsSpan(); + + var altSpan = ProxyAltIdentifier.AsSpan(); + + var altIndex = proxyName.LastIndexOf(altSpan); + + if (altIndex == -1) + return proxy; + + while (altIndex != -1) + { + proxyName = proxyName.Slice(0, altIndex); + altIndex = proxyName.LastIndexOf(altSpan); + } + + return proxyName.ToString(); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs b/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs deleted file mode 100644 index 26020a3..0000000 --- a/src/ModVerify/Verifiers/Commons/ReferencedTexturesVerifier.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using AET.ModVerify.Settings; -using PG.StarWarsGame.Engine.Database; - -namespace AET.ModVerify.Verifiers; -public sealed partial class ReferencedTexturesVerifier( - IGameDatabase gameDatabase, - GameVerifySettings settings, - IServiceProvider serviceProvider) - : - GameVerifier(null, gameDatabase, settings, serviceProvider) -{ - public const string MtdNotFound = "TEX00"; - public const string TexutreNotFound = "TEX01"; - public const string FileNameTooLong = "PAT00"; - - public override void Verify(CancellationToken token) - { - var textures = new HashSet(StringComparer.OrdinalIgnoreCase); - try - { - VerifyGuiTextures(textures); - } - finally - { - textures.Clear(); - } - - } -} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs deleted file mode 100644 index ae56992..0000000 --- a/src/ModVerify/Verifiers/Commons/SharedReferencedModelsVerifier.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using AET.ModVerify.Reporting; -using AET.ModVerify.Settings; -using AET.ModVerify.Utilities; -using AnakinRaW.CommonUtilities.FileSystem; -using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Files; -using PG.StarWarsGame.Files.ALO.Data; -using PG.StarWarsGame.Files.ALO.Files; -using PG.StarWarsGame.Files.ALO.Files.Models; -using PG.StarWarsGame.Files.ALO.Files.Particles; -using PG.StarWarsGame.Files.Binary; -using PG.StarWarsGame.Files.ChunkFiles.Data; - -namespace AET.ModVerify.Verifiers.Commons; - -public sealed class SharedReferencedModelsVerifier( - IGameVerifierInfo? parent, - IEnumerable modelSource, - IGameDatabase database, - GameVerifySettings settings, - IServiceProvider serviceProvider) - : GameVerifier(parent, database, settings, serviceProvider) -{ - private const string ProxyAltIdentifier = "_ALT"; - - private readonly AlreadyVerifiedCache _cache = AlreadyVerifiedCache.Instance; - - public override string FriendlyName => "Models"; - - public override void Verify(CancellationToken token) - { - var aloQueue = new Queue(modelSource); - - while (aloQueue.Count != 0) - { - var fileName = aloQueue.Dequeue(); - if (!_cache.TryAddModel(fileName)) - continue; - - token.ThrowIfCancellationRequested(); - - var modelPath = BuildModelPath(fileName).AsSpan(); - - IAloFile? aloFile = null; - try - { - try - { - aloFile = Database.PGRender.Load3DAsset(modelPath, true, true); - } - catch (BinaryCorruptedException e) - { - var aloFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), modelPath).ToString(); - var message = $"{aloFile} is corrupted: {e.Message}"; - AddError(VerificationError.Create(VerifierChain, VerifierErrorCodes.ModelBroken, message, VerificationSeverity.Critical, aloFilePath)); - continue; - } - - if (aloFile is null) - { - var error = VerificationError.Create( - VerifierChain, - VerifierErrorCodes.ModelNotFound, - $"Unable to find .ALO file '{fileName}'", - VerificationSeverity.Error, - fileName); - AddError(error); - continue; - } - - VerifyModelOrParticle(aloFile, aloQueue); - } - finally - { - aloFile?.Dispose(); - } - } - } - - private void VerifyModelOrParticle(IAloFile aloFile, Queue workingQueue) - { - switch (aloFile) - { - case IAloModelFile model: - VerifyModel(model, workingQueue); - break; - case IAloParticleFile particle: - VerifyParticle(particle); - break; - default: - throw new InvalidOperationException("The data stream is neither a model nor particle."); - } - } - - private void VerifyParticle(IAloParticleFile file) - { - foreach (var texture in file.Content.Textures) - { - GuardedVerify(() => VerifyTextureExists(file, texture), - e => e is ArgumentException, - _ => - { - var particlePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); - AddError(VerificationError.Create( - VerifierChain, - VerifierErrorCodes.InvalidTexture, - $"Invalid texture file name '{texture}' in particle '{particlePath}'", - VerificationSeverity.Error, - [particlePath], texture)); - }); - } - - var fileName = FileSystem.Path.GetFileNameWithoutExtension(file.FilePath.AsSpan()); - var name = file.Content.Name.AsSpan(); - - if (!fileName.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - var particlePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); - AddError(VerificationError.Create( - VerifierChain, - VerifierErrorCodes.InvalidParticleName, - $"The particle name '{file.Content.Name}' does not match file name '{particlePath}'", - VerificationSeverity.Error, - particlePath)); - } - - } - - private void VerifyModel(IAloModelFile file, Queue workingQueue) - { - foreach (var texture in file.Content.Textures) - { - GuardedVerify(() => VerifyTextureExists(file, texture), - e => e is ArgumentException, - _ => - { - var modelFilePath = - FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); - AddError(VerificationError.Create( - VerifierChain, - VerifierErrorCodes.InvalidTexture, - $"Invalid texture file name '{texture}' in model '{modelFilePath}'", - VerificationSeverity.Error, - [modelFilePath], texture)); - }); - } - - foreach (var shader in file.Content.Shaders) - { - GuardedVerify(() => VerifyShaderExists(file, shader), - e => e is ArgumentException, - _ => - { - var modelFilePath = - FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); - AddError(VerificationError.Create( - VerifierChain, - VerifierErrorCodes.InvalidShader, - $"Invalid texture file name '{shader}' in model '{modelFilePath}'", - VerificationSeverity.Error, - [modelFilePath], - shader)); - }); - } - - - foreach (var proxy in file.Content.Proxies) - { - GuardedVerify(() => VerifyProxyExists(file, proxy, workingQueue), - e => e is ArgumentException, - _ => - { - var modelFilePath = FileSystem.Path - .GetGameStrippedPath(Repository.Path.AsSpan(), file.FilePath.AsSpan()).ToString(); - AddError(VerificationError.Create( - VerifierChain, - VerifierErrorCodes.InvalidProxy, - $"Invalid proxy file name '{proxy}' in model '{modelFilePath}'", - VerificationSeverity.Error, - [modelFilePath], - proxy)); - }); - } - } - - private void VerifyTextureExists(IPetroglyphFileHolder model, string texture) - { - if (texture == "None") - return; - - if (!Repository.TextureRepository.FileExists(texture)) - { - var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), model.FilePath.AsSpan()) - .ToString(); - var message = $"{modelFilePath} references missing texture: {texture}"; - var error = VerificationError.Create(VerifierChain, VerifierErrorCodes.ModelMissingTexture, message, VerificationSeverity.Error, [modelFilePath], texture); - AddError(error); - } - } - - private void VerifyProxyExists(IPetroglyphFileHolder model, string proxy, Queue workingQueue) - { - var proxyName = ProxyNameWithoutAlt(proxy); - - if (!Repository.ModelRepository.FileExists(BuildModelPath(proxyName))) - { - var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), model.FilePath.AsSpan()).ToString(); - var message = $"{modelFilePath} references missing proxy particle: {proxyName}"; - var error = VerificationError.Create(VerifierChain, - VerifierErrorCodes.ModelMissingProxy, message, VerificationSeverity.Error, [modelFilePath], proxyName); - AddError(error); - } - else - workingQueue.Enqueue(proxyName); - } - - private string BuildModelPath(string fileName) - { - return FileSystem.Path.Combine("DATA\\ART\\MODELS", fileName); - } - - private void VerifyShaderExists(IPetroglyphFileHolder data, string shader) - { - if (shader is "alDefault.fx" or "alDefault.fxo") - return; - - if (!Repository.EffectsRepository.FileExists(shader)) - { - var modelFilePath = FileSystem.Path.GetGameStrippedPath(Repository.Path.AsSpan(), data.FilePath.AsSpan()).ToString(); - var message = $"{modelFilePath} references missing shader effect: {shader}"; - var error = VerificationError.Create(VerifierChain, VerifierErrorCodes.ModelMissingShader, message, VerificationSeverity.Error, [modelFilePath], shader); - AddError(error); - } - } - - private static string ProxyNameWithoutAlt(string proxy) - { - var proxyName = proxy.AsSpan(); - - var altSpan = ProxyAltIdentifier.AsSpan(); - - var altIndex = proxyName.LastIndexOf(altSpan); - - if (altIndex == -1) - return proxy; - - while (altIndex != -1) - { - proxyName = proxyName.Slice(0, altIndex); - altIndex = proxyName.LastIndexOf(altSpan); - } - - return proxyName.ToString(); - } -} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/Commons/TextureVeifier.cs b/src/ModVerify/Verifiers/Commons/TextureVeifier.cs new file mode 100644 index 0000000..7a657d5 --- /dev/null +++ b/src/ModVerify/Verifiers/Commons/TextureVeifier.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using AET.ModVerify.Reporting; +using AET.ModVerify.Settings; +using Microsoft.Extensions.DependencyInjection; +using PG.StarWarsGame.Engine.Database; + +namespace AET.ModVerify.Verifiers.Commons; + +public sealed class TextureVeifier( + IGameVerifierInfo? parent, + IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) + : GameVerifier(parent, gameDatabase, settings, serviceProvider) +{ + private readonly IAlreadyVerifiedCache? _cache = serviceProvider.GetService(); + + public override void Verify(string texturePath, IReadOnlyCollection contextInfo, CancellationToken token) + { + Verify(texturePath.AsSpan(), contextInfo, token); + } + + public void Verify(ReadOnlySpan textureName, IReadOnlyCollection contextInfo, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + + if (_cache?.TryAddEntry(textureName) == false) + return; + + if (Repository.TextureRepository.FileExists(textureName, false, out var tooLongPath)) + return; + + var pathString = textureName.ToString(); + + if (tooLongPath) + { + VerificationError.Create(VerifierChain, VerifierErrorCodes.FilePathTooLong, + $"Could not find texture '{pathString}' because the engine resolved a path that is too long.", + VerificationSeverity.Error, contextInfo, pathString); + return; + } + + + var messageBuilder = new StringBuilder($"Could not find texture '{pathString}'"); + if (contextInfo.Count > 0) + { + messageBuilder.Append(" for context: ["); + messageBuilder.Append(string.Join("-->", contextInfo)); + messageBuilder.Append(']'); + } + + messageBuilder.Append('.'); + + VerificationError.Create(VerifierChain, VerifierErrorCodes.FileNotFound, + messageBuilder.ToString(), + VerificationSeverity.Error, contextInfo, pathString); + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifier.cs b/src/ModVerify/Verifiers/GameVerifier.cs index d2c737d..8614e70 100644 --- a/src/ModVerify/Verifiers/GameVerifier.cs +++ b/src/ModVerify/Verifiers/GameVerifier.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using AET.ModVerify.Settings; using PG.StarWarsGame.Engine.Database; @@ -12,7 +13,7 @@ public abstract class GameVerifier( IServiceProvider serviceProvider) : GameVerifierBase(parent, gameDatabase, settings, serviceProvider) where T : notnull { - public abstract void Verify(T toVerify, CancellationToken token); + public abstract void Verify(T toVerify, IReadOnlyCollection contextInfo, CancellationToken token); } public abstract class GameVerifier( diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 34eaeff..a2b228a 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -21,7 +21,6 @@ public abstract class GameVerifierBase : IGameVerifierInfo protected readonly IServiceProvider Services; protected readonly GameVerifySettings Settings; - public IReadOnlyCollection VerifyErrors => [.. _verifyErrors.Keys]; public virtual string FriendlyName => GetType().Name; @@ -64,7 +63,6 @@ protected void AddError(VerificationError error) } } - protected void GuardedVerify(Action action, Predicate exceptionFilter, Action exceptionHandler) { try diff --git a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs b/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs similarity index 69% rename from src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs rename to src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs index 15ee468..42e0b0d 100644 --- a/src/ModVerify/Verifiers/ReferencedTexturesVerifier.GUI.cs +++ b/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs @@ -1,24 +1,52 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; +using AET.ModVerify.Settings; +using AET.ModVerify.Verifiers.Commons; +using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Files.MTD.Binary; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; -namespace AET.ModVerify.Verifiers; +namespace AET.ModVerify.Verifiers.GuiDialogs; -sealed partial class ReferencedTexturesVerifier +sealed class GuiDialogsVerifier : GameVerifier { internal const string DefaultComponentIdentifier = "<>"; private static readonly GuiComponentType[] GuiComponentTypes = Enum.GetValues(typeof(GuiComponentType)).OfType().ToArray(); - private void VerifyGuiTextures(ISet visitedTextures) - { - VerifyMegaTexturesExist(); + private readonly IAlreadyVerifiedCache? _cache; + private readonly TextureVeifier _textureVerifier; + public GuiDialogsVerifier(IGameDatabase gameDatabase, + GameVerifySettings settings, + IServiceProvider serviceProvider) : base(null, gameDatabase, settings, serviceProvider) + { + _cache = serviceProvider.GetService(); + _textureVerifier = new TextureVeifier(this, gameDatabase, settings, serviceProvider); + } + + public override void Verify(CancellationToken token) + { + try + { + _textureVerifier.Error += OnTextureError; + VerifyMegaTexturesExist(token); + VerifyGuiTextures(); + } + finally + { + _textureVerifier.Error -= OnTextureError; + } + } + + private void VerifyGuiTextures() + { var components = new List { DefaultComponentIdentifier @@ -26,30 +54,24 @@ private void VerifyGuiTextures(ISet visitedTextures) components.AddRange(Database.GuiDialogManager.Components); foreach (var component in components) - VerifyGuiComponentTexturesExist(component, visitedTextures); + VerifyGuiComponentTexturesExist(component); } - private void VerifyMegaTexturesExist() + private void VerifyMegaTexturesExist(CancellationToken token) { var megaTextureName = Database.GuiDialogManager.GuiDialogsXml?.TextureData.MegaTexture; if (Database.GuiDialogManager.MtdFile is null) { var mtdFileName = megaTextureName ?? "<>"; - VerificationError.Create(VerifierChain, MtdNotFound, $"MtdFile '{mtdFileName}.mtd' could not be found", + VerificationError.Create(VerifierChain, VerifierErrorCodes.FileNotFound, $"MtdFile '{mtdFileName}.mtd' could not be found", VerificationSeverity.Critical, mtdFileName); } - if (megaTextureName is not null) { var megaTextureFileName = $"{megaTextureName}.tga"; - - if (!Repository.TextureRepository.FileExists(megaTextureFileName)) - { - VerificationError.Create(VerifierChain, TexutreNotFound, $"Could not find texture '{megaTextureFileName}' could not be found", - VerificationSeverity.Error, megaTextureFileName); - } + _textureVerifier.Verify(megaTextureFileName, ["GUIDIALOGS.XML"], token); } @@ -57,16 +79,11 @@ private void VerifyMegaTexturesExist() if (compressedMegaTextureName is not null) { var compressedMegaTextureFieName = $"{compressedMegaTextureName}.dds"; - - if (!Repository.TextureRepository.FileExists(compressedMegaTextureFieName)) - { - VerificationError.Create(VerifierChain, TexutreNotFound, $"Could not find texture '{compressedMegaTextureFieName}' could not be found", - VerificationSeverity.Error, compressedMegaTextureFieName); - } + _textureVerifier.Verify(compressedMegaTextureFieName, ["GUIDIALOGS.XML"], token); } } - private void VerifyGuiComponentTexturesExist(string component, ISet visitedTextures) + private void VerifyGuiComponentTexturesExist(string component) { var middleButtonInRepoMode = false; @@ -82,7 +99,7 @@ private void VerifyGuiComponentTexturesExist(string component, ISet visi if (!entriesForComponent.TryGetValue(componentType, out var texture)) continue; - if (!visitedTextures.Add(texture.Texture)) + if (_cache?.TryAddEntry(texture.Texture) == false) { // If we are in a special case we don't want to skip if (!middleButtonInRepoMode && @@ -102,7 +119,7 @@ componentType is not GuiComponentType.Scanlines && if (origin == GuiTextureOrigin.MegaTexture && texture.Texture.Length > MtdFileConstants.MaxFileNameSize) { - AddError(VerificationError.Create(VerifierChain, FileNameTooLong, + AddError(VerificationError.Create(VerifierChain, VerifierErrorCodes.FilePathTooLong, $"The filename is too long. Max length is {MtdFileConstants.MaxFileNameSize} characters.", VerificationSeverity.Error, texture.Texture)); } @@ -113,7 +130,7 @@ componentType is not GuiComponentType.Scanlines && if (texture.Texture.Length > PGConstants.MaxMegEntryPathLength) message += " The file name is too long."; - AddError(VerificationError.Create(VerifierChain, TexutreNotFound, + AddError(VerificationError.Create(VerifierChain, VerifierErrorCodes.FileNotFound, message, VerificationSeverity.Error, [component, origin.ToString()], texture.Texture)); } @@ -140,4 +157,9 @@ private IReadOnlyDictionary GetTextureE } return Database.GuiDialogManager.GetTextureEntries(component, out defined); } + + private void OnTextureError(object sender, VerificationErrorEventArgs e) + { + AddError(e.Error); + } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/IAlreadyVerifiedCache.cs b/src/ModVerify/Verifiers/IAlreadyVerifiedCache.cs new file mode 100644 index 0000000..1fe8ad5 --- /dev/null +++ b/src/ModVerify/Verifiers/IAlreadyVerifiedCache.cs @@ -0,0 +1,11 @@ +using System; +using PG.Commons.Hashing; + +namespace AET.ModVerify.Verifiers; + +public interface IAlreadyVerifiedCache +{ + public bool TryAddEntry(string entry); + public bool TryAddEntry(ReadOnlySpan entry); + public bool TryAddEntry(Crc32 checksum); +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs index 502fa85..778aeda 100644 --- a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs +++ b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs @@ -22,11 +22,15 @@ public override void Verify(CancellationToken token) .SelectMany(x => x.Models) .Concat(FocHardcodedConstants.HardcodedModels); - var inner = new SharedReferencedModelsVerifier(this, models, Database, Settings, Services); + + var inner = new SingleModelVerifier(this, Database, Settings, Services); try { inner.Error += OnModelError; - inner.Verify(token); + foreach (var model in models) + { + inner.Verify(model, [], token); + } } finally { diff --git a/src/ModVerify/Verifiers/VerifierErrorCodes.cs b/src/ModVerify/Verifiers/VerifierErrorCodes.cs index 10b5414..68b9e92 100644 --- a/src/ModVerify/Verifiers/VerifierErrorCodes.cs +++ b/src/ModVerify/Verifiers/VerifierErrorCodes.cs @@ -10,28 +10,25 @@ public static class VerifierErrorCodes public const string GenericExceptionErrorCode = "MV00"; + public const string FileCorrupt = "ENG00"; + + + public const string FileNotFound = "FILE00"; + public const string FilePathTooLong = "FILE01"; + public const string InvalidFilePath = "FILE02"; + public const string DuplicateFound = "DUP00"; public const string SampleNotFound = "WAV00"; - public const string FilePathTooLong = "WAV01"; public const string SampleNotPCM = "WAV02"; public const string SampleNotMono = "WAV03"; public const string InvalidSampleRate = "WAV04"; public const string InvalidBitsPerSeconds = "WAV05"; - public const string ModelNotFound = "ALO00"; - public const string ModelBroken = "ALO01"; - public const string ModelMissingTexture = "ALO02"; - public const string ModelMissingProxy = "ALO03"; - public const string ModelMissingShader = "ALO04"; - public const string InvalidTexture = "ALO05"; - public const string InvalidShader = "ALO06"; - public const string InvalidProxy = "ALO07"; public const string InvalidParticleName = "ALO08"; public const string GenericXmlError = "XML00"; public const string EmptyXmlRoot = "XML01"; - public const string MissingXmlFile = "XML02"; public const string InvalidXmlValue = "XML03"; public const string MalformedXmlValue = "XML04"; public const string MissingXmlAttribute = "XML05"; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index 55e598b..5986d3d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -158,7 +158,7 @@ GuiComponentType.ButtonMiddleMouseOver or if (textureInfo.ComponentType == GuiComponentType.FrameBackground) { textureOrigin = GuiTextureOrigin.Repository; - return GameRepository.FileExists(textureInfo.Texture); + return GameRepository.TextureRepository.FileExists(textureInfo.Texture); } textureOrigin = default; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs index 838971d..1809a3b 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/FileFoundInfo.cs @@ -8,6 +8,8 @@ internal readonly ref struct FileFoundInfo { public bool FileFound => FilePath != ReadOnlySpan.Empty || InMeg; + public bool PathTooLong { get; init; } + public ReadOnlySpan FilePath { get; } public MegDataEntryReference? MegDataEntryReference { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs index 408c89b..4ca45db 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IRepository.cs @@ -10,6 +10,7 @@ public interface IRepository bool FileExists(string filePath, bool megFileOnly = false); bool FileExists(ReadOnlySpan filePath, bool megFileOnly = false); + bool FileExists(ReadOnlySpan filePath, bool megFileOnly, out bool pathTooLong); Stream? TryOpenFile(string filePath, bool megFileOnly = false); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs index 4de72a1..7a59a98 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; @@ -31,14 +32,20 @@ public bool FileExists(string filePath, bool megFileOnly = false) } public bool FileExists(ReadOnlySpan filePath, bool megFileOnly = false) + { + return FileExists(filePath, megFileOnly, out _); + } + + public bool FileExists(ReadOnlySpan filePath, bool megFileOnly, out bool pathTooLong) { var multiPassSb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); var destinationSb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); - var fileFound = MultiPassAction(filePath, ref multiPassSb, ref destinationSb, megFileOnly); - var result = fileFound.FileFound; + var result = MultiPassAction(filePath, ref multiPassSb, ref destinationSb, megFileOnly); + var fileFound = result.FileFound; + pathTooLong = result.PathTooLong; multiPassSb.Dispose(); destinationSb.Dispose(); - return result; + return fileFound; } private protected abstract FileFoundInfo MultiPassAction( diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs index c69851d..1304a89 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.Files.cs @@ -33,10 +33,16 @@ public bool FileExists(string filePath, bool megFileOnly = false) } public bool FileExists(ReadOnlySpan filePath, bool megFileOnly = false) + { + return FileExists(filePath, megFileOnly, out _); + } + + public bool FileExists(ReadOnlySpan filePath, bool megFileOnly, out bool pathTooLong) { var sb = new ValueStringBuilder(stackalloc char[PGConstants.MaxMegEntryPathLength]); var fileFound = FindFile(filePath, ref sb, megFileOnly); var fileExists = fileFound.FileFound; + pathTooLong = fileFound.PathTooLong; sb.Dispose(); return fileExists; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs index 2e7aa80..03ff446 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/TextureRepository.cs @@ -3,7 +3,8 @@ namespace PG.StarWarsGame.Engine.IO.Repositories; -internal class TextureRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : MultiPassRepository(baseRepository, serviceProvider) +internal class TextureRepository(GameRepository baseRepository, IServiceProvider serviceProvider) : + MultiPassRepository(baseRepository, serviceProvider) { private static readonly string DdsExtension = ".dds"; private static readonly string TexturePath = "DATA\\ART\\TEXTURES\\"; @@ -15,7 +16,7 @@ private protected override FileFoundInfo MultiPassAction( bool megFileOnly) { if (filePath.Length > PGConstants.MaxTextureFileName) - return default; + return new FileFoundInfo { PathTooLong = true }; reusableStringBuilder.Append(filePath); @@ -38,7 +39,6 @@ private FileFoundInfo FindTexture(ref ValueStringBuilder multiPassStringBuilder, if (fileInfo.FileFound) return fileInfo; - // Only PG knows why they only search for backslash and not also forward slash, // when in fact in other methods, they handle both. var separatorIndex = multiPassStringBuilder.AsSpan().LastIndexOf('\\'); @@ -47,6 +47,9 @@ private FileFoundInfo FindTexture(ref ValueStringBuilder multiPassStringBuilder, multiPassStringBuilder.Insert(0, TexturePath); + if (multiPassStringBuilder.AsSpan().Length > PGConstants.MaxTextureFileName) + return new FileFoundInfo { PathTooLong = true }; + return BaseRepository.FindFile(multiPassStringBuilder.AsSpan(), ref pathStringBuilder); } From 6abb5abe5cb927cb01a509a1dcbb753f0435bb91 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sun, 30 Mar 2025 15:38:53 +0200 Subject: [PATCH 29/34] fix false positive --- src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs index 4a1f852..0034034 100644 --- a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs +++ b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs @@ -136,7 +136,7 @@ private void VerifyCommandBarShellsGroups() VerificationSeverity.Error, "GameCommandBar")); - if (shellGroups.Count >= 1) + if (shellGroups.Count > 1) AddError(VerificationError.Create(VerifierChain, CommandBarManyShellsGroup, $"Found more than one Shells CommandBarGroup. Mind that group names are case-sensitive. Correct name is '{CommandBarConstants.ShellGroupName}'", From f7053a6bb3667d5e48bde5bf4889fe8ba0708002 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 31 Mar 2025 19:44:56 +0200 Subject: [PATCH 30/34] start mod verify reporting --- src/ModVerify.CliApp/ModVerifyApp.cs | 16 ++- .../Pipeline/GameVerifierPipelineStep.cs | 118 ++++++++++++++++-- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 41 ++++-- src/ModVerify/Verifiers/GameVerifierBase.cs | 8 ++ 4 files changed, 161 insertions(+), 22 deletions(-) diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApp.cs index f8ff9d8..022da4c 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApp.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using AET.ModVerify.Pipeline; +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; namespace AET.ModVerifyTool; @@ -49,11 +50,12 @@ public async Task RunApplication() private async Task> Verify(VerifyGameInstallationData installData) { - var verifyPipeline = new GameVerifyPipeline( + using var verifyPipeline = new GameVerifyPipeline( installData.EngineType, installData.GameLocations, settings.VerifyPipelineSettings, settings.GlobalReportSettings, + new VerifyConsoleProgressReporter(), services); try @@ -104,4 +106,16 @@ private async Task WriteBaseline(IEnumerable errors, string b using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); await baseline.ToJsonAsync(fs); } +} + +public class VerifyConsoleProgressReporter : IVerifyProgressReporter +{ + public void Report(string progressText, double progress, ProgressType type, VerifyProgressInfo detailedProgress) + { + if (type != VerifyProgress.ProgressType) + return; + + + Console.WriteLine(progressText); + } } \ No newline at end of file diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index 52ba258..dad3d49 100644 --- a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -1,25 +1,125 @@ -using System; -using System.Threading; -using AET.ModVerify.Verifiers; +using AET.ModVerify.Verifiers; +using AnakinRaW.CommonUtilities.SimplePipeline; +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; using AnakinRaW.CommonUtilities.SimplePipeline.Steps; using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Threading; namespace AET.ModVerify.Pipeline; -public sealed class GameVerifierPipelineStep(GameVerifier verifier, IServiceProvider serviceProvider) : PipelineStep(serviceProvider) +public sealed class GameVerifierPipelineStep( + GameVerifier verifier, + IStepProgressReporter progressReporter, + IServiceProvider serviceProvider) + : PipelineStep(serviceProvider), IProgressStep { - private readonly GameVerifier _gameVerifier = verifier ?? throw new ArgumentNullException(nameof(verifier)); + internal GameVerifier GameVerifier { get; } = verifier ?? throw new ArgumentNullException(nameof(verifier)); + + public ProgressType Type => VerifyProgress.ProgressType; + + public IStepProgressReporter ProgressReporter { get; } = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); + + public long Size => 1; protected override void RunCore(CancellationToken token) { - Logger?.LogDebug($"Running verifier '{_gameVerifier.FriendlyName}'..."); + Logger?.LogDebug($"Running verifier '{GameVerifier.FriendlyName}'..."); try { - _gameVerifier.Verify(token); + ProgressReporter.Report(this, 0.0); + GameVerifier.Progress += OnVerifyProgress; + GameVerifier.Verify(token); } finally { - Logger?.LogDebug($"Finished verifier '{_gameVerifier.FriendlyName}'"); + ProgressReporter.Report(this, 1.0); + GameVerifier.Progress += OnVerifyProgress; + Logger?.LogDebug($"Finished verifier '{GameVerifier.FriendlyName}'"); } } -} \ No newline at end of file + + private void OnVerifyProgress(object _, VerifyProgressEventArgs e) + { + ProgressReporter.Report(this, e.Progress); + } +} + +public sealed class VerifyProgressEventArgs : ProgressEventArgs +{ + public VerifyProgressEventArgs(string progressText, double progress) + : base(progressText, progress, VerifyProgress.ProgressType) + { + } +} + +public static class VerifyProgress +{ + public static readonly ProgressType ProgressType = new() + { + Id = "Verify", + DisplayName = "Verify" + }; +} + + + +public interface IVerifyProgressReporter : IProgressReporter +{ + +} + +internal class VerifyProgressReporter : IVerifyProgressReporter +{ + public void Report(string progressText, double progress, ProgressType type, VerifyProgressInfo detailedProgress) + { + throw new NotImplementedException(); + } +} + + +internal class AggregatedVerifyProgressReporter( + IVerifyProgressReporter progressReporter, + IEnumerable steps) + : AggregatedProgressReporter(progressReporter, steps) +{ + protected override ProgressType Type => VerifyProgress.ProgressType; + + protected override string GetProgressText(GameVerifierPipelineStep step) + { + return step.GameVerifier.FriendlyName; + } + + protected override double CalculateAggregatedProgress(GameVerifierPipelineStep task, double progress, out VerifyProgressInfo progressInfo) + { + progressInfo = default; + return 0; + } +} + +public struct VerifyProgressInfo +{ + public int TotalVerifiers { get; internal set; } +} + + +internal class LateInitDelegatingProgressReporter : IStepProgressReporter, IDisposable +{ + private IStepProgressReporter? _innerReporter; + + public void Report(IProgressStep step, double progress) + { + _innerReporter?.Report(step, progress); + } + + public void Initialize(IStepProgressReporter progressReporter) + { + _innerReporter = progressReporter; + } + + public void Dispose() + { + _innerReporter = null; + } +} diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index 59afca6..5414869 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Settings; using AET.ModVerify.Settings; using AET.ModVerify.Utilities; @@ -14,12 +9,18 @@ using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Database; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace AET.ModVerify.Pipeline; public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipeline.Pipeline { - private readonly List _verificationSteps = new(); + private readonly List _verifiers = new(); + private readonly List _verificationSteps = new(); private readonly GameEngineType _targetType; private readonly GameLocations _gameLocations; private readonly ParallelStepRunner _verifyRunner; @@ -27,6 +28,9 @@ public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipelin private readonly VerifyPipelineSettings _pipelineSettings; private readonly GlobalVerifyReportSettings _reportSettings; + private readonly IVerifyProgressReporter _progressReporter; + private readonly LateInitDelegatingProgressReporter _verifyDelegatingProgressReporter = new(); + protected override bool FailFast { get; } public IReadOnlyCollection FilteredErrors { get; private set; } = []; @@ -36,12 +40,14 @@ public GameVerifyPipeline( GameLocations gameLocations, VerifyPipelineSettings pipelineSettings, GlobalVerifyReportSettings reportSettings, + IVerifyProgressReporter progressReporter, IServiceProvider serviceProvider) : base(serviceProvider) { _targetType = targetType; _gameLocations = gameLocations ?? throw new ArgumentNullException(nameof(gameLocations)); _pipelineSettings = pipelineSettings ?? throw new ArgumentNullException(nameof(pipelineSettings)); _reportSettings = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings)); + _progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); if (pipelineSettings.ParallelVerifiers is < 0 or > 64) throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", nameof(pipelineSettings)); @@ -53,7 +59,7 @@ public GameVerifyPipeline( protected override Task PrepareCoreAsync() { - _verificationSteps.Clear(); + _verifiers.Clear(); return Task.FromResult(true); } @@ -70,12 +76,15 @@ protected override async Task RunCoreAsync(CancellationToken token) }; var database = await databaseService.InitializeGameAsync(initOptions, token); - + AddStep(new GameEngineErrorCollector(initializationErrorListener, database, _pipelineSettings.GameVerifySettings, ServiceProvider)); foreach (var gameVerificationStep in CreateVerificationSteps(database)) AddStep(gameVerificationStep); + var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); + _verifyDelegatingProgressReporter.Initialize(aggregatedVerifyProgressReporter); + try { Logger?.LogInformation("Running game verifiers..."); @@ -88,7 +97,13 @@ protected override async Task RunCoreAsync(CancellationToken token) Logger?.LogDebug("Game verifiers finished."); } - FilteredErrors = GetReportableErrors(_verificationSteps.SelectMany(s => s.VerifyErrors)).ToList(); + FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); + } + + protected override void DisposeResources() + { + base.DisposeResources(); + _verifyDelegatingProgressReporter.Dispose(); } protected override void OnError(object sender, StepRunnerErrorEventArgs e) @@ -108,8 +123,10 @@ private IEnumerable CreateVerificationSteps(IGameDatabase database private void AddStep(GameVerifier verifier) { - _verifyRunner.AddStep(new GameVerifierPipelineStep(verifier, ServiceProvider)); - _verificationSteps.Add(verifier); + var verificationStep = new GameVerifierPipelineStep(verifier, _verifyDelegatingProgressReporter, ServiceProvider); + _verifyRunner.AddStep(verificationStep); + _verificationSteps.Add(verificationStep); + _verifiers.Add(verifier); } private IEnumerable GetReportableErrors(IEnumerable errors) diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index a2b228a..1019f7a 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -7,6 +7,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; +using AET.ModVerify.Pipeline; namespace AET.ModVerify.Verifiers; @@ -14,6 +15,8 @@ public abstract class GameVerifierBase : IGameVerifierInfo { public event EventHandler? Error; + public event EventHandler? Progress; + private readonly IGameDatabase _gameDatabase; private readonly ConcurrentDictionary _verifyErrors = new(); @@ -75,6 +78,11 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter } } + protected void OnProgress(string message, double progress) + { + Progress?.Invoke(this, new VerifyProgressEventArgs(message, progress)); + } + private IReadOnlyList CreateVerifierChain() { var verifierChain = new List { this }; From 76aebc13625dd567c78c0124fac4a744122869fb Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Wed, 2 Apr 2025 19:58:31 +0200 Subject: [PATCH 31/34] status reporting --- .../ModSelectors/ConsoleModSelector.cs | 1 + .../ModSelectors/SettingsBasedModSelector.cs | 4 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 6 +- src/ModVerify.CliApp/ModVerifyApp.cs | 84 ++++++++---- .../CommandLine/BaseModVerifyOptions.cs | 5 + src/ModVerify.CliApp/Program.cs | 48 ++++--- .../Properties/launchSettings.json | 2 +- .../EngineInitializeProgressReporter.cs | 30 +++++ .../VerifyConsoleProgressReporter.cs | 79 +++++++++++ src/ModVerify.CliApp/SettingsBuilder.cs | 2 + ...ta.cs => VerifyInstallationInformation.cs} | 2 +- src/ModVerify/ModVerify.csproj | 4 +- .../Pipeline/DefaultGameVerifiersProvider.cs | 4 +- .../Pipeline/GameVerifierPipelineStep.cs | 107 +++++++-------- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 69 +++++----- .../Pipeline/IGameVerifiersProvider.cs | 4 +- ...s => ConcurrentGameEngineErrorReporter.cs} | 2 +- ...ction.cs => IGameEngineErrorCollection.cs} | 2 +- .../Engine/GameAssertErrorReporter.cs | 1 + src/ModVerify/Utilities/Extensions.cs | 20 +++ src/ModVerify/Verifiers/AudioFilesVerifier.cs | 11 +- .../CommandBar/CommandBarVerifier.Base.cs | 14 +- .../Verifiers/Commons/ModelVerifier.cs | 10 +- .../Verifiers/Commons/TextureVeifier.cs | 8 +- .../Verifiers/DuplicateNameFinder.cs | 20 +-- .../Verifiers/GameEngineErrorCollector.cs | 10 +- src/ModVerify/Verifiers/GameVerifier.cs | 10 +- src/ModVerify/Verifiers/GameVerifierBase.cs | 23 ++-- .../GuiDialogs/GuiDialogsVerifier.cs | 21 ++- .../Verifiers/ReferencedModelsVerifier.cs | 22 +-- .../Audio/Sfx/ISfxEventGameManager.cs | 4 +- .../Audio/Sfx/SfxEventGameManager.cs | 3 +- .../CommandBar/CommandBarGameManager.cs | 3 +- .../Components/CommandBarBaseComponent.cs | 2 +- .../CommandBar/ICommandBarGameManager.cs | 1 - .../Database/GameDatabaseService.cs | 24 ---- .../Database/GameInitializationOptions.cs | 14 -- .../Database/GameInitializer.cs | 122 ----------------- .../Database/IGameDatabaseService.cs | 9 -- .../ErrorReporting/EngineAssertKind.cs | 3 +- ...Reporter.cs => GameEngineErrorReporter.cs} | 2 +- ...r.cs => GameEngineErrorReporterWrapper.cs} | 12 +- ...eporter.cs => IGameEngineErrorReporter.cs} | 2 +- .../GameConstants/GameConstants.cs | 3 +- .../GameDatabase.cs => GameEngine.cs} | 6 +- .../PG.StarWarsGame.Engine/GameEngineType.cs | 4 +- .../{Database => }/GameManagerBase.cs | 8 +- .../GameObjects/GameObjectTypeGameManager.cs | 3 +- .../GameObjects/IGameObjectTypeGameManager.cs | 4 +- .../GuiDialog/GuiDialogGameManager.cs | 3 +- .../{Database => }/IGameManager.cs | 2 +- .../IO/GameRepositoryFactory.cs | 2 +- .../IO/IGameRepositoryFactory.cs | 2 +- .../IO/Repositories/FocGameRepository.cs | 2 +- .../IO/Repositories/GameRepository.cs | 8 +- .../IO/Utilities/FileInfoGlobbingWrapper.cs | 2 +- .../IPetroglyphStarWarsGameEngineService.cs | 17 +++ ...GameDatabase.cs => IStarWarsGameEngine.cs} | 14 +- .../PG.StarWarsGame.Engine.csproj | 5 +- .../PetroglyphEngineServiceContribution.cs | 3 +- .../PetroglyphStarWarsGameEngineService.cs | 126 ++++++++++++++++++ .../Rendering/Font/FontManager.cs | 5 +- .../Rendering/PGRender.cs | 2 +- 63 files changed, 594 insertions(+), 453 deletions(-) create mode 100644 src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs create mode 100644 src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs rename src/ModVerify.CliApp/{VerifyGameInstallationData.cs => VerifyInstallationInformation.cs} (94%) rename src/ModVerify/Reporting/{ConcurrentGameGameErrorReporter.cs => ConcurrentGameEngineErrorReporter.cs} (89%) rename src/ModVerify/Reporting/{IDatabaseErrorCollection.cs => IGameEngineErrorCollection.cs} (86%) create mode 100644 src/ModVerify/Utilities/Extensions.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs delete mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/{GameErrorReporter.cs => GameEngineErrorReporter.cs} (77%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/{GameErrorReporterWrapper.cs => GameEngineErrorReporterWrapper.cs} (64%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/{IGameErrorReporter.cs => IGameEngineErrorReporter.cs} (80%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database/GameDatabase.cs => GameEngine.cs} (87%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database => }/GameManagerBase.cs (89%) rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database => }/IGameManager.cs (86%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs rename src/PetroglyphTools/PG.StarWarsGame.Engine/{Database/IGameDatabase.cs => IStarWarsGameEngine.cs} (73%) create mode 100644 src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index fcc2838..107bbc0 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -28,6 +28,7 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin var game = finderResult.Game; list.Add(finderResult.Game); + Console.WriteLine(); Console.WriteLine("================="); Console.WriteLine(); Console.WriteLine($"0: {game.Name}"); diff --git a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs index 2c98ba2..12f7861 100644 --- a/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/SettingsBasedModSelector.cs @@ -8,7 +8,7 @@ namespace AET.ModVerifyTool.ModSelectors; internal class SettingsBasedModSelector(IServiceProvider serviceProvider) { - public VerifyGameInstallationData CreateInstallationDataFromSettings(GameInstallationsSettings settings) + public VerifyInstallationInformation CreateInstallationDataFromSettings(GameInstallationsSettings settings) { var gameLocations = new ModSelectorFactory(serviceProvider) .CreateSelector(settings) @@ -20,7 +20,7 @@ public VerifyGameInstallationData CreateInstallationDataFromSettings(GameInstall if (engineType is null) throw new InvalidOperationException("Engine type not specified."); - return new VerifyGameInstallationData + return new VerifyInstallationInformation { EngineType = engineType.Value, GameLocations = gameLocations, diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 3e7b7c5..3399f49 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -14,11 +14,6 @@ alamo,petroglyph,glyphx - - true - true - - @@ -50,6 +45,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/ModVerify.CliApp/ModVerifyApp.cs b/src/ModVerify.CliApp/ModVerifyApp.cs index 022da4c..1270725 100644 --- a/src/ModVerify.CliApp/ModVerifyApp.cs +++ b/src/ModVerify.CliApp/ModVerifyApp.cs @@ -9,9 +9,11 @@ using System.IO; using System.IO.Abstractions; using System.Linq; +using System.Threading; using System.Threading.Tasks; using AET.ModVerify.Pipeline; -using AnakinRaW.CommonUtilities.SimplePipeline.Progress; +using AET.ModVerifyTool.Reporting; +using PG.StarWarsGame.Engine; namespace AET.ModVerifyTool; @@ -48,20 +50,68 @@ public async Task RunApplication() return 0; } - private async Task> Verify(VerifyGameInstallationData installData) + private async Task> Verify(VerifyInstallationInformation installInformation) { + var gameEngineService = services.GetRequiredService(); + var engineErrorReporter = new ConcurrentGameEngineErrorReporter(); + + IStarWarsGameEngine gameEngine; + + try + { + var initProgress = new Progress(); + var initProgressReporter = new EngineInitializeProgressReporter(initProgress); + + try + { + _logger?.LogInformation($"Creating Game Engine '{installInformation.EngineType}'"); + gameEngine = await gameEngineService.InitializeAsync( + installInformation.EngineType, + installInformation.GameLocations, + engineErrorReporter, + initProgress, + false, + CancellationToken.None).ConfigureAwait(false); + _logger?.LogInformation($"Game Engine created"); + } + finally + { + initProgressReporter.Dispose(); + } + } + catch (Exception e) + { + _logger?.LogError(e, $"Creating game engine failed: {e.Message}"); + throw; + } + + var progressReporter = new VerifyConsoleProgressReporter(installInformation.Name); + using var verifyPipeline = new GameVerifyPipeline( - installData.EngineType, - installData.GameLocations, + gameEngine, + engineErrorReporter, settings.VerifyPipelineSettings, settings.GlobalReportSettings, - new VerifyConsoleProgressReporter(), + progressReporter, services); try { - _logger?.LogInformation($"Verifying '{installData.Name}'..."); - await verifyPipeline.RunAsync().ConfigureAwait(false); + try + { + _logger?.LogInformation($"Verifying '{installInformation.Name}'..."); + await verifyPipeline.RunAsync().ConfigureAwait(false); + progressReporter.Report(string.Empty, 1.0); + } + catch + { + progressReporter.ReportError("Verification failed", null); + throw; + } + finally + { + progressReporter.Dispose(); + } } catch (OperationCanceledException) { @@ -72,11 +122,8 @@ private async Task> Verify(VerifyGameInst _logger?.LogError(e, $"Verification failed: {e.Message}"); throw; } - finally - { - _logger?.LogInformation("Finished verification"); - } + _logger?.LogInformation("Finished verification"); return verifyPipeline.FilteredErrors; } @@ -91,8 +138,7 @@ private async Task ReportErrors(IReadOnlyCollection errors) if (errors.Any(x => x.Severity >= settings.AppThrowsOnMinimumSeverity)) throw new GameVerificationException(errors); } - - + private async Task WriteBaseline(IEnumerable errors, string baselineFile) { var baseline = new VerificationBaseline(settings.GlobalReportSettings.MinimumReportSeverity, errors); @@ -106,16 +152,4 @@ private async Task WriteBaseline(IEnumerable errors, string b using var fs = _fileSystem.FileStream.New(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); await baseline.ToJsonAsync(fs); } -} - -public class VerifyConsoleProgressReporter : IVerifyProgressReporter -{ - public void Report(string progressText, double progress, ProgressType type, VerifyProgressInfo detailedProgress) - { - if (type != VerifyProgress.ProgressType) - return; - - - Console.WriteLine(progressText); - } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs index 1997e54..43f9ca1 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs @@ -51,4 +51,9 @@ internal abstract class BaseModVerifyOptions HelpText = "Additional fallback paths, which may contain assets that shall be included when doing the verification. Do not add EaW here. " + "Multiple paths can be separated using the ';' (semicolon) character.")] public IList? AdditionalFallbackPath { get; set; } + + [Option("sequential", Default = false, + HelpText = "When set, game verifiers will run sequentially and not in parallel. " + + "This increases analysis duration but may help in debugging sessions.")] + public bool Sequential { get; set; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 38ee4c9..a82a9ef 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -45,6 +45,8 @@ internal class Program private const string GameInfrastructureNamespace = "PG.StarWarsGame.Infrastructure"; private static readonly string GameVerifierStepNamespace = typeof(GameVerifierPipelineStep).FullName!; + private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; + private static async Task Main(string[] args) { PrintHeader(); @@ -194,29 +196,39 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem loggingBuilder.AddSerilog(fileLogger); var cLogger = new LoggerConfiguration() - .WriteTo.Async(c => - { - c.Console( - logLevel, - theme: AnsiConsoleTheme.Code, - outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}"); - }) - .Filter.ByExcluding(x => + + .WriteTo.Console( + logLevel, + theme: AnsiConsoleTheme.Code, + outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}") + .Filter.ByIncludingOnly(x => { if (!x.Properties.TryGetValue("SourceContext", out var value)) return true; + var source = value.ToString().AsSpan().Trim('\"'); - if (source.StartsWith(EngineParserNamespace.AsSpan())) - return true; - if (source.StartsWith(ParserNamespace.AsSpan())) - return true; - if (source.StartsWith(GameInfrastructureNamespace.AsSpan())) - return true; - if (source.StartsWith(GameVerifierStepNamespace.AsSpan())) + if (source.StartsWith(ModVerifyRootNameSpace.AsSpan())) return true; + return false; }) + //.Filter.ByExcluding(x => + //{ + // if (!x.Properties.TryGetValue("SourceContext", out var value)) + // return true; + // var source = value.ToString().AsSpan().Trim('\"'); + + // if (source.StartsWith(EngineParserNamespace.AsSpan())) + // return true; + // if (source.StartsWith(ParserNamespace.AsSpan())) + // return true; + // if (source.StartsWith(GameInfrastructureNamespace.AsSpan())) + // return true; + // if (source.StartsWith(GameVerifierStepNamespace.AsSpan())) + // return true; + // return false; + //}) .CreateLogger(); loggingBuilder.AddSerilog(cLogger); } @@ -259,11 +271,11 @@ private static void PrintHeader() private static void PrintApplicationFailure() { Console.WriteLine(); - Console.WriteLine("**************"); + Console.WriteLine("********************"); Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine(" App Failure! "); + Console.WriteLine(" ModVerify Failure! "); Console.ResetColor(); - Console.WriteLine("**************"); + Console.WriteLine("********************"); Console.WriteLine(); Console.WriteLine("The application encountered an unexpected error and will terminate now!"); Console.WriteLine(); diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index f5990c3..d5306fa 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --baseline focBaseline.json --offline" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --baseline focBaseline.json --offline --sequential" }, "Interactive Baseline": { "commandName": "Project", diff --git a/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs b/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs new file mode 100644 index 0000000..69413c0 --- /dev/null +++ b/src/ModVerify.CliApp/Reporting/EngineInitializeProgressReporter.cs @@ -0,0 +1,30 @@ +using System; + +namespace AET.ModVerifyTool.Reporting; + +internal sealed class EngineInitializeProgressReporter : IDisposable +{ + private Progress? _progress; + + public EngineInitializeProgressReporter(Progress? progress) + { + if (progress is null) + return; + progress.ProgressChanged += OnProgress; + } + + private void OnProgress(object sender, string e) + { + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine(e); + Console.ResetColor(); + } + + public void Dispose() + { + Console.WriteLine(); + if (_progress is not null) + _progress.ProgressChanged -= OnProgress; + _progress = null; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs new file mode 100644 index 0000000..a6533ca --- /dev/null +++ b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs @@ -0,0 +1,79 @@ +using AET.ModVerify.Pipeline; +using AnakinRaW.CommonUtilities; +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; +using ShellProgressBar; +using System; +using System.Threading; + +namespace AET.ModVerifyTool.Reporting; + +public sealed class VerifyConsoleProgressReporter(string toVerifyName) : DisposableObject, IVerifyProgressReporter +{ + private static readonly ProgressBarOptions ProgressBarOptions = new() + { + BackgroundColor = ConsoleColor.DarkGray, + ForegroundColorError = ConsoleColor.DarkRed, + ProgressCharacter = '─', + WriteQueuedMessage = WriteQueuedMessage, + }; + + private ProgressBar? _progressBar; + + public void ReportError(string message, string? errorLine) + { + var progressBar = EnsureProgressBar(); + progressBar.WriteErrorLine(errorLine); + progressBar.Message = message; + } + + public void Report(string message, double progress) + { + Report(message, progress, VerifyProgress.ProgressType, default); + } + + public void Report(string progressText, double progress, ProgressType type, VerifyProgressInfo detailedProgress) + { + if (type != VerifyProgress.ProgressType) + return; + + var progressBar = EnsureProgressBar(); + + if (progress >= 1.0) + progressBar.Message = $"Verified '{toVerifyName}'"; + + var cpb = progressBar.AsProgress(); + cpb.Report(progress); + progressBar.WriteLine(progressText); + } + + protected override void DisposeResources() + { + base.DisposeResources(); + _progressBar?.Dispose(); + Console.WriteLine(); + } + + private ProgressBar EnsureProgressBar() + { + return LazyInitializer.EnsureInitialized(ref _progressBar, + () => new ProgressBar(100, $"Verifying '{toVerifyName}'", ProgressBarOptions))!; + } + + private static int WriteQueuedMessage(ConsoleOutLine arg) + { + if (string.IsNullOrEmpty(arg.Line)) + return 0; + + var writer = Console.Out; + Console.ForegroundColor = ConsoleColor.DarkGray; + try + { + writer.WriteLine(arg.Line); + return 1; + } + finally + { + Console.ResetColor(); + } + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index 7102d57..abe1298 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -44,6 +44,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) { VerifyPipelineSettings = new VerifyPipelineSettings { + ParallelVerifiers = verifyOptions.Sequential ? 1 : 4, VerifiersProvider = new DefaultGameVerifiersProvider(), FailFast = verifyOptions.FailFast, GameVerifySettings = new GameVerifySettings @@ -86,6 +87,7 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio { VerifyPipelineSettings = new VerifyPipelineSettings { + ParallelVerifiers = baselineVerb.Sequential ? 1 : 4, GameVerifySettings = new GameVerifySettings { IgnoreAsserts = false, diff --git a/src/ModVerify.CliApp/VerifyGameInstallationData.cs b/src/ModVerify.CliApp/VerifyInstallationInformation.cs similarity index 94% rename from src/ModVerify.CliApp/VerifyGameInstallationData.cs rename to src/ModVerify.CliApp/VerifyInstallationInformation.cs index d873788..66816ab 100644 --- a/src/ModVerify.CliApp/VerifyGameInstallationData.cs +++ b/src/ModVerify.CliApp/VerifyInstallationInformation.cs @@ -3,7 +3,7 @@ namespace AET.ModVerifyTool; -internal sealed class VerifyGameInstallationData +internal sealed class VerifyInstallationInformation { public required string Name { get; init; } diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index a2bb7c0..7f0ed29 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -22,8 +22,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs index e916333..b74296d 100644 --- a/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs +++ b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs @@ -3,14 +3,14 @@ using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; using AET.ModVerify.Verifiers.GuiDialogs; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Pipeline; public sealed class DefaultGameVerifiersProvider : IGameVerifiersProvider { public IEnumerable GetVerifiers( - IGameDatabase database, + IStarWarsGameEngine database, GameVerifySettings settings, IServiceProvider serviceProvider) { diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index dad3d49..e0ab783 100644 --- a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -11,46 +11,46 @@ namespace AET.ModVerify.Pipeline; public sealed class GameVerifierPipelineStep( GameVerifier verifier, - IStepProgressReporter progressReporter, IServiceProvider serviceProvider) - : PipelineStep(serviceProvider), IProgressStep + : PipelineStep(serviceProvider), IProgressStep { + public event EventHandler>? Progress; + internal GameVerifier GameVerifier { get; } = verifier ?? throw new ArgumentNullException(nameof(verifier)); public ProgressType Type => VerifyProgress.ProgressType; - public IStepProgressReporter ProgressReporter { get; } = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); - public long Size => 1; protected override void RunCore(CancellationToken token) { - Logger?.LogDebug($"Running verifier '{GameVerifier.FriendlyName}'..."); try { - ProgressReporter.Report(this, 0.0); + Logger?.LogDebug($"Running verifier '{GameVerifier.FriendlyName}'..."); + ReportProgress(new ProgressEventArgs("Started", 0.0)); + GameVerifier.Progress += OnVerifyProgress; GameVerifier.Verify(token); + + Logger?.LogDebug($"Finished verifier '{GameVerifier.FriendlyName}'"); + ReportProgress(new ProgressEventArgs("Finished", 1.0)); } finally { - ProgressReporter.Report(this, 1.0); GameVerifier.Progress += OnVerifyProgress; - Logger?.LogDebug($"Finished verifier '{GameVerifier.FriendlyName}'"); } } - private void OnVerifyProgress(object _, VerifyProgressEventArgs e) + private void OnVerifyProgress(object _, ProgressEventArgs e) { - ProgressReporter.Report(this, e.Progress); + if (e.Progress > 1.0) + e = new ProgressEventArgs(e.ProgressText, 1.0, e.ProgressInfo); + ReportProgress(e); } -} -public sealed class VerifyProgressEventArgs : ProgressEventArgs -{ - public VerifyProgressEventArgs(string progressText, double progress) - : base(progressText, progress, VerifyProgress.ProgressType) + private void ReportProgress(ProgressEventArgs e) { + Progress?.Invoke(this, e); } } @@ -63,63 +63,58 @@ public static class VerifyProgress }; } - - -public interface IVerifyProgressReporter : IProgressReporter -{ - -} - -internal class VerifyProgressReporter : IVerifyProgressReporter -{ - public void Report(string progressText, double progress, ProgressType type, VerifyProgressInfo detailedProgress) - { - throw new NotImplementedException(); - } -} - +public interface IVerifyProgressReporter : IProgressReporter; internal class AggregatedVerifyProgressReporter( IVerifyProgressReporter progressReporter, IEnumerable steps) : AggregatedProgressReporter(progressReporter, steps) { - protected override ProgressType Type => VerifyProgress.ProgressType; + private readonly object _syncLock = new(); - protected override string GetProgressText(GameVerifierPipelineStep step) - { - return step.GameVerifier.FriendlyName; - } + private readonly HashSet _completedSteps = new(); + + private long _totalProgressSize; - protected override double CalculateAggregatedProgress(GameVerifierPipelineStep task, double progress, out VerifyProgressInfo progressInfo) + protected override string GetProgressText(GameVerifierPipelineStep step, string progressText) { - progressInfo = default; - return 0; + return $"Verifier '{step.GameVerifier.FriendlyName}': {progressText}"; } -} - -public struct VerifyProgressInfo -{ - public int TotalVerifiers { get; internal set; } -} - -internal class LateInitDelegatingProgressReporter : IStepProgressReporter, IDisposable -{ - private IStepProgressReporter? _innerReporter; - - public void Report(IProgressStep step, double progress) + protected override ProgressEventArgs CalculateAggregatedProgress( + GameVerifierPipelineStep step, + ProgressEventArgs progress) { - _innerReporter?.Report(step, progress); + lock (_syncLock) + { + var currentStepProgressSize = (long)(progress.Progress * step.Size); + var completed = currentStepProgressSize >= step.Size; + + var progressInfo = new VerifyProgressInfo + { + TotalVerifiers = TotalStepCount, + }; + + if (!completed || _completedSteps.Add(step)) + _totalProgressSize += (long)(progress.Progress * step.Size); + + return new ProgressEventArgs(progress.ProgressText, GetTotalProgress(), progressInfo); + } } - public void Initialize(IStepProgressReporter progressReporter) - { - _innerReporter = progressReporter; + private double GetTotalProgress() + { + return Math.Min((double)_totalProgressSize / TotalSize, 0.99); } - public void Dispose() + protected override void DisposeResources() { - _innerReporter = null; + base.DisposeResources(); + _completedSteps.Clear(); } } + +public struct VerifyProgressInfo +{ + public int TotalVerifiers { get; internal set; } +} \ No newline at end of file diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index 5414869..92ba883 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Database; using System; using System.Collections.Generic; using System.Linq; @@ -21,38 +20,41 @@ public sealed class GameVerifyPipeline : AnakinRaW.CommonUtilities.SimplePipelin { private readonly List _verifiers = new(); private readonly List _verificationSteps = new(); - private readonly GameEngineType _targetType; - private readonly GameLocations _gameLocations; - private readonly ParallelStepRunner _verifyRunner; + private readonly StepRunnerBase _verifyRunner; + + private readonly IStarWarsGameEngine _gameEngine; + private readonly IGameEngineErrorCollection _engineErrors; private readonly VerifyPipelineSettings _pipelineSettings; private readonly GlobalVerifyReportSettings _reportSettings; private readonly IVerifyProgressReporter _progressReporter; - private readonly LateInitDelegatingProgressReporter _verifyDelegatingProgressReporter = new(); protected override bool FailFast { get; } public IReadOnlyCollection FilteredErrors { get; private set; } = []; public GameVerifyPipeline( - GameEngineType targetType, - GameLocations gameLocations, + IStarWarsGameEngine gameEngine, + IGameEngineErrorCollection engineErrors, VerifyPipelineSettings pipelineSettings, GlobalVerifyReportSettings reportSettings, IVerifyProgressReporter progressReporter, IServiceProvider serviceProvider) : base(serviceProvider) { - _targetType = targetType; - _gameLocations = gameLocations ?? throw new ArgumentNullException(nameof(gameLocations)); + _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); + _engineErrors = engineErrors ?? throw new ArgumentNullException(nameof(gameEngine)); _pipelineSettings = pipelineSettings ?? throw new ArgumentNullException(nameof(pipelineSettings)); _reportSettings = reportSettings ?? throw new ArgumentNullException(nameof(reportSettings)); _progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter)); if (pipelineSettings.ParallelVerifiers is < 0 or > 64) throw new ArgumentException("_pipelineSettings has invalid parallel worker number.", nameof(pipelineSettings)); - - _verifyRunner = new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider); + + if (pipelineSettings.ParallelVerifiers == 1) + _verifyRunner = new SequentialStepRunner(serviceProvider); + else + _verifyRunner = new ParallelStepRunner(pipelineSettings.ParallelVerifiers, serviceProvider); FailFast = pipelineSettings.FailFast; } @@ -60,30 +62,18 @@ public GameVerifyPipeline( protected override Task PrepareCoreAsync() { _verifiers.Clear(); - return Task.FromResult(true); - } - - protected override async Task RunCoreAsync(CancellationToken token) - { - var databaseService = ServiceProvider.GetRequiredService(); - var initializationErrorListener = new ConcurrentGameGameErrorReporter(); - var initOptions = new GameInitializationOptions - { - Locations = _gameLocations, - TargetEngineType = _targetType, - GameErrorReporter = initializationErrorListener + AddStep(new GameEngineErrorCollector(_engineErrors, _gameEngine, _pipelineSettings.GameVerifySettings, ServiceProvider)); - }; - var database = await databaseService.InitializeGameAsync(initOptions, token); - - AddStep(new GameEngineErrorCollector(initializationErrorListener, database, _pipelineSettings.GameVerifySettings, ServiceProvider)); - - foreach (var gameVerificationStep in CreateVerificationSteps(database)) + foreach (var gameVerificationStep in CreateVerificationSteps(_gameEngine)) AddStep(gameVerificationStep); + return Task.FromResult(true); + } + + protected override async Task RunCoreAsync(CancellationToken token) + { var aggregatedVerifyProgressReporter = new AggregatedVerifyProgressReporter(_progressReporter, _verificationSteps); - _verifyDelegatingProgressReporter.Initialize(aggregatedVerifyProgressReporter); try { @@ -93,17 +83,20 @@ protected override async Task RunCoreAsync(CancellationToken token) } finally { + aggregatedVerifyProgressReporter.Dispose(); _verifyRunner.Error -= OnError; Logger?.LogDebug("Game verifiers finished."); } - FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); - } + token.ThrowIfCancellationRequested(); - protected override void DisposeResources() - { - base.DisposeResources(); - _verifyDelegatingProgressReporter.Dispose(); + var failedSteps = _verifyRunner.ExecutedSteps.Where(p => + p.Error != null && !p.Error.IsExceptionType()).ToList(); + + if (failedSteps.Count != 0) + throw new StepFailureException(failedSteps); + + FilteredErrors = GetReportableErrors(_verifiers.SelectMany(s => s.VerifyErrors)).ToList(); } protected override void OnError(object sender, StepRunnerErrorEventArgs e) @@ -116,14 +109,14 @@ protected override void OnError(object sender, StepRunnerErrorEventArgs e) base.OnError(sender, e); } - private IEnumerable CreateVerificationSteps(IGameDatabase database) + private IEnumerable CreateVerificationSteps(IStarWarsGameEngine database) { return _pipelineSettings.VerifiersProvider.GetVerifiers(database, _pipelineSettings.GameVerifySettings, ServiceProvider); } private void AddStep(GameVerifier verifier) { - var verificationStep = new GameVerifierPipelineStep(verifier, _verifyDelegatingProgressReporter, ServiceProvider); + var verificationStep = new GameVerifierPipelineStep(verifier, ServiceProvider); _verifyRunner.AddStep(verificationStep); _verificationSteps.Add(verificationStep); _verifiers.Add(verifier); diff --git a/src/ModVerify/Pipeline/IGameVerifiersProvider.cs b/src/ModVerify/Pipeline/IGameVerifiersProvider.cs index be70bb0..930afa6 100644 --- a/src/ModVerify/Pipeline/IGameVerifiersProvider.cs +++ b/src/ModVerify/Pipeline/IGameVerifiersProvider.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using AET.ModVerify.Settings; using AET.ModVerify.Verifiers; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Pipeline; public interface IGameVerifiersProvider { IEnumerable GetVerifiers( - IGameDatabase database, + IStarWarsGameEngine database, GameVerifySettings settings, IServiceProvider serviceProvider); } \ No newline at end of file diff --git a/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs b/src/ModVerify/Reporting/ConcurrentGameEngineErrorReporter.cs similarity index 89% rename from src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs rename to src/ModVerify/Reporting/ConcurrentGameEngineErrorReporter.cs index c11e62e..3130547 100644 --- a/src/ModVerify/Reporting/ConcurrentGameGameErrorReporter.cs +++ b/src/ModVerify/Reporting/ConcurrentGameEngineErrorReporter.cs @@ -5,7 +5,7 @@ namespace AET.ModVerify.Reporting; -public sealed class ConcurrentGameGameErrorReporter : GameErrorReporter, IDatabaseErrorCollection +public sealed class ConcurrentGameEngineErrorReporter : GameEngineErrorReporter, IGameEngineErrorCollection { private readonly ConcurrentBag _xmlErrors = new(); private readonly ConcurrentBag _initializationErrors = new(); diff --git a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs b/src/ModVerify/Reporting/IGameEngineErrorCollection.cs similarity index 86% rename from src/ModVerify/Reporting/IDatabaseErrorCollection.cs rename to src/ModVerify/Reporting/IGameEngineErrorCollection.cs index 7cbcbb1..14d59d9 100644 --- a/src/ModVerify/Reporting/IDatabaseErrorCollection.cs +++ b/src/ModVerify/Reporting/IGameEngineErrorCollection.cs @@ -3,7 +3,7 @@ namespace AET.ModVerify.Reporting; -public interface IDatabaseErrorCollection +public interface IGameEngineErrorCollection { IEnumerable XmlErrors { get; } IEnumerable InitializationErrors { get; } diff --git a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs index 93587b5..a2a00c5 100644 --- a/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Engine/GameAssertErrorReporter.cs @@ -47,6 +47,7 @@ private static string GetIdFromError(EngineAssertKind assertKind) EngineAssertKind.NullOrEmptyValue => VerifierErrorCodes.AssertValueNullOrEmpty, EngineAssertKind.ValueOutOfRange => VerifierErrorCodes.AssertValueOutOfRange, EngineAssertKind.InvalidValue => VerifierErrorCodes.AssertValueInvalid, + EngineAssertKind.FileNotFound => VerifierErrorCodes.FileNotFound, _ => throw new ArgumentOutOfRangeException(nameof(assertKind), assertKind, null) }; } diff --git a/src/ModVerify/Utilities/Extensions.cs b/src/ModVerify/Utilities/Extensions.cs new file mode 100644 index 0000000..996f474 --- /dev/null +++ b/src/ModVerify/Utilities/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; + +namespace AET.ModVerify.Utilities; + +internal static class Extensions +{ + public static bool IsExceptionType(this Exception error) where T : Exception + { + switch (error) + { + case T _: + return true; + case AggregateException aggregateException: + return aggregateException.InnerExceptions.Any(p => p.IsExceptionType()); + default: + return false; + } + } +} \ No newline at end of file diff --git a/src/ModVerify/Verifiers/AudioFilesVerifier.cs b/src/ModVerify/Verifiers/AudioFilesVerifier.cs index 85ada7a..b6094e7 100644 --- a/src/ModVerify/Verifiers/AudioFilesVerifier.cs +++ b/src/ModVerify/Verifiers/AudioFilesVerifier.cs @@ -13,7 +13,6 @@ using PG.Commons.Hashing; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Audio.Sfx; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Files.MEG.Services.Builder.Normalization; @@ -37,8 +36,8 @@ public class AudioFilesVerifier : GameVerifier private readonly IFileSystem _fileSystem; private readonly IGameLanguageManager _languageManager; - public AudioFilesVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : base(null, gameDatabase, settings, serviceProvider) + public AudioFilesVerifier(IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) + : base(null, gameEngine, settings, serviceProvider) { _hashingService = serviceProvider.GetRequiredService(); _fileSystem = serviceProvider.GetRequiredService(); @@ -46,13 +45,13 @@ public AudioFilesVerifier(IGameDatabase gameDatabase, GameVerifySettings setting .GetLanguageManager(Repository.EngineType); } - public override string FriendlyName => "Verify Audio Files"; + public override string FriendlyName => "Audio Files"; public override void Verify(CancellationToken token) { var visitedSamples = new HashSet(); var languagesToVerify = GetLanguagesToVerify().ToList(); - foreach (var sfxEvent in Database.SfxGameManager.Entries) + foreach (var sfxEvent in GameEngine.SfxGameManager.Entries) { foreach (var codedSample in sfxEvent.AllSamples) { @@ -226,7 +225,7 @@ private IEnumerable GetLanguagesToVerify() case VerifyLocalizationOption.CurrentSystem: return new List { _languageManager.GetLanguagesFromUser() }; case VerifyLocalizationOption.AllInstalled: - return Database.InstalledLanguages; + return GameEngine.InstalledLanguages; case VerifyLocalizationOption.All: return _languageManager.SupportedLanguages; default: diff --git a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs index 0034034..3373f8e 100644 --- a/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs +++ b/src/ModVerify/Verifiers/CommandBar/CommandBarVerifier.Base.cs @@ -4,14 +4,14 @@ using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AnakinRaW.CommonUtilities.Collections; +using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.CommandBar; using PG.StarWarsGame.Engine.CommandBar.Components; -using PG.StarWarsGame.Engine.Database; namespace AET.ModVerify.Verifiers; -public partial class CommandBarVerifier(IGameDatabase gameDatabase, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifier(null, gameDatabase, settings, serviceProvider) +public partial class CommandBarVerifier(IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) + : GameVerifier(null, gameEngine, settings, serviceProvider) { public const string CommandBarNoShellsGroup = "CMDBAR00"; public const string CommandBarManyShellsGroup = "CMDBAR01"; @@ -20,7 +20,7 @@ public partial class CommandBarVerifier(IGameDatabase gameDatabase, GameVerifySe public const string CommandBarUnsupportedComponent = "CMDBAR04"; public const string CommandBarShellNoModel = "CMDBAR05"; - public override string FriendlyName => "CommandBar Verifiers"; + public override string FriendlyName => "CommandBar"; public override void Verify(CancellationToken token) { @@ -50,7 +50,7 @@ private void VerifyCommandBarModel(CommandBarBaseComponent component) return; } - var model = Database.PGRender.LoadModelAndAnimations(shellComponent.ModelPath.AsSpan(), null); + var model = GameEngine.PGRender.LoadModelAndAnimations(shellComponent.ModelPath.AsSpan(), null); if (model is null) { AddError(VerificationError.Create(VerifierChain, @@ -81,7 +81,7 @@ private void VerifyCommandBarComponents() var occupiedComponentIds = SupportedCommandBarComponentData.GetComponentIdsForEngine(Repository.EngineType).Keys .ToDictionary(value => value, _ => false); - foreach (var component in Database.CommandBar.Components) + foreach (var component in GameEngine.CommandBar.Components) { if (!occupiedComponentIds.TryGetValue(component.Id, out var alreadyOccupied)) { @@ -116,7 +116,7 @@ partial class CommandBarVerifier private void VerifyCommandBarShellsGroups() { var shellGroups = new FrugalList(); - foreach (var groupPair in Database.CommandBar.Groups) + foreach (var groupPair in GameEngine.CommandBar.Groups) { if (groupPair.Key == CommandBarConstants.ShellGroupName) { diff --git a/src/ModVerify/Verifiers/Commons/ModelVerifier.cs b/src/ModVerify/Verifiers/Commons/ModelVerifier.cs index f79f436..25704d1 100644 --- a/src/ModVerify/Verifiers/Commons/ModelVerifier.cs +++ b/src/ModVerify/Verifiers/Commons/ModelVerifier.cs @@ -5,7 +5,7 @@ using AET.ModVerify.Settings; using AET.ModVerify.Utilities; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; using PG.StarWarsGame.Files; using PG.StarWarsGame.Files.ALO.Data; using PG.StarWarsGame.Files.ALO.Files; @@ -26,11 +26,11 @@ public sealed class SingleModelVerifier : GameVerifier private readonly IAlreadyVerifiedCache? _cache; public SingleModelVerifier(IGameVerifierInfo? parent, - IGameDatabase database, + IStarWarsGameEngine engine, GameVerifySettings settings, - IServiceProvider serviceProvider) : base(parent, database, settings, serviceProvider) + IServiceProvider serviceProvider) : base(parent, engine, settings, serviceProvider) { - _textureVerifier = new TextureVeifier(this, database, settings, serviceProvider); + _textureVerifier = new TextureVeifier(this, engine, settings, serviceProvider); _cache = serviceProvider.GetService(); } @@ -68,7 +68,7 @@ private void VerifyAlamoFile(string modelPath, IReadOnlyCollection conte { try { - aloFile = Database.PGRender.Load3DAsset(modelPath, true, true); + aloFile = GameEngine.PGRender.Load3DAsset(modelPath, true, true); } catch (BinaryCorruptedException e) { diff --git a/src/ModVerify/Verifiers/Commons/TextureVeifier.cs b/src/ModVerify/Verifiers/Commons/TextureVeifier.cs index 7a657d5..80bb269 100644 --- a/src/ModVerify/Verifiers/Commons/TextureVeifier.cs +++ b/src/ModVerify/Verifiers/Commons/TextureVeifier.cs @@ -5,16 +5,16 @@ using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Verifiers.Commons; public sealed class TextureVeifier( - IGameVerifierInfo? parent, - IGameDatabase gameDatabase, + IGameVerifierInfo? parent, + IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifier(parent, gameDatabase, settings, serviceProvider) + : GameVerifier(parent, gameEngine, settings, serviceProvider) { private readonly IAlreadyVerifiedCache? _cache = serviceProvider.GetService(); diff --git a/src/ModVerify/Verifiers/DuplicateNameFinder.cs b/src/ModVerify/Verifiers/DuplicateNameFinder.cs index 47c2bf8..23ab1b8 100644 --- a/src/ModVerify/Verifiers/DuplicateNameFinder.cs +++ b/src/ModVerify/Verifiers/DuplicateNameFinder.cs @@ -6,7 +6,7 @@ using AET.ModVerify.Settings; using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; using PG.StarWarsGame.Engine.Xml; using PG.StarWarsGame.Files.MTD.Data; using PG.StarWarsGame.Files.MTD.Files; @@ -14,25 +14,25 @@ namespace AET.ModVerify.Verifiers; public sealed class DuplicateNameFinder( - IGameDatabase gameDatabase, + IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifier(null, gameDatabase, settings, serviceProvider) + : GameVerifier(null, gameEngine, settings, serviceProvider) { public override string FriendlyName => "Duplicates"; public override void Verify(CancellationToken token) { - CheckXmlObjectsForDuplicates("GameObject", Database.GameObjectTypeManager); - CheckXmlObjectsForDuplicates("SFXEvent", Database.SfxGameManager); + CheckXmlObjectsForDuplicates("GameObject", GameEngine.GameObjectTypeManager); + CheckXmlObjectsForDuplicates("SFXEvent", GameEngine.SfxGameManager); - if (Database.GuiDialogManager.MtdFile is not null) - CheckMtdForDuplicates(Database.GuiDialogManager.MtdFile); + if (GameEngine.GuiDialogManager.MtdFile is not null) + CheckMtdForDuplicates(GameEngine.GuiDialogManager.MtdFile); - if (Database.CommandBar.MegaTextureFile is not null) + if (GameEngine.CommandBar.MegaTextureFile is not null) { - if (!Database.CommandBar.MegaTextureFile.FilePath.Equals(Database.GuiDialogManager.MtdFile?.FileName)) - CheckMtdForDuplicates(Database.CommandBar.MegaTextureFile); + if (!GameEngine.CommandBar.MegaTextureFile.FilePath.Equals(GameEngine.GuiDialogManager.MtdFile?.FileName)) + CheckMtdForDuplicates(GameEngine.CommandBar.MegaTextureFile); } } diff --git a/src/ModVerify/Verifiers/GameEngineErrorCollector.cs b/src/ModVerify/Verifiers/GameEngineErrorCollector.cs index e165c01..6475ea7 100644 --- a/src/ModVerify/Verifiers/GameEngineErrorCollector.cs +++ b/src/ModVerify/Verifiers/GameEngineErrorCollector.cs @@ -4,17 +4,17 @@ using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Reporters.Engine; using AET.ModVerify.Settings; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Verifiers; public sealed class GameEngineErrorCollector( - IDatabaseErrorCollection errorCollection, - IGameDatabase gameDatabase, + IGameEngineErrorCollection errorCollection, + IStarWarsGameEngine gameEngine, GameVerifySettings settings, - IServiceProvider serviceProvider) : GameVerifier(null, gameDatabase, settings, serviceProvider) + IServiceProvider serviceProvider) : GameVerifier(null, gameEngine, settings, serviceProvider) { - public override string FriendlyName => "Reporting Game Engine Errors"; + public override string FriendlyName => "Game Engine Initialization"; public override void Verify(CancellationToken token) { diff --git a/src/ModVerify/Verifiers/GameVerifier.cs b/src/ModVerify/Verifiers/GameVerifier.cs index 8614e70..aebfe94 100644 --- a/src/ModVerify/Verifiers/GameVerifier.cs +++ b/src/ModVerify/Verifiers/GameVerifier.cs @@ -2,26 +2,26 @@ using System.Collections.Generic; using System.Threading; using AET.ModVerify.Settings; -using PG.StarWarsGame.Engine.Database; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Verifiers; public abstract class GameVerifier( IGameVerifierInfo? parent, - IGameDatabase gameDatabase, + IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(parent, gameDatabase, settings, serviceProvider) where T : notnull + : GameVerifierBase(parent, gameEngine, settings, serviceProvider) where T : notnull { public abstract void Verify(T toVerify, IReadOnlyCollection contextInfo, CancellationToken token); } public abstract class GameVerifier( IGameVerifierInfo? parent, - IGameDatabase gameDatabase, + IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifierBase(parent, gameDatabase, settings, serviceProvider) + : GameVerifierBase(parent, gameEngine, settings, serviceProvider) { public abstract void Verify(CancellationToken token); } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 1019f7a..48f92f3 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -1,13 +1,14 @@ -using AET.ModVerify.Reporting; +using AET.ModVerify.Pipeline; +using AET.ModVerify.Reporting; using AET.ModVerify.Settings; +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.IO; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; -using AET.ModVerify.Pipeline; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Verifiers; @@ -15,9 +16,9 @@ public abstract class GameVerifierBase : IGameVerifierInfo { public event EventHandler? Error; - public event EventHandler? Progress; + public event EventHandler>? Progress; - private readonly IGameDatabase _gameDatabase; + private readonly IStarWarsGameEngine _gameEngine; private readonly ConcurrentDictionary _verifyErrors = new(); protected readonly IFileSystem FileSystem; @@ -32,15 +33,15 @@ public abstract class GameVerifierBase : IGameVerifierInfo public IGameVerifierInfo? Parent { get; } - protected IGameDatabase Database { get; } + protected IStarWarsGameEngine GameEngine { get; } - protected IGameRepository Repository => _gameDatabase.GameRepository; + protected IGameRepository Repository => _gameEngine.GameRepository; protected IReadOnlyList VerifierChain { get; } protected GameVerifierBase( IGameVerifierInfo? parent, - IGameDatabase gameDatabase, + IStarWarsGameEngine gameEngine, GameVerifySettings settings, IServiceProvider serviceProvider) { @@ -48,10 +49,10 @@ protected GameVerifierBase( throw new ArgumentNullException(nameof(serviceProvider)); FileSystem = serviceProvider.GetRequiredService(); Services = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - _gameDatabase = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); + _gameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); Parent = parent; Settings = settings ?? throw new ArgumentNullException(nameof(settings)); - Database = gameDatabase ?? throw new ArgumentNullException(nameof(gameDatabase)); + GameEngine = gameEngine ?? throw new ArgumentNullException(nameof(gameEngine)); VerifierChain = CreateVerifierChain(); } @@ -80,7 +81,7 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter protected void OnProgress(string message, double progress) { - Progress?.Invoke(this, new VerifyProgressEventArgs(message, progress)); + Progress?.Invoke(this, new(message, progress)); } private IReadOnlyList CreateVerifierChain() diff --git a/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs b/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs index 42e0b0d..435c5de 100644 --- a/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs +++ b/src/ModVerify/Verifiers/GuiDialogs/GuiDialogsVerifier.cs @@ -3,7 +3,6 @@ using AET.ModVerify.Verifiers.Commons; using Microsoft.Extensions.DependencyInjection; using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.GuiDialog; using PG.StarWarsGame.Files.MTD.Binary; using System; @@ -23,12 +22,12 @@ sealed class GuiDialogsVerifier : GameVerifier private readonly IAlreadyVerifiedCache? _cache; private readonly TextureVeifier _textureVerifier; - public GuiDialogsVerifier(IGameDatabase gameDatabase, + public GuiDialogsVerifier(IStarWarsGameEngine gameEngine, GameVerifySettings settings, - IServiceProvider serviceProvider) : base(null, gameDatabase, settings, serviceProvider) + IServiceProvider serviceProvider) : base(null, gameEngine, settings, serviceProvider) { _cache = serviceProvider.GetService(); - _textureVerifier = new TextureVeifier(this, gameDatabase, settings, serviceProvider); + _textureVerifier = new TextureVeifier(this, gameEngine, settings, serviceProvider); } public override void Verify(CancellationToken token) @@ -51,7 +50,7 @@ private void VerifyGuiTextures() { DefaultComponentIdentifier }; - components.AddRange(Database.GuiDialogManager.Components); + components.AddRange(GameEngine.GuiDialogManager.Components); foreach (var component in components) VerifyGuiComponentTexturesExist(component); @@ -60,8 +59,8 @@ private void VerifyGuiTextures() private void VerifyMegaTexturesExist(CancellationToken token) { - var megaTextureName = Database.GuiDialogManager.GuiDialogsXml?.TextureData.MegaTexture; - if (Database.GuiDialogManager.MtdFile is null) + var megaTextureName = GameEngine.GuiDialogManager.GuiDialogsXml?.TextureData.MegaTexture; + if (GameEngine.GuiDialogManager.MtdFile is null) { var mtdFileName = megaTextureName ?? "<>"; VerificationError.Create(VerifierChain, VerifierErrorCodes.FileNotFound, $"MtdFile '{mtdFileName}.mtd' could not be found", @@ -75,7 +74,7 @@ private void VerifyMegaTexturesExist(CancellationToken token) } - var compressedMegaTextureName = Database.GuiDialogManager.GuiDialogsXml?.TextureData.CompressedMegaTexture; + var compressedMegaTextureName = GameEngine.GuiDialogManager.GuiDialogsXml?.TextureData.CompressedMegaTexture; if (compressedMegaTextureName is not null) { var compressedMegaTextureFieName = $"{compressedMegaTextureName}.dds"; @@ -109,7 +108,7 @@ componentType is not GuiComponentType.Scanlines && continue; } - if (!Database.GuiDialogManager.TextureExists( + if (!GameEngine.GuiDialogManager.TextureExists( texture, out var origin, out var isNone, @@ -153,9 +152,9 @@ private IReadOnlyDictionary GetTextureE if (component == DefaultComponentIdentifier) { defined = true; - return Database.GuiDialogManager.DefaultTextureEntries; + return GameEngine.GuiDialogManager.DefaultTextureEntries; } - return Database.GuiDialogManager.GetTextureEntries(component, out defined); + return GameEngine.GuiDialogManager.GetTextureEntries(component, out defined); } private void OnTextureError(object sender, VerificationErrorEventArgs e) diff --git a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs index 778aeda..92dc435 100644 --- a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs +++ b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs @@ -1,29 +1,33 @@ -using System; +using AET.ModVerify.Settings; +using AET.ModVerify.Verifiers.Commons; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine; +using System; using System.Linq; using System.Threading; -using AET.ModVerify.Settings; -using PG.StarWarsGame.Engine; -using PG.StarWarsGame.Engine.Database; -using AET.ModVerify.Verifiers.Commons; +using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Verifiers; public sealed class ReferencedModelsVerifier( - IGameDatabase database, + IStarWarsGameEngine engine, GameVerifySettings settings, IServiceProvider serviceProvider) - : GameVerifier(null, database, settings, serviceProvider) + : GameVerifier(null, engine, settings, serviceProvider) { public override string FriendlyName => "Referenced Models"; + private readonly ILogger? _logger = + serviceProvider.GetService()?.CreateLogger(typeof(ReferencedModelsVerifier)); + public override void Verify(CancellationToken token) { - var models = Database.GameObjectTypeManager.Entries + var models = GameEngine.GameObjectTypeManager.Entries .SelectMany(x => x.Models) .Concat(FocHardcodedConstants.HardcodedModels); - var inner = new SingleModelVerifier(this, Database, Settings, Services); + var inner = new SingleModelVerifier(this, GameEngine, Settings, Services); try { inner.Error += OnModelError; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs index f054f1c..41c4bb8 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/ISfxEventGameManager.cs @@ -1,5 +1,3 @@ -using PG.StarWarsGame.Engine.Database; - -namespace PG.StarWarsGame.Engine.Audio.Sfx; +namespace PG.StarWarsGame.Engine.Audio.Sfx; public interface ISfxEventGameManager : IGameManager; \ 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 5805746..b037939 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Audio/Sfx/SfxEventGameManager.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Localization; @@ -12,7 +11,7 @@ namespace PG.StarWarsGame.Engine.Audio.Sfx; -internal class SfxEventGameManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class SfxEventGameManager(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), ISfxEventGameManager { public IEnumerable InstalledLanguages { get; private set; } = []; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs index 7863880..311c212 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/CommandBarGameManager.cs @@ -4,7 +4,6 @@ using PG.Commons.Hashing; using PG.StarWarsGame.Engine.CommandBar.Components; using PG.StarWarsGame.Engine.CommandBar.Xml; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.IO.Repositories; @@ -27,7 +26,7 @@ internal class CommandBarGameManager( PGRender pgRender, IGameConstants gameConstants, IFontManager fontManager, - GameErrorReporterWrapper errorReporter, + GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), ICommandBarGameManager { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs index ddfb52d..a00f70e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/Components/CommandBarBaseComponent.cs @@ -20,7 +20,7 @@ public abstract class CommandBarBaseComponent(CommandBarComponentData xmlData) public CommandBarComponentGroup? Group { get; internal set; } public CommandBarShellComponent? ParentShell { get; internal set; } - public static CommandBarBaseComponent? Create(CommandBarComponentData xmlData, IGameErrorReporter errorReporter) + public static CommandBarBaseComponent? Create(CommandBarComponentData xmlData, IGameEngineErrorReporter errorReporter) { var type = GetTypeFromString(xmlData.Type.AsSpan()); switch (type) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs index ec56c35..665d217 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/CommandBar/ICommandBarGameManager.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using PG.StarWarsGame.Engine.CommandBar.Components; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Files.MTD.Files; namespace PG.StarWarsGame.Engine.CommandBar; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs deleted file mode 100644 index 820db03..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabaseService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.ErrorReporting; -using PG.StarWarsGame.Engine.IO; - -namespace PG.StarWarsGame.Engine.Database; - -internal class GameDatabaseService(IServiceProvider serviceProvider) : IGameDatabaseService -{ - public Task InitializeGameAsync( - GameInitializationOptions gameInitializationOptions, - CancellationToken cancellationToken = default) - { - var repoFactory = serviceProvider.GetRequiredService(); - - using var errorListenerWrapper = new GameErrorReporterWrapper(gameInitializationOptions.GameErrorReporter, serviceProvider); - var repository = repoFactory.Create(gameInitializationOptions.TargetEngineType, gameInitializationOptions.Locations, errorListenerWrapper); - - var gameInitializer = new GameInitializer(repository, gameInitializationOptions.CancelOnError, serviceProvider); - return gameInitializer.InitializeAsync(errorListenerWrapper, cancellationToken); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs deleted file mode 100644 index e00e0d3..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializationOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using PG.StarWarsGame.Engine.ErrorReporting; - -namespace PG.StarWarsGame.Engine.Database; - -public class GameInitializationOptions -{ - public required GameEngineType TargetEngineType { get; init; } - - public required GameLocations Locations { get; init; } - - public bool CancelOnError { get; init; } - - public IGameErrorReporter? GameErrorReporter { get; init; } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs deleted file mode 100644 index 9158a0e..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameInitializer.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.Audio.Sfx; -using PG.StarWarsGame.Engine.CommandBar; -using PG.StarWarsGame.Engine.ErrorReporting; -using PG.StarWarsGame.Engine.GameObjects; -using PG.StarWarsGame.Engine.GuiDialog; -using PG.StarWarsGame.Engine.IO.Repositories; -using PG.StarWarsGame.Engine.Rendering; -using PG.StarWarsGame.Engine.Rendering.Font; - -namespace PG.StarWarsGame.Engine.Database; - -internal class GameInitializer(GameRepository repository, bool cancelOnError, IServiceProvider serviceProvider) -{ - private readonly ILogger? _logger = serviceProvider.GetService()?.CreateLogger(typeof(GameInitializer)); - - private CancellationTokenSource? _cancellationTokenSource; - - public async Task InitializeAsync(GameErrorReporterWrapper errorReporter, CancellationToken token) - { - _logger?.LogInformation("Initializing Game Database..."); - errorReporter.InitializationError += OnInitializationError; - - _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); - - try - { - // LensFlares.xml - // SurfaceFX.xml - // TerrainDecalFX.xml - // GraphicDetails.xml - // DynamicTrackFX.xml - // ShadowBlobMaterials.xml - // TacticalCameras.xml - // LightSources.xml - // StarWars3DTextCrawl.xml - // MusicEvents.xml - // SpeechEvents.xml - // GameConstantsXml.xml - // Audio.xml - // WeatherAudio.xml - // HeroClash.xml - // TradeRouteLines.xml - // RadarMap.xml - // WeatherModifiers.xml - // Movies.xml - // LightningEffectTypes.xml - // DifficultyAdjustments.xml - // WeatherScenarios.xml - // UnitAbilityTypes.xml - // BlackMarketItems.xml - // MovementClassTypeDefs.xml - // AITerrainEffectiveness.xml - - - // CONTAINER FILES: - // GameObjectFiles.xml - // TradeRouteFiles.xml - // HardPointDataFiles.xml - // CampaignFiles.xml - // FactionFiles.xml - // TargetingPrioritySetFiles.xml - // MousePointerFiles.xml - - var pgRender = new PGRender(repository, errorReporter, serviceProvider); - - var gameConstants = new GameConstants.GameConstants(repository, errorReporter, serviceProvider); - await gameConstants.InitializeAsync( _cancellationTokenSource.Token); - - // AudioConstants - - // MousePointer - - var fontManger = new FontManager(repository, errorReporter, serviceProvider); - await fontManger.InitializeAsync(_cancellationTokenSource.Token); - - var guiDialogs = new GuiDialogGameManager(repository, errorReporter, serviceProvider); - await guiDialogs.InitializeAsync(_cancellationTokenSource.Token); - - var sfxGameManager = new SfxEventGameManager(repository, errorReporter, serviceProvider); - await sfxGameManager.InitializeAsync( _cancellationTokenSource.Token); - - var commandBarManager = new CommandBarGameManager(repository, pgRender, gameConstants, fontManger, errorReporter, serviceProvider); - await commandBarManager.InitializeAsync( _cancellationTokenSource.Token); - - var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorReporter, serviceProvider); - await gameObjetTypeManager.InitializeAsync( _cancellationTokenSource.Token); - - repository.Seal(); - - return new GameDatabase - { - PGRender = pgRender, - FontManager = fontManger, - GameRepository = repository, - GameConstants = gameConstants, - GuiDialogManager = guiDialogs, - CommandBar = commandBarManager, - GameObjectTypeManager = gameObjetTypeManager, - SfxGameManager = sfxGameManager, - InstalledLanguages = sfxGameManager.InstalledLanguages - }; - } - finally - { - errorReporter.InitializationError -= OnInitializationError; - _cancellationTokenSource?.Dispose(); - _cancellationTokenSource = null; - _logger?.LogInformation("Finished initializing game database"); - } - } - - private void OnInitializationError(object sender, InitializationError e) - { - if(cancelOnError) - _cancellationTokenSource?.Cancel(); - } -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs deleted file mode 100644 index 46cfda2..0000000 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabaseService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace PG.StarWarsGame.Engine.Database; - -public interface IGameDatabaseService -{ - Task InitializeGameAsync(GameInitializationOptions gameInitializationOptions, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs index da0e52e..e3a2d69 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs @@ -5,5 +5,6 @@ public enum EngineAssertKind NullOrEmptyValue, ValueOutOfRange, InvalidValue, - CorruptBinary + CorruptBinary, + FileNotFound, } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporter.cs similarity index 77% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporter.cs index 67b8d49..0030344 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporter.cs @@ -1,6 +1,6 @@ namespace PG.StarWarsGame.Engine.ErrorReporting; -public abstract class GameErrorReporter : IGameErrorReporter +public abstract class GameEngineErrorReporter : IGameEngineErrorReporter { public virtual void Report(XmlError error) { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs similarity index 64% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs index 1893f81..a636aba 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameErrorReporterWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/GameEngineErrorReporterWrapper.cs @@ -1,24 +1,20 @@ using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using PG.StarWarsGame.Files.XML.ErrorHandling; using PG.StarWarsGame.Files.XML.Parsers; namespace PG.StarWarsGame.Engine.ErrorReporting; -internal sealed class GameErrorReporterWrapper : XmlErrorReporter, IGameErrorReporter +internal sealed class GameEngineErrorReporterWrapper : XmlErrorReporter, IGameEngineErrorReporter { internal event EventHandler? InitializationError; - private readonly IGameErrorReporter? _errorReporter; - private readonly ILogger? _logger; + private readonly IGameEngineErrorReporter? _errorReporter; - public GameErrorReporterWrapper(IGameErrorReporter? errorReporter, IServiceProvider serviceProvider) + public GameEngineErrorReporterWrapper(IGameEngineErrorReporter? errorReporter) { if (errorReporter is null) return; _errorReporter = errorReporter; - _logger = serviceProvider.GetService()?.CreateLogger(GetType()); } public void Report(XmlError error) @@ -42,8 +38,6 @@ public override void Report(IPetroglyphXmlParser parser, XmlParseErrorEventArgs if (_errorReporter is null) return; - _logger?.LogWarning($"Xml parser '{parser}' reported error for file '{error.Location.XmlFile}': {error.Message}"); - Report(new XmlError { FileLocation = error.Location, diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs similarity index 80% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs index 1a4484c..1294fd1 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameErrorReporter.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/IGameEngineErrorReporter.cs @@ -1,6 +1,6 @@ namespace PG.StarWarsGame.Engine.ErrorReporting; -public interface IGameErrorReporter +public interface IGameEngineErrorReporter { void Report(XmlError error); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs index 4368f48..1c36061 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameConstants/GameConstants.cs @@ -1,13 +1,12 @@ using System; using System.Threading; using System.Threading.Tasks; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; namespace PG.StarWarsGame.Engine.GameConstants; -internal class GameConstants(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class GameConstants(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameConstants { protected override Task InitializeCoreAsync(CancellationToken token) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngine.cs similarity index 87% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngine.cs index 9a63eff..8188117 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngine.cs @@ -9,10 +9,12 @@ using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; -namespace PG.StarWarsGame.Engine.Database; +namespace PG.StarWarsGame.Engine; -internal class GameDatabase : IGameDatabase +internal sealed class GameEngine : IStarWarsGameEngine { + public required GameEngineType EngineType { get; init; } + public required IPGRender PGRender { get; init; } public required IFontManager FontManager { get; init; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs index 962c389..7c2628d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs @@ -1,4 +1,6 @@ -namespace PG.StarWarsGame.Engine; +using Vanara.PInvoke; + +namespace PG.StarWarsGame.Engine; /// /// The type of engine can be either "Empire at War" of "Forces of Corruption" diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs similarity index 89% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs index 33696b3..7fa4d02 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/GameManagerBase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameManagerBase.cs @@ -11,9 +11,9 @@ using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; -namespace PG.StarWarsGame.Engine.Database; +namespace PG.StarWarsGame.Engine; -internal abstract class GameManagerBase(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal abstract class GameManagerBase(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameManager { protected readonly ValueListDictionary NamedEntries = new(); @@ -38,11 +38,11 @@ internal abstract class GameManagerBase protected readonly IFileSystem FileSystem; protected readonly ILogger? Logger; - protected readonly GameErrorReporterWrapper ErrorReporter; + protected readonly GameEngineErrorReporterWrapper ErrorReporter; public bool IsInitialized => _initialized; - protected GameManagerBase(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + protected GameManagerBase(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) { GameRepository = repository ?? throw new ArgumentNullException(nameof(repository)); ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs index 6147838..407d9c5 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs @@ -2,14 +2,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using PG.StarWarsGame.Engine.Xml.Parsers; namespace PG.StarWarsGame.Engine.GameObjects; -internal class GameObjectTypeGameManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal class GameObjectTypeGameManager(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGameObjectTypeGameManager { protected override async Task InitializeCoreAsync(CancellationToken token) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs index ae22ca1..f31273f 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/IGameObjectTypeGameManager.cs @@ -1,5 +1,3 @@ -using PG.StarWarsGame.Engine.Database; - -namespace PG.StarWarsGame.Engine.GameObjects; +namespace PG.StarWarsGame.Engine.GameObjects; public interface IGameObjectTypeGameManager : IGameManager; \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs index 5986d3d..82ce825 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GuiDialog/GuiDialogGameManager.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.Commons.Hashing; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.GuiDialog.Xml; using PG.StarWarsGame.Engine.IO.Repositories; @@ -14,7 +13,7 @@ namespace PG.StarWarsGame.Engine.GuiDialog; -internal partial class GuiDialogGameManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) +internal partial class GuiDialogGameManager(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : GameManagerBase(repository, errorReporter, serviceProvider), IGuiDialogManager { private readonly IMtdFileService _mtdFileService = serviceProvider.GetRequiredService(); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameManager.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs similarity index 86% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameManager.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs index f331a24..b9f60a7 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IGameManager.cs @@ -2,7 +2,7 @@ using AnakinRaW.CommonUtilities.Collections; using PG.Commons.Hashing; -namespace PG.StarWarsGame.Engine.Database; +namespace PG.StarWarsGame.Engine; public interface IGameManager { diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs index 9ff2485..5aed364 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/GameRepositoryFactory.cs @@ -6,7 +6,7 @@ namespace PG.StarWarsGame.Engine.IO; internal sealed class GameRepositoryFactory(IServiceProvider serviceProvider) : IGameRepositoryFactory { - public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, GameErrorReporterWrapper errorReporter) + public GameRepository Create(GameEngineType engineType, GameLocations gameLocations, GameEngineErrorReporterWrapper errorReporter) { if (engineType == GameEngineType.Eaw) throw new NotImplementedException("Empire at War is currently not supported."); diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs index c185793..fa59118 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/IGameRepositoryFactory.cs @@ -5,5 +5,5 @@ namespace PG.StarWarsGame.Engine.IO; internal interface IGameRepositoryFactory { - GameRepository Create(GameEngineType engineType, GameLocations gameLocations, GameErrorReporterWrapper errorReporter); + GameRepository Create(GameEngineType engineType, GameLocations gameLocations, GameEngineErrorReporterWrapper errorReporter); } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs index 0be4daf..a1b07fb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/FocGameRepository.cs @@ -12,7 +12,7 @@ internal class FocGameRepository : GameRepository { public override GameEngineType EngineType => GameEngineType.Foc; - public FocGameRepository(GameLocations gameLocations, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + public FocGameRepository(GameLocations gameLocations, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(gameLocations, errorReporter, serviceProvider) { if (gameLocations == null) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs index f4f9f9f..42bfa54 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Repositories/GameRepository.cs @@ -27,7 +27,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository private readonly ICrc32HashingService _crc32HashingService; private readonly IVirtualMegArchiveBuilder _virtualMegBuilder; private readonly IGameLanguageManagerProvider _languageManagerProvider; - private readonly GameErrorReporterWrapper _errorReporter; + private readonly GameEngineErrorReporterWrapper _errorReporter; protected readonly string GameDirectory; @@ -47,7 +47,7 @@ internal abstract partial class GameRepository : ServiceBase, IGameRepository private readonly List _loadedMegFiles = new(); protected IVirtualMegArchive? MasterMegArchive { get; private set; } - protected GameRepository(GameLocations gameLocations, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(serviceProvider) + protected GameRepository(GameLocations gameLocations, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(serviceProvider) { if (gameLocations == null) throw new ArgumentNullException(nameof(gameLocations)); @@ -253,7 +253,11 @@ internal void Seal() if (IsSpeechMeg(megPath)) Logger.LogDebug($"Unable to find Speech MEG file '{megPath}'"); else + { + var message = $"Unable to find MEG file '{megPath}'"; + _errorReporter.Assert(EngineAssert.Create(EngineAssertKind.FileNotFound, megPath, [], message)); Logger.LogWarning($"Unable to find MEG file '{megPath}'"); + } return null; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs index df99faa..fef3dd9 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/Utilities/FileInfoGlobbingWrapper.cs @@ -23,7 +23,7 @@ _fileInfo.Directory is null : new DirectoryInfoGlobbingWrapper(_fileSystem, _fileInfo.Directory); /// - /// Initialize a new instance + /// InitializeAsync a new instance /// /// The filesystem /// The file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs new file mode 100644 index 0000000..314ce06 --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IPetroglyphStarWarsGameEngineService.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using PG.StarWarsGame.Engine.ErrorReporting; + +namespace PG.StarWarsGame.Engine; + +public interface IPetroglyphStarWarsGameEngineService +{ + public Task InitializeAsync( + GameEngineType engineType, + GameLocations gameLocations, + IGameEngineErrorReporter? errorReporter = null, + IProgress? initProgress = null, + bool cancelOnInitializationError = false, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IStarWarsGameEngine.cs similarity index 73% rename from src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs rename to src/PetroglyphTools/PG.StarWarsGame.Engine/IStarWarsGameEngine.cs index ca18eec..bdb7215 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Database/IGameDatabase.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IStarWarsGameEngine.cs @@ -1,4 +1,6 @@ -using PG.StarWarsGame.Engine.Audio.Sfx; +using System.Collections.Generic; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.CommandBar; using PG.StarWarsGame.Engine.GameConstants; using PG.StarWarsGame.Engine.GameObjects; using PG.StarWarsGame.Engine.GuiDialog; @@ -6,13 +8,13 @@ using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Rendering; using PG.StarWarsGame.Engine.Rendering.Font; -using System.Collections.Generic; -using PG.StarWarsGame.Engine.CommandBar; -namespace PG.StarWarsGame.Engine.Database; +namespace PG.StarWarsGame.Engine; -public interface IGameDatabase +public interface IStarWarsGameEngine { + GameEngineType EngineType { get; } + IPGRender PGRender { get; } IFontManager FontManager { get; } @@ -25,7 +27,7 @@ public interface IGameDatabase ICommandBarGameManager CommandBar { get; } - IGameObjectTypeGameManager GameObjectTypeManager { get; } + IGameObjectTypeGameManager GameObjectTypeManager { get; } ISfxEventGameManager SfxGameManager { get; } diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index d5752e7..35fbaae 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -20,7 +20,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -43,4 +43,7 @@ + + + \ 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 62b8070..0132d10 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphEngineServiceContribution.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using PG.StarWarsGame.Engine.Database; using PG.StarWarsGame.Engine.IO; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Engine.Xml; @@ -15,6 +14,6 @@ public static void ContributeServices(IServiceCollection serviceCollection) serviceCollection.AddSingleton(sp => new GameLanguageManagerProvider(sp)); serviceCollection.AddSingleton(sp => new PetroglyphXmlFileParserFactory(sp)); - serviceCollection.AddSingleton(sp => new GameDatabaseService(sp)); + serviceCollection.AddSingleton(sp => new PetroglyphStarWarsGameEngineService(sp)); } } \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs new file mode 100644 index 0000000..d2acfba --- /dev/null +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PetroglyphStarWarsGameEngineService.cs @@ -0,0 +1,126 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PG.StarWarsGame.Engine.Audio.Sfx; +using PG.StarWarsGame.Engine.CommandBar; +using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.GameObjects; +using PG.StarWarsGame.Engine.GuiDialog; +using PG.StarWarsGame.Engine.IO; +using PG.StarWarsGame.Engine.Rendering; +using PG.StarWarsGame.Engine.Rendering.Font; + +namespace PG.StarWarsGame.Engine; + +internal sealed class PetroglyphStarWarsGameEngineService(IServiceProvider serviceProvider) + : IPetroglyphStarWarsGameEngineService +{ + private readonly IServiceProvider _serviceProvider = + serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + + private readonly ILogger? _logger = serviceProvider.GetService() + ?.CreateLogger(typeof(PetroglyphStarWarsGameEngineService)); + + public async Task InitializeAsync( + GameEngineType engineType, + GameLocations gameLocations, + IGameEngineErrorReporter? errorReporter = null, + IProgress? progress = null, + bool cancelOnInitializationError = false, + CancellationToken cancellationToken = default) + + { + var errorListenerWrapper = new GameEngineErrorReporterWrapper(errorReporter); + var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + errorListenerWrapper.InitializationError += OnInitializationError; + + try + { + return await InitializeEngine(engineType, gameLocations, errorListenerWrapper, progress, cts.Token) + .ConfigureAwait(false); + } + finally + { + errorListenerWrapper.InitializationError -= OnInitializationError; + errorListenerWrapper.Dispose(); + cts.Dispose(); + } + + void OnInitializationError(object sender, InitializationError e) + { + if (cancelOnInitializationError) + cts.Cancel(); + } + } + + private async Task InitializeEngine( + GameEngineType engineType, + GameLocations gameLocations, + GameEngineErrorReporterWrapper errorReporter, + IProgress? progress, + CancellationToken token) + { + try + { + _logger?.LogInformation($"Initializing game engine for type '{engineType}'."); + + var repoFactory = _serviceProvider.GetRequiredService(); + var repository = repoFactory.Create(engineType, gameLocations, errorReporter); + + var pgRender = new PGRender(repository, errorReporter, serviceProvider); + + var gameConstants = new GameConstants.GameConstants(repository, errorReporter, serviceProvider); + progress?.Report("Initializing GameConstants"); + await gameConstants.InitializeAsync(token); + + // AudioConstants + + // MousePointer + + var fontManger = new FontManager(repository, errorReporter, serviceProvider); + progress?.Report("Initializing FontManager"); + await fontManger.InitializeAsync(token); + + var guiDialogs = new GuiDialogGameManager(repository, errorReporter, serviceProvider); + progress?.Report("Initializing GUIDialogManager"); + await guiDialogs.InitializeAsync(token); + + var sfxGameManager = new SfxEventGameManager(repository, errorReporter, serviceProvider); + progress?.Report("Initializing SFXManager"); + await sfxGameManager.InitializeAsync(token); + + var commandBarManager = new CommandBarGameManager(repository, pgRender, gameConstants, fontManger, errorReporter, serviceProvider); + progress?.Report("Initializing CommandBar"); + await commandBarManager.InitializeAsync(token); + + var gameObjetTypeManager = new GameObjectTypeGameManager(repository, errorReporter, serviceProvider); + progress?.Report("Initializing GameObjectTypeManager"); + await gameObjetTypeManager.InitializeAsync(token); + + token.ThrowIfCancellationRequested(); + + repository.Seal(); + + return new GameEngine + { + EngineType = engineType, + PGRender = pgRender, + FontManager = fontManger, + GameRepository = repository, + GameConstants = gameConstants, + GuiDialogManager = guiDialogs, + CommandBar = commandBarManager, + GameObjectTypeManager = gameObjetTypeManager, + SfxGameManager = sfxGameManager, + InstalledLanguages = sfxGameManager.InstalledLanguages + }; + } + finally + { + _logger?.LogDebug("Finished initializing game database."); + } + } +} \ 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 0e43ede..00ca4f2 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/Font/FontManager.cs @@ -1,5 +1,4 @@ -using PG.StarWarsGame.Engine.Database; -using PG.StarWarsGame.Engine.ErrorReporting; +using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Engine.IO.Repositories; using System; using System.Collections.Generic; @@ -16,7 +15,7 @@ internal class FontManager : GameManagerBase, IFontManager private ISet _fontNames = null!; - public FontManager(GameRepository repository, GameErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) + public FontManager(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : base(repository, errorReporter, serviceProvider) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs index 8a003a1..e87d9ea 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/Rendering/PGRender.cs @@ -19,7 +19,7 @@ namespace PG.StarWarsGame.Engine.Rendering; internal class PGRender( GameRepository gameRepository, - GameErrorReporterWrapper errorReporter, + GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider) : IPGRender { private readonly IAloFileService _aloFileService = serviceProvider.GetRequiredService(); From 9e083237273edcc9490ce9f8fc0d5d37f3fda7a5 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 5 Apr 2025 19:09:35 +0200 Subject: [PATCH 32/34] release candiate --- .github/dependabot.yml | 23 +- Directory.Build.props | 57 +- README.md | 40 +- focBaseline.json | 2893 +++++++++-------- src/Directory.Build.props | 46 - src/ModVerify.CliApp/ConsoleUtilities.cs | 94 + .../ModSelectors/ConsoleModSelector.cs | 31 +- src/ModVerify.CliApp/ModVerify.CliApp.csproj | 15 +- .../CommandLine/BaseModVerifyOptions.cs | 8 +- src/ModVerify.CliApp/Program.cs | 107 +- .../Properties/launchSettings.json | 2 +- .../VerifyConsoleProgressReporter.cs | 15 +- src/ModVerify.CliApp/SettingsBuilder.cs | 4 +- .../Updates/GithubReleaseEntry.cs | 17 + .../Updates/GithubReleaseList.cs | 5 + .../Updates/ModVerifyUpdaterChecker.cs | 67 + .../Updates/ModVerifyUpdaterInformation.cs | 22 + src/ModVerify.CliApp/Updates/UpdateInfo.cs | 14 + src/ModVerify/ModVerify.csproj | 29 +- .../Pipeline/DefaultGameVerifiersProvider.cs | 2 +- .../Pipeline/GameVerifierPipelineStep.cs | 73 +- src/ModVerify/Pipeline/GameVerifyPipeline.cs | 2 +- .../AggregatedVerifyProgressReporter.cs | 72 + .../Progress/IVerifyProgressReporter.cs | 5 + .../Pipeline/Progress/VerifyProgress.cs | 12 + .../Pipeline/Progress/VerifyProgressInfo.cs | 8 + .../Reporting/Reporters/ReporterBase.cs | 1 - .../VerificationReportersExtensions.cs | 3 +- src/ModVerify/Verifiers/AudioFilesVerifier.cs | 7 +- src/ModVerify/Verifiers/GameVerifierBase.cs | 8 +- .../Verifiers/ReferencedModelsVerifier.cs | 13 +- src/ModVerify/Verifiers/VerifierErrorCodes.cs | 2 +- src/PetroglyphTools/Directory.Build.props | 7 +- .../PG.StarWarsGame.Engine/GameEngineType.cs | 4 +- .../IO/MultiPassRepository.cs | 1 - .../PG.StarWarsGame.Engine.csproj | 2 +- .../Parsers/PetroglyphXmlElementParser.cs | 3 +- 37 files changed, 2094 insertions(+), 1620 deletions(-) delete mode 100644 src/Directory.Build.props create mode 100644 src/ModVerify.CliApp/ConsoleUtilities.cs create mode 100644 src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs create mode 100644 src/ModVerify.CliApp/Updates/GithubReleaseList.cs create mode 100644 src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs create mode 100644 src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs create mode 100644 src/ModVerify.CliApp/Updates/UpdateInfo.cs create mode 100644 src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs create mode 100644 src/ModVerify/Pipeline/Progress/IVerifyProgressReporter.cs create mode 100644 src/ModVerify/Pipeline/Progress/VerifyProgress.cs create mode 100644 src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8f37748..b43ddf6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,24 @@ version: 2 updates: - - package-ecosystem: "nuget" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: "github-actions" + directory: "/" schedule: - interval: "daily" \ No newline at end of file + interval: "daily" + groups: + actions-deps: + patterns: + - "*" + target-branch: "develop" + + + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "daily" + target-branch: "develop" + open-pull-requests-limit: 1 + groups: + all-deps: + patterns: + - "*" \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 26f6094..49ca26f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,9 +1,52 @@  - - - - all - 3.6.143 - - + + + + $(MSBuildThisFileDirectory) + true + true + $(RepoRootPath) + $(RepoRootPath)bin\Packages\$(Configuration)\ + + + + ModVerify + Alamo Engine Tools and Contributors + Copyright © 2025 Alamo Engine Tools and contributors. All rights reserved. + https://github.com/AlamoEngine-Tools/ModVerify + $(RepoRootPath)LICENSE + MIT + https://github.com/AlamoEngine-Tools/ModVerify + git + Alamo Engine Tools + README.md + aet.png + + + + latest + disable + enable + True + latest + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + 3.7.115 + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 9ef3e13..a7c6aaf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # ModVerify: A Mod Verification Tool -ModVerify is a command-line tool designed to verify mods for the game Star Wars: Empire at War and its expansion Forces of Corruption. +ModVerify is a command-line tool designed to analyze +mods for the game Star Wars: Empire at War and its expansion Forces of Corruption +for common errors in XML and other game files. ## Table of Contents @@ -8,6 +10,9 @@ ModVerify is a command-line tool designed to verify mods for the game Star Wars: - [Usage](#usage) - [Options](#options) - [Available Checks](#available-checks) +- [Creating a new Baseline](#creating-a-new-baseline) + +--- ## Installation @@ -18,15 +23,18 @@ Download the latest release from the [releases page](https://github.com/AlamoEng You can place the files anywhere on your system, eg. your Desktop. There is no need to place it inside a mod's directory. -*Note: Both versions have the exact same feature set. They just target a different .NET runtime. Linux and CI/CD support is not fully tested yet. Current priority is on the Windows-only version.* +***Note**: Both versions have the exact same feature set. They just target a different .NET runtime. Linux and CI/CD support is not fully tested yet. Current priority is on the Windows-only version.* + +--- ## Usage Simply run the executable file `ModVerify.exe`. -When given no specific argument through the command line, the app will ask you which game or mod you want to verify. When the tool is done, it will write the verification results into new files next to the executable. +When given no specific argument through the command line, ModVerify will ask you which game or mod you want to verify. When ModVerify is done analyzing, it will write the verification results into new files next to the executable. -A `.JSON` file lists all found issues. Into seperate `.txt` files the same errors get grouped by a category of the finding. The text files may be easier to read, while the json file is more useful for 3rd party tool processing. +A `.JSON` file contains all identified issues. The additional `.txt` files contain the same errors but are grouped by the verifier that reported the issue. +The text files may be easier to read, while the JSON file is more useful for 3rd party tool processing. ## Options @@ -49,8 +57,6 @@ Specified the output path where analysis result shall be written to. ### `--baseline` Specifies a baseline file that shall be used to filter out known errors. You can download the [FoC baseline](focBaseline.json) which includes all errors produced by the vanilla game. -### `--createBaseline` -If you want to create your own baseline, add this option with a file path such as `myModBaseline.json`. ### Example This is an example run configuration that analyzes a specific mod, uses a the FoC basline and writes the output into a dedicated directory: @@ -59,19 +65,35 @@ This is an example run configuration that analyzes a specific mod, uses a the Fo ModVerify.exe --path "C:\My Games\FoC\Mods\MyMod" --output "C:\My Games\FoC\Mods\MyMod\verifyResults" --baseline focBaseline.json ``` +--- ## Available Checks The following verifiers are currently implemented: -### For SFX Events: -- Checks whether coded preset exists +### SFX Events: +- Checks whether coded preset exist - Checks the referenced samples for validity (bit rate, sample size, channels, etc.) - Duplicates +### GUIDialogs +- Checks the referenced textures exist -### For GameObjects +### GameObjects - Checks the referenced models for validity (textures, particles and shaders) - Duplicates +### Engine +- Performs assertion checks the Debug builds of the game are also doing (not complete) +- Sports XML errors and unexpected values + +--- + +## Creating a new Baseline +If you want to create your own baseline use the `createBaseline` option. + +### Example +```bash +ModVerify.exe createBaseline -o myBaseline.json --path "C:\My Games\FoC\Mods\MyMod" +``` diff --git a/focBaseline.json b/focBaseline.json index 2b429dc..ad63da6 100644 --- a/focBaseline.json +++ b/focBaseline.json @@ -1,1632 +1,1827 @@ { + "version": "2.0", + "minSeverity": "Information", "errors": [ { "id": "XML04", - "verifier": "AET.ModVerify.Verifiers.XmlParseErrorCollector", - "message": "Expected integer but got \u002780, 20\u0027 at DATA\\XML\\SFXEVENTSWEAPONS.XML at line: 90", - "severity": 0, - "assets": [ + "verifiers": [ + "XMLError" + ], + "message": "Expected double but got value \u002737\u0060\u0027. File=\u0027DATA\\XML\\COMMANDBARCOMPONENTS.XML #11571\u0027", + "severity": "Warning", + "context": [ + "DATA\\XML\\COMMANDBARCOMPONENTS.XML", + "Size", + "parentName=\u0027bm_text_steal\u0027" + ], + "asset": "Size" + }, + { + "id": "XML04", + "verifiers": [ + "XMLError" + ], + "message": "Expected integer but got \u002780, 20\u0027. File=\u0027DATA\\XML\\SFXEVENTSWEAPONS.XML #90\u0027", + "severity": "Warning", + "context": [ "DATA\\XML\\SFXEVENTSWEAPONS.XML", "Probability", "parentName=\u0027Unit_TIE_Fighter_Fire\u0027" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_VCH.ALO references missing proxy particle: P_heat_small01.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_VCH.ALO", - "P_heat_small01.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_SCH.ALO references missing proxy particle: p_cold_tiny01.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_SCH.ALO", - "p_cold_tiny01.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO references missing proxy particle: p_smoke_small_thin2.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO", - "p_smoke_small_thin2.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO references missing proxy particle: p_explosion_smoke_small_thin5.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO", - "p_explosion_smoke_small_thin5.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_rv_XWingProp.alo\u0027", - "severity": 0, - "assets": [ - "Cin_rv_XWingProp.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\ALTTEST.ALO references missing texture: Cin_DeathStar.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\ALTTEST.ALO", - "Cin_DeathStar.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_MDU_CAGE.ALO references missing texture: UB_girder_B.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_MDU_CAGE.ALO", - "UB_girder_B.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_MDU_CAGE.ALO references missing texture: NB_YsalamiriTree_B.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_MDU_CAGE.ALO", - "NB_YsalamiriTree_B.tga" - ] - }, - { - "id": "ALO04", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO references missing shader effect: Default.fx", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO", - "Default.fx" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\RV_MPTL-2A.ALO references missing proxy particle: P_mptl-2a_Die.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\RV_MPTL-2A.ALO", - "P_mptl-2a_Die.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027w_planet_volcanic.alo\u0027", - "severity": 0, - "assets": [ - "w_planet_volcanic.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027RV_nebulonb_D_death_00.ALO\u0027", - "severity": 0, - "assets": [ - "RV_nebulonb_D_death_00.ALO" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_STARS_LOW.ALO references missing proxy particle: Lensflare0.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_STARS_LOW.ALO", - "Lensflare0.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO references missing proxy particle: Lensflare0.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO", - "Lensflare0.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_STARS_HIGH.ALO references missing proxy particle: Lensflare0.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_STARS_HIGH.ALO", - "Lensflare0.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_STARS_CINE.ALO references missing proxy particle: Lensflare0.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_STARS_CINE.ALO", - "Lensflare0.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO references missing proxy particle: Lensflare0.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO", - "Lensflare0.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\EI_MARAJADE.ALO references missing proxy particle: p_desert_ground_dust.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\EI_MARAJADE.ALO", - "p_desert_ground_dust.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO references missing proxy particle: p_desert_ground_dust.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO", - "p_desert_ground_dust.alo" - ] - }, - { - "id": "ALO04", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_SKIPRAY.ALO references missing shader effect: Default.fx", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_SKIPRAY.ALO", - "Default.fx" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO references missing proxy particle: p_smoke_small_thin2.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO", - "p_smoke_small_thin2.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO references missing proxy particle: p_steam_small.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO", - "p_steam_small.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_PRISON.ALO references missing proxy particle: p_prison_light.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_PRISON.ALO", - "p_prison_light.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_PRISON.ALO references missing proxy particle: p_smoke_small_thin2.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_PRISON.ALO", - "p_smoke_small_thin2.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_PRISON.ALO references missing proxy particle: p_smoke_small_thin4.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_PRISON.ALO", - "p_smoke_small_thin4.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO references missing proxy particle: p_explosion_small_delay00.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO", - "p_explosion_small_delay00.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO references missing proxy particle: p_bomb_spin.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO", - "p_bomb_spin.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_GRENADE.ALO references missing texture: w_grenade.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_GRENADE.ALO", - "w_grenade.tga" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027p_splash_wake_lava.alo\u0027", - "severity": 0, - "assets": [ - "p_splash_wake_lava.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\EV_TIE_PHANTOM.ALO references missing texture: W_TE_Rock_f_02_b.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\EV_TIE_PHANTOM.ALO", - "W_TE_Rock_f_02_b.tga" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO references missing proxy particle: p_hp_archammer-damage.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO", - "p_hp_archammer-damage.alo" - ] - }, - { - "id": "ALO04", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO references missing shader effect: Default.fx", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO", - "Default.fx" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\RV_BWING.ALO references missing proxy particle: pe_bwing_yellow.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\RV_BWING.ALO", - "pe_bwing_yellow.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_F9TZTRANSPORT.ALO references missing texture: W_TE_Rock_f_02_b.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_F9TZTRANSPORT.ALO", - "W_TE_Rock_f_02_b.tga" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO references missing proxy particle: p_ewok_drag_dirt.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO", - "p_ewok_drag_dirt.alo" - ] - }, - { - "id": "ALO04", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO references missing shader effect: Default.fx", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO", - "Default.fx" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_VENGEANCE.ALO references missing texture: W_TE_Rock_f_02_b.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_VENGEANCE.ALO", - "W_TE_Rock_f_02_b.tga" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UB_01_STATION_D.ALO references missing proxy particle: p_uwstation_death.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UB_01_STATION_D.ALO", - "p_uwstation_death.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UB_02_STATION_D.ALO references missing proxy particle: p_uwstation_death.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UB_02_STATION_D.ALO", - "p_uwstation_death.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UB_03_STATION_D.ALO references missing proxy particle: p_uwstation_death.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UB_03_STATION_D.ALO", - "p_uwstation_death.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UB_04_STATION_D.ALO references missing proxy particle: p_uwstation_death.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UB_04_STATION_D.ALO", - "p_uwstation_death.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UB_05_STATION_D.ALO references missing proxy particle: p_uwstation_death.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UB_05_STATION_D.ALO", - "p_uwstation_death.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO references missing proxy particle: lookat.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO", - "lookat.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO references missing proxy particle: p_ssd_debris.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO", - "p_ssd_debris.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_ECLIPSE.ALO references missing proxy particle: lookat.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_ECLIPSE.ALO", - "lookat.alo" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UI_SABOTEUR.ALO references missing proxy particle: p_desert_ground_dust.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UI_SABOTEUR.ALO", - "p_desert_ground_dust.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\NB_YSALAMIRI_TREE.ALO references missing texture: NB_YsalamiriTree_B.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\NB_YSALAMIRI_TREE.ALO", - "NB_YsalamiriTree_B.tga" - ] - }, - { - "id": "ALO03", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UI_IG88.ALO references missing proxy particle: p_desert_ground_dust.alo", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UI_IG88.ALO", - "p_desert_ground_dust.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\UV_KRAYTCLASSDESTROYER_TYBER.ALO references missing texture: W_TE_Rock_f_02_b.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\UV_KRAYTCLASSDESTROYER_TYBER.ALO", - "W_TE_Rock_f_02_b.tga" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_SwampGasEmit.ALO\u0027", - "severity": 0, - "assets": [ - "W_SwampGasEmit.ALO" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_Bush_Swmp00.ALO\u0027", - "severity": 0, - "assets": [ - "W_Bush_Swmp00.ALO" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_Volcano_Rock02.ALO\u0027", - "severity": 0, - "assets": [ - "W_Volcano_Rock02.ALO" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_Vol_Steam01.ALO\u0027", - "severity": 0, - "assets": [ - "W_Vol_Steam01.ALO" - ] + ], + "asset": "Probability" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RB_HYPERVELOCITYGUN.ALO" + ], + "asset": "p_smoke_small_thin2" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Fire_Medium.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Fire_Medium.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_Kamino_Reflect.ALO\u0027", - "severity": 0, - "assets": [ - "W_Kamino_Reflect.ALO" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EV_Stardestroyer_Warp.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EV_Stardestroyer_Warp.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_AllShaders.ALO\u0027", - "severity": 0, - "assets": [ - "W_AllShaders.ALO" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_GreyGroup.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_GreyGroup.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_03_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_03_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Biker_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Biker_Row.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027W_droid_steam.alo\u0027", - "severity": 0, - "assets": [ - "W_droid_steam.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Trooper_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Trooper_Row.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_bridge.alo\u0027", - "severity": 0, - "assets": [ - "Cin_bridge.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_rv_XWingProp.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_rv_XWingProp.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_DeathStar_High.alo\u0027", - "severity": 0, - "assets": [ - "Cin_DeathStar_High.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027p_splash_wake_lava.alo\u0027", + "severity": "Error", + "context": [], + "asset": "p_splash_wake_lava.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CINE_EV_StarDestroyer.ALO\u0027", - "severity": 0, - "assets": [ - "CINE_EV_StarDestroyer.ALO" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_LeverPanel.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_LeverPanel.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_smoke_small_thin2" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EI_Vader.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EI_Vader.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_Coruscant.alo\u0027", - "severity": 0, - "assets": [ - "Cin_Coruscant.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_Soldier.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_Planet_Alderaan_High.alo\u0027", - "severity": 0, - "assets": [ - "Cin_Planet_Alderaan_High.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_NavyRow.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_NavyRow.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027MODELS\u0027", + "severity": "Error", + "context": [], + "asset": "MODELS" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_droid_steam.alo\u0027", + "severity": "Error", + "context": [], + "asset": "W_droid_steam.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_Planet_Hoth_High.alo\u0027", - "severity": 0, - "assets": [ - "Cin_Planet_Hoth_High.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EV_lambdaShuttle_150.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EV_lambdaShuttle_150.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_EV_Stardestroyer_Warp.alo\u0027", - "severity": 0, - "assets": [ - "Cin_EV_Stardestroyer_Warp.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_DeathStar_Hangar.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_DeathStar_Hangar.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_Officer.alo\u0027", - "severity": 0, - "assets": [ - "Cin_Officer.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Fire_Huge.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Fire_Huge.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_CINE.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_ewok_drag_dirt\u0027 not found for model \u0027DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UI_EWOK_HANDLER.ALO" + ], + "asset": "p_ewok_drag_dirt" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_steam_small\u0027 not found for model \u0027DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RB_HEAVYVEHICLEFACTORY.ALO" + ], + "asset": "p_steam_small" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_hp_archammer-damage\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EV_ARCHAMMER.ALO" + ], + "asset": "p_hp_archammer-damage" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_02_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_02_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Rbel_grey.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_grey.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_explosion_small_delay00\u0027 not found for model \u0027DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EB_COMMANDCENTER.ALO" + ], + "asset": "p_explosion_small_delay00" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_REb_CelebCharacters.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_REb_CelebCharacters.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\UI_IG88.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UI_IG88.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027RV_nebulonb_D_death_00.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "RV_nebulonb_D_death_00.ALO" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027Cin_DStar_Dish_close.alo\u0027", - "severity": 0, - "assets": [ - "Cin_DStar_Dish_close.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_DStar_LeverPanel.alo\u0027", - "severity": 0, - "assets": [ - "Cin_DStar_LeverPanel.alo" - ] + "severity": "Error", + "context": [], + "asset": "Cin_DStar_Dish_close.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\UI_SABOTEUR.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UI_SABOTEUR.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_AllShaders.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_AllShaders.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027P_heat_small01\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_VCH.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_VCH.ALO" + ], + "asset": "P_heat_small01" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_NavyTrooper_Row.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_NavyTrooper_Row.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027Cin_DeathStar_Wall.alo\u0027", - "severity": 0, - "assets": [ - "Cin_DeathStar_Wall.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_DStar_protons.alo\u0027", - "severity": 0, - "assets": [ - "Cin_DStar_protons.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_DStar_TurretLasers.alo\u0027", - "severity": 0, - "assets": [ - "Cin_DStar_TurretLasers.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_EV_TieAdvanced.alo\u0027", - "severity": 0, - "assets": [ - "Cin_EV_TieAdvanced.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "severity": "Error", + "context": [], + "asset": "Cin_DeathStar_Wall.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_01_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_01_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027Cin_ImperialCraft.alo\u0027", - "severity": 0, - "assets": [ - "Cin_ImperialCraft.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "severity": "Error", + "context": [], + "asset": "Cin_ImperialCraft.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_05_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_05_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_ssd_debris\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_ECLIPSE_UC_DC.ALO" + ], + "asset": "p_ssd_debris" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_SKIPRAY.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_SKIPRAY.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_CINE_LUA.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027Cin_Shuttle_Tyderium.alo\u0027", - "severity": 0, - "assets": [ - "Cin_Shuttle_Tyderium.alo" - ] + "severity": "Error", + "context": [], + "asset": "Cin_Shuttle_Tyderium.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_p_proton_torpedo.alo\u0027", - "severity": 0, - "assets": [ - "CIN_p_proton_torpedo.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027w_planet_volcanic.alo\u0027", + "severity": "Error", + "context": [], + "asset": "w_planet_volcanic.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027lookat\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_ECLIPSE_UC.ALO" + ], + "asset": "lookat" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027pe_bwing_yellow\u0027 not found for model \u0027DATA\\ART\\MODELS\\RV_BWING.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RV_BWING.ALO" + ], + "asset": "pe_bwing_yellow" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_EV_TieAdvanced.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_EV_TieAdvanced.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_DeathStar_Hangar.alo\u0027", - "severity": 0, - "assets": [ - "CIN_DeathStar_Hangar.alo" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_SITH_LEFTHALL.ALO references missing texture: Cin_Reb_CelebHall_Wall.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_SITH_LEFTHALL.ALO", - "Cin_Reb_CelebHall_Wall.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_SITH_LEFTHALL.ALO references missing texture: Cin_Reb_CelebHall_Wall_B.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_SITH_LEFTHALL.ALO", - "Cin_Reb_CelebHall_Wall_B.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_SITH_CONSOLE.ALO references missing texture: Cin_Reb_CelebHall_Wall.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_SITH_CONSOLE.ALO", - "Cin_Reb_CelebHall_Wall.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_SITH_CONSOLE.ALO references missing texture: Cin_Reb_CelebHall_Wall_B.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_SITH_CONSOLE.ALO", - "Cin_Reb_CelebHall_Wall_B.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_TILE.ALO references missing texture: Cin_Reb_CelebHall_Wall_B.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_TILE.ALO", - "Cin_Reb_CelebHall_Wall_B.tga" - ] - }, - { - "id": "ALO02", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "DATA\\ART\\MODELS\\W_TILE.ALO references missing texture: Cin_Reb_CelebHall_Wall.tga", - "severity": 0, - "assets": [ - "DATA\\ART\\MODELS\\W_TILE.ALO", - "Cin_Reb_CelebHall_Wall.tga" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Officer.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Officer.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027P_mptl-2a_Die\u0027 not found for model \u0027DATA\\ART\\MODELS\\RV_MPTL-2A.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RV_MPTL-2A.ALO" + ], + "asset": "P_mptl-2a_Die" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_CRUSADERCLASSCORVETTE.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Reb_CelebHall.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Reb_CelebHall.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027lookat\u0027 not found for model \u0027DATA\\ART\\MODELS\\UV_ECLIPSE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UV_ECLIPSE.ALO" + ], + "asset": "lookat" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027w_sith_arch.alo\u0027", - "severity": 0, - "assets": [ - "w_sith_arch.alo" - ] + "severity": "Error", + "context": [], + "asset": "w_sith_arch.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Trooper_Row.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Trooper_Row.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DeathStar_High.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DeathStar_High.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_prison_light\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_prison_light" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_SwampGasEmit.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_SwampGasEmit.ALO" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Biker_Row.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Biker_Row.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_protons.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_protons.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027CIN_Officer_Row.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Officer_Row.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_NavyTrooper_Row.alo\u0027", - "severity": 0, - "assets": [ - "CIN_NavyTrooper_Row.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Lambda_Mouth.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Lambda_Mouth.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Lambda_Head.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Lambda_Head.alo" - ] + "severity": "Error", + "context": [], + "asset": "CIN_Officer_Row.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Reb_CelebHall.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Reb_CelebHall.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_REb_CelebCharacters.alo\u0027", - "severity": 0, - "assets": [ - "CIN_REb_CelebCharacters.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Rbel_NavyRow.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Rbel_NavyRow.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Rbel_Soldier.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027CIN_Rbel_Soldier_Group.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Rbel_Soldier_Group.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Rbel_grey.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Rbel_grey.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Rbel_GreyGroup.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Rbel_GreyGroup.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_EV_lambdaShuttle_150.alo\u0027", - "severity": 0, - "assets": [ - "Cin_EV_lambdaShuttle_150.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027Cin_EI_Vader.alo\u0027", - "severity": 0, - "assets": [ - "Cin_EI_Vader.alo" - ] - }, - { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "severity": "Error", + "context": [], + "asset": "CIN_Rbel_Soldier_Group.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\EI_MARAJADE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EI_MARAJADE.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], "message": "Unable to find .ALO file \u0027Cin_EI_Palpatine.alo\u0027", - "severity": 0, - "assets": [ - "Cin_EI_Palpatine.alo" - ] + "severity": "Error", + "context": [], + "asset": "Cin_EI_Palpatine.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EV_TIE_LANCET.ALO" + ], + "asset": "Default.fx" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Planet_Alderaan_High.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Planet_Alderaan_High.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_HIGH.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_HIGH.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_bomb_spin\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_THERMAL_DETONATOR_EMPIRE.ALO" + ], + "asset": "p_bomb_spin" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_DStar_TurretLasers.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_DStar_TurretLasers.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin4\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_PRISON.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_PRISON.ALO" + ], + "asset": "p_smoke_small_thin4" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Vol_Steam01.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Vol_Steam01.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_desert_ground_dust\u0027 not found for model \u0027DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\RI_KYLEKATARN.ALO" + ], + "asset": "p_desert_ground_dust" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Planet_Hoth_High.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Planet_Hoth_High.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_LOW.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_LOW.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_uwstation_death\u0027 not found for model \u0027DATA\\ART\\MODELS\\UB_04_STATION_D.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\UB_04_STATION_D.ALO" + ], + "asset": "p_uwstation_death" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_Coruscant.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_Coruscant.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_smoke_small_thin2\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_MONCAL_BUILDING.ALO" + ], + "asset": "p_smoke_small_thin2" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_p_proton_torpedo.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_p_proton_torpedo.alo" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Fire_Huge.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Fire_Huge.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027Cin_bridge.alo\u0027", + "severity": "Error", + "context": [], + "asset": "Cin_bridge.alo" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_explosion_smoke_small_thin5\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_NOGHRI_HUT.ALO" + ], + "asset": "p_explosion_smoke_small_thin5" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Kamino_Reflect.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Kamino_Reflect.ALO" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Fire_Medium.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Fire_Medium.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CINE_EV_StarDestroyer.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "CINE_EV_StarDestroyer.ALO" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027p_cold_tiny01\u0027 not found for model \u0027DATA\\ART\\MODELS\\NB_SCH.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\NB_SCH.ALO" + ], + "asset": "p_cold_tiny01" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Proxy particle \u0027Lensflare0\u0027 not found for model \u0027DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO\u0027", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\W_STARS_MEDIUM.ALO" + ], + "asset": "Lensflare0" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Volcano_Rock02.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Volcano_Rock02.ALO" }, { - "id": "ALO00", - "verifier": "AET.ModVerify.Verifiers.ReferencedModelsVerifier", - "message": "Unable to find .ALO file \u0027CIN_Probe_Droid.alo\u0027", - "severity": 0, - "assets": [ - "CIN_Probe_Droid.alo" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Lambda_Head.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Lambda_Head.alo" }, { - "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027C000_DST0102_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "C000_DST0102_ENG.WAV" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027W_Bush_Swmp00.ALO\u0027", + "severity": "Error", + "context": [], + "asset": "W_Bush_Swmp00.ALO" }, { - "id": "WAV03", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027WIND_GUST_1_STEREO.WAV\u0027 is not mono audio.", - "severity": 0, - "assets": [ - "WIND_GUST_1_STEREO.WAV" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Probe_Droid.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Probe_Droid.alo" }, { - "id": "WAV03", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027WIND_GUST_2_STEREO.WAV\u0027 is not mono audio.", - "severity": 0, - "assets": [ - "WIND_GUST_2_STEREO.WAV" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Unable to find .ALO file \u0027CIN_Lambda_Mouth.alo\u0027", + "severity": "Error", + "context": [], + "asset": "CIN_Lambda_Mouth.alo" }, { - "id": "WAV03", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027WIND_GUST_3_STEREO.WAV\u0027 is not mono audio.", - "severity": 0, - "assets": [ - "WIND_GUST_3_STEREO.WAV" - ] + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.ReferencedModelsVerifier", + "AET.ModVerify.Verifiers.Commons.SingleModelVerifier" + ], + "message": "Shader effect \u0027Default.fx\u0027 not found for model \u0027DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO\u0027.", + "severity": "Error", + "context": [ + "DATA\\ART\\MODELS\\EV_MDU_SENSORNODE.ALO" + ], + "asset": "Default.fx" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027TESTUNITMOVE_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "TESTUNITMOVE_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0106_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0106_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027AMB_DES_CLEAR_LOOP_1.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "AMB_DES_CLEAR_LOOP_1.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_MAL0503_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_MAL0503_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027AMB_URB_CLEAR_LOOP_1.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "AMB_URB_CLEAR_LOOP_1.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0315_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0315_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_TMC0212_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_TMC0212_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_3.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "FS_BEETLE_3.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_MAL0503_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_MAL0503_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0314_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0314_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_DEF3006_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_DEF3006_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0109_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0109_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_DEF3106_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_DEF3106_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_2.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "FS_BEETLE_2.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0101_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0101_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0202_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0202_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0102_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0102_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0204_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0204_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0103_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0103_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0304_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0304_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0104_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0104_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027EGL_STAR_VIPER_SPINNING_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "EGL_STAR_VIPER_SPINNING_1.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0105_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0105_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0402_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0402_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0106_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0106_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0112_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0112_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0107_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0107_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_ARC3106_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_ARC3106_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0108_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0108_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0201_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0201_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0109_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0109_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_ARC3104_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_ARC3104_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0110_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0110_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0311_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0311_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], "message": "Audio file \u0027U000_LEI0111_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0111_ENG.WAV" - ] + "severity": "Error", + "context": [], + "asset": "U000_LEI0111_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0112_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0112_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0114_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0114_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0113_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0113_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_DEF3106_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_DEF3106_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0114_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0114_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0110_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0110_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0115_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0115_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0503_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0503_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0201_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0201_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_ARC3105_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_ARC3105_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0202_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0202_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0305_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0305_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0203_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0203_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0208_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0208_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0204_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0204_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0313_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0313_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0205_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0205_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0308_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0308_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], "message": "Audio file \u0027U000_LEI0206_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0206_ENG.WAV" - ] + "severity": "Error", + "context": [], + "asset": "U000_LEI0206_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0207_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0207_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0105_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0105_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0208_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0208_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "FS_BEETLE_1.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0209_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0209_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027FS_BEETLE_4.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "FS_BEETLE_4.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0210_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0210_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0209_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0209_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0211_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0211_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0501_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0501_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0212_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0212_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027TESTUNITMOVE_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "TESTUNITMOVE_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0213_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0213_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0312_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0312_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0215_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0215_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0404_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0404_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0301_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0301_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0603_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0603_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0303_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0303_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0108_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0108_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0304_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0304_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0306_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0306_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0305_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0305_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0303_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0303_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0306_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0306_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027AMB_DES_CLEAR_LOOP_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "AMB_DES_CLEAR_LOOP_1.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], "message": "Audio file \u0027U000_LEI0307_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0307_ENG.WAV" - ] + "severity": "Error", + "context": [], + "asset": "U000_LEI0307_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0308_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0308_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0102_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0102_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0309_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0309_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027C000_DST0102_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "C000_DST0102_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0311_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0311_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0205_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0205_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0312_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0312_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0504_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0504_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0313_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0313_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0210_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0210_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0314_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0314_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0403_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0403_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0315_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0315_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0211_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0211_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0401_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0401_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0604_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0604_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0402_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0402_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_TMC0212_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_TMC0212_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0403_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0403_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0309_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0309_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0404_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0404_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_DEF3006_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_DEF3006_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0501_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0501_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0101_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0101_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0502_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0502_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0203_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0203_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0503_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0503_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0401_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0401_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0504_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0504_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0602_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0602_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0601_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0601_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_MCF1601_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_MCF1601_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0602_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0602_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0115_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0115_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0603_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0603_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0207_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0207_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_LEI0604_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_LEI0604_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0502_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0502_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_ARC3104_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_ARC3104_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0113_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0113_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_ARC3105_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_ARC3105_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0213_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0213_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_ARC3106_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_ARC3106_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0104_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0104_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027U000_MCF1601_ENG.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "U000_MCF1601_ENG.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0301_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0301_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027FS_BEETLE_1.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "FS_BEETLE_1.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0601_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0601_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027FS_BEETLE_2.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "FS_BEETLE_2.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0212_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0212_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027FS_BEETLE_3.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "FS_BEETLE_3.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0103_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0103_ENG.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027FS_BEETLE_4.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "FS_BEETLE_4.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027AMB_URB_CLEAR_LOOP_1.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "AMB_URB_CLEAR_LOOP_1.WAV" }, { "id": "WAV00", - "verifier": "AET.ModVerify.Verifiers.AudioFilesVerifier", - "message": "Audio file \u0027EGL_STAR_VIPER_SPINNING_1.WAV\u0027 could not be found.", - "severity": 0, - "assets": [ - "EGL_STAR_VIPER_SPINNING_1.WAV" - ] + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0107_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0107_ENG.WAV" + }, + { + "id": "WAV00", + "verifiers": [ + "AET.ModVerify.Verifiers.AudioFilesVerifier" + ], + "message": "Audio file \u0027U000_LEI0215_ENG.WAV\u0027 could not be found.", + "severity": "Error", + "context": [], + "asset": "U000_LEI0215_ENG.WAV" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027i_button_petro_sliver.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_MENU_PETRO_LOGO", + "MegaTexture" + ], + "asset": "i_button_petro_sliver.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027underworld_logo_off.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" + ], + "asset": "underworld_logo_off.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027i_dialogue_button_large_middle_off.tga\u0027 at location \u0027Repository\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_B_BUTTON_BIG", + "Repository" + ], + "asset": "i_dialogue_button_large_middle_off.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027underworld_logo_rollover.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" + ], + "asset": "underworld_logo_rollover.tga" + }, + { + "id": "FILE00", + "verifiers": [ + "AET.ModVerify.Verifiers.GuiDialogs.GuiDialogsVerifier" + ], + "message": "Could not find GUI texture \u0027underworld_logo_selected.tga\u0027 at location \u0027MegaTexture\u0027.", + "severity": "Error", + "context": [ + "IDC_PLAY_FACTION_A_BUTTON_BIG", + "MegaTexture" + ], + "asset": "underworld_logo_selected.tga" } ] } \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index 4bc8e0c..0000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,46 +0,0 @@ - - - - $(MSBuildThisFileDirectory)../ - true - true - $(RepoRootPath) - $(RepoRootPath)bin\Packages\$(Configuration)\ - - - - Alamo Engine Tools and Contributors - Copyright © 2024 Alamo Engine Tools and contributors. All rights reserved. - https://github.com/AlamoEngine-Tools/ModVerify - $(RepoRootPath)LICENSE - MIT - https://github.com/AlamoEngine-Tools/ModVerify - git - Alamo Engine Tools - README.md - aet.png - - - - latest - disable - enable - True - latest - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - \ No newline at end of file diff --git a/src/ModVerify.CliApp/ConsoleUtilities.cs b/src/ModVerify.CliApp/ConsoleUtilities.cs new file mode 100644 index 0000000..db3e741 --- /dev/null +++ b/src/ModVerify.CliApp/ConsoleUtilities.cs @@ -0,0 +1,94 @@ +using System; + +namespace AET.ModVerifyTool; + +internal static class ConsoleUtilities +{ + public delegate bool ConsoleQuestionValueFactory(string input, out T value); + + public static void WriteHorizontalLine(char lineChar = '─', int length = 20) + { + var line = new string(lineChar, length); + Console.WriteLine(line); + } + + public static void WriteHeader() + { + Console.WriteLine("***********************************"); + Console.WriteLine("***********************************"); + Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Mod Verify")); + Console.WriteLine("***********************************"); + Console.WriteLine("***********************************"); + Console.WriteLine(" by AnakinRaW"); + Console.WriteLine(); + Console.WriteLine(); + } + + public static void WriteApplicationFailure() + { + Console.WriteLine(); + WriteHorizontalLine('*'); + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine(" ModVerify Failure! "); + Console.ResetColor(); + WriteHorizontalLine('*'); + Console.WriteLine(); + Console.WriteLine("The application encountered an unexpected error and will terminate now!"); + Console.WriteLine(); + } + + public static T UserQuestionOnSameLine(string question, ConsoleQuestionValueFactory inputCorrect) + { + while (true) + { + var promptLeft = 0; + var promptTop = Console.CursorTop; + + Console.SetCursorPosition(promptLeft, promptTop); + Console.Write(question); + Console.SetCursorPosition(promptLeft + question.Length, promptTop); + + var input = ReadLineInline(); + + if (!inputCorrect(input, out var result)) + { + Console.SetCursorPosition(0, promptTop); + Console.Write(new string(' ', Console.WindowWidth - 1)); + continue; + } + + Console.WriteLine(); + return result; + } + } + + private static string ReadLineInline() + { + var input = ""; + while (true) + { + var key = Console.ReadKey(intercept: true); + + if (key.Key == ConsoleKey.Enter) + break; + + if (key.Key == ConsoleKey.Backspace) + { + if (input.Length > 0) + { + input = input[..^1]; + Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); + Console.Write(' '); + Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); + } + } + else if (!char.IsControl(key.KeyChar)) + { + input += key.KeyChar; + Console.Write(key.KeyChar); + } + } + + return input; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs index 107bbc0..7a29368 100644 --- a/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs +++ b/src/ModVerify.CliApp/ModSelectors/ConsoleModSelector.cs @@ -29,8 +29,7 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin list.Add(finderResult.Game); Console.WriteLine(); - Console.WriteLine("================="); - Console.WriteLine(); + ConsoleUtilities.WriteHorizontalLine(); Console.WriteLine($"0: {game.Name}"); var counter = 1; @@ -57,7 +56,7 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin var fallbackGame = finderResult.FallbackGame; list.Add(fallbackGame); - Console.WriteLine("_________________"); + ConsoleUtilities.WriteHorizontalLine('_'); Console.WriteLine(fallbackGame.Type == GameType.Eaw ? $"{counter++}: {fallbackGame.Name} [Not yet supported]" : $"{counter++}: {fallbackGame.Name}"); @@ -82,7 +81,7 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin if (workshopMods.Count > 0) { - Console.WriteLine("_________________"); + ConsoleUtilities.WriteHorizontalLine('_'); Console.WriteLine("Workshop Items:"); foreach (var mod in workshopMods) { @@ -90,22 +89,20 @@ private static IPhysicalPlayableObject SelectPlayableObject(GameFinderResult fin list.Add(mod); } } - - Console.WriteLine(); - Console.WriteLine("================="); + + ConsoleUtilities.WriteHorizontalLine(); try { - while (true) - { - Console.Write("Select a game or mod: "); - var numberString = Console.ReadLine(); - - if (!int.TryParse(numberString, out var number)) - continue; - if (number < list.Count) - return list[number]; - } + var selected = ConsoleUtilities.UserQuestionOnSameLine("Select a game or mod: ", + (string input, out int value) => + { + if (!int.TryParse(input, out value)) + return false; + + return value <= list.Count; + }); + return list[selected]; } finally { diff --git a/src/ModVerify.CliApp/ModVerify.CliApp.csproj b/src/ModVerify.CliApp/ModVerify.CliApp.csproj index 3399f49..61c0a3b 100644 --- a/src/ModVerify.CliApp/ModVerify.CliApp.csproj +++ b/src/ModVerify.CliApp/ModVerify.CliApp.csproj @@ -1,7 +1,6 @@  - false net9.0;net48 Exe AET.ModVerifyTool @@ -46,18 +45,16 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - - - + + true + - + + diff --git a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs index 43f9ca1..d239112 100644 --- a/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs +++ b/src/ModVerify.CliApp/Options/CommandLine/BaseModVerifyOptions.cs @@ -52,8 +52,8 @@ internal abstract class BaseModVerifyOptions "Multiple paths can be separated using the ';' (semicolon) character.")] public IList? AdditionalFallbackPath { get; set; } - [Option("sequential", Default = false, - HelpText = "When set, game verifiers will run sequentially and not in parallel. " + - "This increases analysis duration but may help in debugging sessions.")] - public bool Sequential { get; set; } + [Option("parallel", Default = false, + HelpText = "When set, game verifiers will run in parallel. " + + "While this may reduce analysis time, console output might be harder to read.")] + public bool Parallel { get; set; } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index a82a9ef..7aa6802 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -1,9 +1,12 @@ -using AET.ModVerify.Reporting; +using AET.ModVerify; +using AET.ModVerify.Reporting; using AET.ModVerify.Reporting.Reporters; using AET.ModVerify.Reporting.Reporters.JSON; using AET.ModVerify.Reporting.Reporters.Text; using AET.ModVerify.Reporting.Settings; using AET.ModVerifyTool.Options; +using AET.ModVerifyTool.Options.CommandLine; +using AET.ModVerifyTool.Updates; using AET.SteamAbstraction; using AnakinRaW.CommonUtilities.Hashing; using AnakinRaW.CommonUtilities.Registry; @@ -14,10 +17,12 @@ 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; using PG.StarWarsGame.Files.XML; +using PG.StarWarsGame.Files.XML.Parsers; using PG.StarWarsGame.Infrastructure; using PG.StarWarsGame.Infrastructure.Services.Detection; using PG.StarWarsGame.Infrastructure.Services.Name; @@ -26,30 +31,22 @@ using Serilog.Filters; using Serilog.Sinks.SystemConsole.Themes; using System; -using System.Dynamic; using System.IO.Abstractions; using System.Threading.Tasks; -using AET.ModVerify; -using AET.ModVerify.Pipeline; -using AET.ModVerifyTool.Options.CommandLine; using Testably.Abstractions; using ILogger = Serilog.ILogger; -using VerifyVerbOption = AET.ModVerifyTool.Options.CommandLine.VerifyVerbOption; namespace AET.ModVerifyTool; internal class Program { - private const string EngineParserNamespace = "PG.StarWarsGame.Engine.Xml.Parsers"; - private const string ParserNamespace = "PG.StarWarsGame.Files.XML.Parsers"; - private const string GameInfrastructureNamespace = "PG.StarWarsGame.Infrastructure"; - private static readonly string GameVerifierStepNamespace = typeof(GameVerifierPipelineStep).FullName!; - + private static readonly string EngineParserNamespace = typeof(XmlObjectParser<>).Namespace!; + private static readonly string ParserNamespace = typeof(PetroglyphXmlFileParser<>).Namespace!; private static readonly string ModVerifyRootNameSpace = typeof(Program).Namespace!; private static async Task Main(string[] args) { - PrintHeader(); + ConsoleUtilities.WriteHeader(); var result = 0; @@ -87,13 +84,17 @@ private static async Task Run(BaseModVerifyOptions options) { var settings = new SettingsBuilder(coreServices).BuildSettings(options); var services = CreateAppServices(coreServiceCollection, settings); - var verifier = new ModVerifyApp(settings, services); + if (!settings.Offline) + await CheckForUpdate(services, logger); + + + var verifier = new ModVerifyApp(settings, services); return await verifier.RunApplication().ConfigureAwait(false); } catch (Exception e) { - PrintApplicationFailure(); + ConsoleUtilities.WriteApplicationFailure(); logger?.LogCritical(e, e.Message); return e.HResult; } @@ -107,6 +108,36 @@ private static async Task Run(BaseModVerifyOptions options) } } + private static async Task CheckForUpdate(IServiceProvider services, Microsoft.Extensions.Logging.ILogger? logger) + { + var updateChecker = new ModVerifyUpdaterChecker(services); + + logger?.LogDebug("Checking for available update"); + + try + { + var updateInfo = await updateChecker.CheckForUpdateAsync().ConfigureAwait(false); + if (updateInfo.IsUpdateAvailable) + { + ConsoleUtilities.WriteHorizontalLine(); + + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine("New Update Available!"); + Console.ResetColor(); + + Console.WriteLine($"Version: {updateInfo.NewVersion}, Download here: {updateInfo.DownloadLink}"); + ConsoleUtilities.WriteHorizontalLine(); + Console.WriteLine(); + + } + } + catch(Exception e) + { + logger?.LogWarning($"Unable to check for updates due to an internal error: {e.Message}"); + logger?.LogTrace(e, "Checking for update failed: " + e.Message); + } + } + private static IServiceCollection CreateCoreServices(bool verboseLogging) { var fileSystem = new RealFileSystem(); @@ -185,13 +216,14 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem #if DEBUG logLevel = LogEventLevel.Debug; loggingBuilder.AddDebug(); -#else +#endif + if (verbose) { - logLevel = LogLevel.Verbose; + logLevel = LogEventLevel.Verbose; loggingBuilder.AddDebug(); } -#endif + var fileLogger = SetupFileLogging(fileSystem, logLevel); loggingBuilder.AddSerilog(fileLogger); @@ -213,22 +245,6 @@ private static void ConfigureLogging(ILoggingBuilder loggingBuilder, IFileSystem return false; }) - //.Filter.ByExcluding(x => - //{ - // if (!x.Properties.TryGetValue("SourceContext", out var value)) - // return true; - // var source = value.ToString().AsSpan().Trim('\"'); - - // if (source.StartsWith(EngineParserNamespace.AsSpan())) - // return true; - // if (source.StartsWith(ParserNamespace.AsSpan())) - // return true; - // if (source.StartsWith(GameInfrastructureNamespace.AsSpan())) - // return true; - // if (source.StartsWith(GameVerifierStepNamespace.AsSpan())) - // return true; - // return false; - //}) .CreateLogger(); loggingBuilder.AddSerilog(cLogger); } @@ -255,29 +271,4 @@ private static bool IsXmlParserLogging(LogEvent logEvent) { return Matching.FromSource(ParserNamespace)(logEvent) || Matching.FromSource(EngineParserNamespace)(logEvent); } - - private static void PrintHeader() - { - Console.WriteLine("***********************************"); - Console.WriteLine("***********************************"); - Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Mod Verify")); - Console.WriteLine("***********************************"); - Console.WriteLine("***********************************"); - Console.WriteLine(" by AnakinRaW"); - Console.WriteLine(); - Console.WriteLine(); - } - - private static void PrintApplicationFailure() - { - Console.WriteLine(); - Console.WriteLine("********************"); - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine(" ModVerify Failure! "); - Console.ResetColor(); - Console.WriteLine("********************"); - Console.WriteLine(); - Console.WriteLine("The application encountered an unexpected error and will terminate now!"); - Console.WriteLine(); - } } \ No newline at end of file diff --git a/src/ModVerify.CliApp/Properties/launchSettings.json b/src/ModVerify.CliApp/Properties/launchSettings.json index d5306fa..1e01ece 100644 --- a/src/ModVerify.CliApp/Properties/launchSettings.json +++ b/src/ModVerify.CliApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Interactive Verify": { "commandName": "Project", - "commandLineArgs": "verify -o verifyResults --minFailSeverity Information --baseline focBaseline.json --offline --sequential" + "commandLineArgs": "verify -o verifyResults --minFailSeverity Information -v --baseline focBaseline.json --offline" }, "Interactive Baseline": { "commandName": "Project", diff --git a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs index a6533ca..37d3ca2 100644 --- a/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs +++ b/src/ModVerify.CliApp/Reporting/VerifyConsoleProgressReporter.cs @@ -1,9 +1,9 @@ -using AET.ModVerify.Pipeline; -using AnakinRaW.CommonUtilities; +using AnakinRaW.CommonUtilities; using AnakinRaW.CommonUtilities.SimplePipeline.Progress; using ShellProgressBar; using System; using System.Threading; +using AET.ModVerify.Pipeline.Progress; namespace AET.ModVerifyTool.Reporting; @@ -28,22 +28,27 @@ public void ReportError(string message, string? errorLine) public void Report(string message, double progress) { - Report(message, progress, VerifyProgress.ProgressType, default); + Report(progress, message, VerifyProgress.ProgressType, default); } - public void Report(string progressText, double progress, ProgressType type, VerifyProgressInfo detailedProgress) + public void Report(double progress, string? progressText, ProgressType type, VerifyProgressInfo detailedProgress) { if (type != VerifyProgress.ProgressType) return; var progressBar = EnsureProgressBar(); + // TODO: Only recognize detailed mode + progressBar.Message = progressText; + if (progress >= 1.0) progressBar.Message = $"Verified '{toVerifyName}'"; var cpb = progressBar.AsProgress(); cpb.Report(progress); - progressBar.WriteLine(progressText); + + // TODO: Only in verbose mode + //progressBar.WriteLine(progressText); } protected override void DisposeResources() diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index abe1298..c9b585a 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -44,7 +44,7 @@ private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) { VerifyPipelineSettings = new VerifyPipelineSettings { - ParallelVerifiers = verifyOptions.Sequential ? 1 : 4, + ParallelVerifiers = verifyOptions.Parallel ? 4 : 1, VerifiersProvider = new DefaultGameVerifiersProvider(), FailFast = verifyOptions.FailFast, GameVerifySettings = new GameVerifySettings @@ -87,7 +87,7 @@ private ModVerifyAppSettings BuildFromCreateBaselineVerb(CreateBaselineVerbOptio { VerifyPipelineSettings = new VerifyPipelineSettings { - ParallelVerifiers = baselineVerb.Sequential ? 1 : 4, + ParallelVerifiers = baselineVerb.Parallel ? 4 : 1, GameVerifySettings = new GameVerifySettings { IgnoreAsserts = false, diff --git a/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs b/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs new file mode 100644 index 0000000..968e6a9 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/GithubReleaseEntry.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace AET.ModVerifyTool.Updates; + +[JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Skip)] +[method: JsonConstructor] +internal sealed class GithubReleaseEntry(string tag, string branch, bool isPrerelease) +{ + [JsonPropertyName("tag_name")] + public string Tag { get; } = tag; + + [JsonPropertyName("target_commitish")] + public string Branch { get; } = branch; + + [JsonPropertyName("prerelease")] + public bool IsPrerelease { get; } = isPrerelease; +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/GithubReleaseList.cs b/src/ModVerify.CliApp/Updates/GithubReleaseList.cs new file mode 100644 index 0000000..a54edfb --- /dev/null +++ b/src/ModVerify.CliApp/Updates/GithubReleaseList.cs @@ -0,0 +1,5 @@ +using System.Collections.Generic; + +namespace AET.ModVerifyTool.Updates; + +internal sealed class GithubReleaseList : List; \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs new file mode 100644 index 0000000..d0ab59b --- /dev/null +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterChecker.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Semver; + +namespace AET.ModVerifyTool.Updates; + +internal sealed class ModVerifyUpdaterChecker +{ + private readonly ILogger? _logger; + + public ModVerifyUpdaterChecker(IServiceProvider serviceProvider) + { + _logger = serviceProvider.GetService()?.CreateLogger(GetType()); + } + + public async Task CheckForUpdateAsync() + { + var githubReleases = await DownloadReleaseList().ConfigureAwait(false); + + var branch = ModVerifyUpdaterInformation.BranchName; + var latestRelease = githubReleases.FirstOrDefault(r => r.Branch == branch); + + if (latestRelease == null) + throw new InvalidOperationException($"Unable to find a release for branch '{branch}'."); + + if (!SemVersion.TryParse(latestRelease.Tag, SemVersionStyles.Any, out var latestVersion)) + throw new InvalidOperationException($"Cannot create a version from tag '{latestRelease.Tag}'."); + + var currentVersion = ModVerifyUpdaterInformation.CurrentVersion; + if (currentVersion is null) + throw new InvalidOperationException("Unable to get current version."); + + if (SemVersion.ComparePrecedence(currentVersion, latestVersion) >= 0) + { + _logger?.LogDebug($"No update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + return default; + } + + _logger?.LogDebug($"Update available - [Current Version = {currentVersion}], [Available Version = {latestVersion}]"); + return new UpdateInfo + { + DownloadLink = ModVerifyUpdaterInformation.ModVerifyReleasesDownloadLink, + IsUpdateAvailable = true, + NewVersion = latestVersion.ToString() + }; + } + + private static async Task DownloadReleaseList() + { + using var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ModVerifyUpdaterInformation.UserAgent); + using var downloadStream = await httpClient.GetStreamAsync(ModVerifyUpdaterInformation.GithubReleasesApiLink).ConfigureAwait(false); + + using var jsonStream = new MemoryStream(); + await downloadStream.CopyToAsync(jsonStream).ConfigureAwait(false); + jsonStream.Seek(0, SeekOrigin.Begin); + + var releases = await JsonSerializer.DeserializeAsync(jsonStream).ConfigureAwait(false); + return releases ?? throw new InvalidOperationException("Unable to deserialize releases."); + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs new file mode 100644 index 0000000..2fa1022 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/ModVerifyUpdaterInformation.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; +using Semver; + +namespace AET.ModVerifyTool.Updates; + +internal static class ModVerifyUpdaterInformation +{ + public const string BranchName = "main"; + public const string GithubReleasesApiLink = "https://api.github.com/repos/AlamoEngine-Tools/ModVerify/releases"; + public const string ModVerifyReleasesDownloadLink = "https://github.com/AlamoEngine-Tools/ModVerify/releases/latest"; + public const string UserAgent = "AET.Modifo"; + + public static readonly SemVersion? CurrentVersion; + + static ModVerifyUpdaterInformation() + { + var currentAssembly = typeof(ModVerifyUpdaterInformation).Assembly; + var fi = FileVersionInfo.GetVersionInfo(currentAssembly.Location); + SemVersion.TryParse(fi.ProductVersion, SemVersionStyles.Any, out var currentVersion); + CurrentVersion = currentVersion; + } +} \ No newline at end of file diff --git a/src/ModVerify.CliApp/Updates/UpdateInfo.cs b/src/ModVerify.CliApp/Updates/UpdateInfo.cs new file mode 100644 index 0000000..b6a6505 --- /dev/null +++ b/src/ModVerify.CliApp/Updates/UpdateInfo.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + +namespace AET.ModVerifyTool.Updates; + +internal readonly struct UpdateInfo +{ + public string DownloadLink { get; init; } + + public string? NewVersion { get; init; } + + [MemberNotNullWhen(true, nameof(DownloadLink), nameof(NewVersion))] + public bool IsUpdateAvailable { get; init; } + +} \ No newline at end of file diff --git a/src/ModVerify/ModVerify.csproj b/src/ModVerify/ModVerify.csproj index 7f0ed29..e5f5b7d 100644 --- a/src/ModVerify/ModVerify.csproj +++ b/src/ModVerify/ModVerify.csproj @@ -22,26 +22,27 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - + + - + diff --git a/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs index b74296d..26b3893 100644 --- a/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs +++ b/src/ModVerify/Pipeline/DefaultGameVerifiersProvider.cs @@ -18,6 +18,6 @@ public IEnumerable GetVerifiers( 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 CommandBarVerifier(database, settings, serviceProvider); } } \ No newline at end of file diff --git a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs index e0ab783..a27ee9b 100644 --- a/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs +++ b/src/ModVerify/Pipeline/GameVerifierPipelineStep.cs @@ -4,8 +4,8 @@ using AnakinRaW.CommonUtilities.SimplePipeline.Steps; using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Threading; +using AET.ModVerify.Pipeline.Progress; namespace AET.ModVerify.Pipeline; @@ -27,13 +27,13 @@ protected override void RunCore(CancellationToken token) try { Logger?.LogDebug($"Running verifier '{GameVerifier.FriendlyName}'..."); - ReportProgress(new ProgressEventArgs("Started", 0.0)); + ReportProgress(new ProgressEventArgs(0.0, "Started")); GameVerifier.Progress += OnVerifyProgress; GameVerifier.Verify(token); Logger?.LogDebug($"Finished verifier '{GameVerifier.FriendlyName}'"); - ReportProgress(new ProgressEventArgs("Finished", 1.0)); + ReportProgress(new ProgressEventArgs(1.0, "Finished")); } finally { @@ -44,7 +44,7 @@ protected override void RunCore(CancellationToken token) private void OnVerifyProgress(object _, ProgressEventArgs e) { if (e.Progress > 1.0) - e = new ProgressEventArgs(e.ProgressText, 1.0, e.ProgressInfo); + e = new ProgressEventArgs(1.0, e.ProgressText, e.ProgressInfo); ReportProgress(e); } @@ -52,69 +52,4 @@ private void ReportProgress(ProgressEventArgs e) { Progress?.Invoke(this, e); } -} - -public static class VerifyProgress -{ - public static readonly ProgressType ProgressType = new() - { - Id = "Verify", - DisplayName = "Verify" - }; -} - -public interface IVerifyProgressReporter : IProgressReporter; - -internal class AggregatedVerifyProgressReporter( - IVerifyProgressReporter progressReporter, - IEnumerable steps) - : AggregatedProgressReporter(progressReporter, steps) -{ - private readonly object _syncLock = new(); - - private readonly HashSet _completedSteps = new(); - - private long _totalProgressSize; - - protected override string GetProgressText(GameVerifierPipelineStep step, string progressText) - { - return $"Verifier '{step.GameVerifier.FriendlyName}': {progressText}"; - } - - protected override ProgressEventArgs CalculateAggregatedProgress( - GameVerifierPipelineStep step, - ProgressEventArgs progress) - { - lock (_syncLock) - { - var currentStepProgressSize = (long)(progress.Progress * step.Size); - var completed = currentStepProgressSize >= step.Size; - - var progressInfo = new VerifyProgressInfo - { - TotalVerifiers = TotalStepCount, - }; - - if (!completed || _completedSteps.Add(step)) - _totalProgressSize += (long)(progress.Progress * step.Size); - - return new ProgressEventArgs(progress.ProgressText, GetTotalProgress(), progressInfo); - } - } - - private double GetTotalProgress() - { - return Math.Min((double)_totalProgressSize / TotalSize, 0.99); - } - - protected override void DisposeResources() - { - base.DisposeResources(); - _completedSteps.Clear(); - } -} - -public struct VerifyProgressInfo -{ - public int TotalVerifiers { get; internal set; } } \ No newline at end of file diff --git a/src/ModVerify/Pipeline/GameVerifyPipeline.cs b/src/ModVerify/Pipeline/GameVerifyPipeline.cs index 92ba883..b7ac00d 100644 --- a/src/ModVerify/Pipeline/GameVerifyPipeline.cs +++ b/src/ModVerify/Pipeline/GameVerifyPipeline.cs @@ -5,7 +5,6 @@ using AET.ModVerify.Verifiers; using AnakinRaW.CommonUtilities.SimplePipeline; using AnakinRaW.CommonUtilities.SimplePipeline.Runners; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; using System; @@ -13,6 +12,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using AET.ModVerify.Pipeline.Progress; namespace AET.ModVerify.Pipeline; diff --git a/src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs b/src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs new file mode 100644 index 0000000..361febe --- /dev/null +++ b/src/ModVerify/Pipeline/Progress/AggregatedVerifyProgressReporter.cs @@ -0,0 +1,72 @@ +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; +using System; +using System.Collections.Generic; + +namespace AET.ModVerify.Pipeline.Progress; + +internal class AggregatedVerifyProgressReporter( + IVerifyProgressReporter progressReporter, + IEnumerable steps) + : AggregatedProgressReporter(progressReporter, steps) +{ + private readonly object _syncLock = new(); + private readonly IDictionary _progressTable = new Dictionary(StringComparer.OrdinalIgnoreCase); + private double _completedSize; + private readonly HashSet _completedVerifiers = new(); + + protected override string GetProgressText(GameVerifierPipelineStep step, string? progressText) + { + return string.IsNullOrEmpty(progressText) + ? $"Verifier '{step.GameVerifier.FriendlyName}'" + : $"Verifier '{step.GameVerifier.FriendlyName}': {progressText}"; + } + + protected override ProgressEventArgs CalculateAggregatedProgress( + GameVerifierPipelineStep step, + ProgressEventArgs progress) + { + var key = step.GameVerifier.Name; + + var progressValue = progress.Progress; + var stepProgress = progressValue * step.Size; + + if (progressValue >= 1.0) + _completedVerifiers.Add(key); + + double totalProgress; + + lock (_syncLock) + { + if (!_progressTable.TryGetValue(key, out var storedProgress)) + { + _progressTable.Add(key, stepProgress); + _completedSize += stepProgress; + } + else + { + var deltaSize = stepProgress - storedProgress; + _progressTable[key] = stepProgress; + _completedSize += deltaSize; + } + + totalProgress = _completedSize / TotalSize; + totalProgress = Math.Min(totalProgress, 1.0); + } + + if (_completedVerifiers.Count >= TotalStepCount && progressValue >= 1.0) + totalProgress = 1.0; + + var progressInfo = new VerifyProgressInfo + { + TotalVerifiers = TotalStepCount, + }; + return new ProgressEventArgs(totalProgress, progress.ProgressText, progressInfo); + } + + protected override void DisposeResources() + { + base.DisposeResources(); + _progressTable.Clear(); + _completedVerifiers.Clear(); + } +} \ No newline at end of file diff --git a/src/ModVerify/Pipeline/Progress/IVerifyProgressReporter.cs b/src/ModVerify/Pipeline/Progress/IVerifyProgressReporter.cs new file mode 100644 index 0000000..e63e0f9 --- /dev/null +++ b/src/ModVerify/Pipeline/Progress/IVerifyProgressReporter.cs @@ -0,0 +1,5 @@ +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; + +namespace AET.ModVerify.Pipeline.Progress; + +public interface IVerifyProgressReporter : IProgressReporter; \ No newline at end of file diff --git a/src/ModVerify/Pipeline/Progress/VerifyProgress.cs b/src/ModVerify/Pipeline/Progress/VerifyProgress.cs new file mode 100644 index 0000000..a18f3d3 --- /dev/null +++ b/src/ModVerify/Pipeline/Progress/VerifyProgress.cs @@ -0,0 +1,12 @@ +using AnakinRaW.CommonUtilities.SimplePipeline.Progress; + +namespace AET.ModVerify.Pipeline.Progress; + +public static class VerifyProgress +{ + public static readonly ProgressType ProgressType = new() + { + Id = "Verify", + DisplayName = "Verify" + }; +} \ No newline at end of file diff --git a/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs b/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs new file mode 100644 index 0000000..1409239 --- /dev/null +++ b/src/ModVerify/Pipeline/Progress/VerifyProgressInfo.cs @@ -0,0 +1,8 @@ +namespace AET.ModVerify.Pipeline.Progress; + +public struct VerifyProgressInfo +{ + public bool IsDetailed { get; init; } + + public int TotalVerifiers { get; internal set; } +} \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ReporterBase.cs b/src/ModVerify/Reporting/Reporters/ReporterBase.cs index 42b0441..fd86119 100644 --- a/src/ModVerify/Reporting/Reporters/ReporterBase.cs +++ b/src/ModVerify/Reporting/Reporters/ReporterBase.cs @@ -12,7 +12,6 @@ public abstract class ReporterBase(T settings, IServiceProvider serviceProvid protected T Settings { get; } = settings ?? throw new ArgumentNullException(nameof(settings)); - public abstract Task ReportAsync(IReadOnlyCollection errors); protected IEnumerable FilteredErrors(IReadOnlyCollection errors) diff --git a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs index 25b21a6..601e424 100644 --- a/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs +++ b/src/ModVerify/Reporting/Reporters/VerificationReportersExtensions.cs @@ -45,7 +45,8 @@ public static IServiceCollection RegisterTextFileReporter( return serviceCollection.AddSingleton(sp => new TextFileReporter(settings, sp)); } - public static IServiceCollection RegisterConsoleReporter(this IServiceCollection serviceCollection, + public static IServiceCollection RegisterConsoleReporter( + this IServiceCollection serviceCollection, VerifyReportSettings settings, bool summaryOnly = false) { diff --git a/src/ModVerify/Verifiers/AudioFilesVerifier.cs b/src/ModVerify/Verifiers/AudioFilesVerifier.cs index b6094e7..c5e2085 100644 --- a/src/ModVerify/Verifiers/AudioFilesVerifier.cs +++ b/src/ModVerify/Verifiers/AudioFilesVerifier.cs @@ -15,7 +15,6 @@ using PG.StarWarsGame.Engine.Audio.Sfx; using PG.StarWarsGame.Engine.Localization; using PG.StarWarsGame.Files.MEG.Services.Builder.Normalization; - #if NETSTANDARD2_0 using AnakinRaW.CommonUtilities.FileSystem; #endif @@ -51,10 +50,16 @@ public override void Verify(CancellationToken token) { var visitedSamples = new HashSet(); var languagesToVerify = GetLanguagesToVerify().ToList(); + + + var numSamples = GameEngine.SfxGameManager.Entries.Sum(x => x.AllSamples.Count()); + double counter = 0; + foreach (var sfxEvent in GameEngine.SfxGameManager.Entries) { foreach (var codedSample in sfxEvent.AllSamples) { + OnProgress(++counter / numSamples, $"Audio File - '{codedSample}'"); VerifySample(codedSample.AsSpan(), sfxEvent, languagesToVerify, visitedSamples); } } diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 48f92f3..8c9d67d 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -1,5 +1,4 @@ -using AET.ModVerify.Pipeline; -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AnakinRaW.CommonUtilities.SimplePipeline.Progress; using Microsoft.Extensions.DependencyInjection; @@ -8,6 +7,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; +using AET.ModVerify.Pipeline.Progress; using PG.StarWarsGame.Engine; namespace AET.ModVerify.Verifiers; @@ -79,9 +79,9 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter } } - protected void OnProgress(string message, double progress) + protected void OnProgress(double progress, string? message) { - Progress?.Invoke(this, new(message, progress)); + Progress?.Invoke(this, new(progress, message)); } private IReadOnlyList CreateVerifierChain() diff --git a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs index 92dc435..a36648b 100644 --- a/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs +++ b/src/ModVerify/Verifiers/ReferencedModelsVerifier.cs @@ -1,11 +1,9 @@ using AET.ModVerify.Settings; using AET.ModVerify.Verifiers.Commons; -using Microsoft.Extensions.Logging; using PG.StarWarsGame.Engine; using System; using System.Linq; using System.Threading; -using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Verifiers; @@ -17,15 +15,17 @@ public sealed class ReferencedModelsVerifier( { public override string FriendlyName => "Referenced Models"; - private readonly ILogger? _logger = - serviceProvider.GetService()?.CreateLogger(typeof(ReferencedModelsVerifier)); - public override void Verify(CancellationToken token) { var models = GameEngine.GameObjectTypeManager.Entries .SelectMany(x => x.Models) - .Concat(FocHardcodedConstants.HardcodedModels); + .Concat(FocHardcodedConstants.HardcodedModels).ToList(); + + if (models.Count == 0) + return; + var numModels = models.Count; + var counter = 0; var inner = new SingleModelVerifier(this, GameEngine, Settings, Services); try @@ -33,6 +33,7 @@ public override void Verify(CancellationToken token) inner.Error += OnModelError; foreach (var model in models) { + OnProgress((double)++counter / numModels, $"Model - '{model}'"); inner.Verify(model, [], token); } } diff --git a/src/ModVerify/Verifiers/VerifierErrorCodes.cs b/src/ModVerify/Verifiers/VerifierErrorCodes.cs index 68b9e92..986cbe6 100644 --- a/src/ModVerify/Verifiers/VerifierErrorCodes.cs +++ b/src/ModVerify/Verifiers/VerifierErrorCodes.cs @@ -25,7 +25,7 @@ public static class VerifierErrorCodes public const string InvalidSampleRate = "WAV04"; public const string InvalidBitsPerSeconds = "WAV05"; - public const string InvalidParticleName = "ALO08"; + public const string InvalidParticleName = "ALO01"; public const string GenericXmlError = "XML00"; public const string EmptyXmlRoot = "XML01"; diff --git a/src/PetroglyphTools/Directory.Build.props b/src/PetroglyphTools/Directory.Build.props index c8ba572..4b4cdb9 100644 --- a/src/PetroglyphTools/Directory.Build.props +++ b/src/PetroglyphTools/Directory.Build.props @@ -1,12 +1,11 @@ + - - + - Alamo Engine Tools and Contributors https://github.com/AlamoEngine-Tools/PetroglyphTools https://github.com/AlamoEngine-Tools/PetroglyphTools - + \ No newline at end of file diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs index 7c2628d..962c389 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/GameEngineType.cs @@ -1,6 +1,4 @@ -using Vanara.PInvoke; - -namespace PG.StarWarsGame.Engine; +namespace PG.StarWarsGame.Engine; /// /// The type of engine can be either "Empire at War" of "Forces of Corruption" diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs index 7a59a98..0fce4d3 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/IO/MultiPassRepository.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj index 35fbaae..6c05d99 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine/PG.StarWarsGame.Engine.csproj @@ -20,7 +20,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs index 77afc74..5fa2c6d 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs +++ b/src/PetroglyphTools/PG.StarWarsGame.Files.XML/Parsers/PetroglyphXmlElementParser.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Xml.Linq; +using System.Xml.Linq; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace PG.StarWarsGame.Files.XML.Parsers; From 5408ba969d0609da446e1bd064cc7d2263e77c94 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 5 Apr 2025 19:14:33 +0200 Subject: [PATCH 33/34] thx copilot :D --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7c6aaf..70b4f91 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ ModVerify.exe --path "C:\My Games\FoC\Mods\MyMod" --output "C:\My Games\FoC\Mods The following verifiers are currently implemented: ### SFX Events: -- Checks whether coded preset exist +- Checks whether coded presets exist - Checks the referenced samples for validity (bit rate, sample size, channels, etc.) - Duplicates From 2a035ff13d49cfc3f229de08d6901e33fde0f3f7 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 5 Apr 2025 19:25:26 +0200 Subject: [PATCH 34/34] do not close automatically in interactive mode --- src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs | 2 ++ src/ModVerify.CliApp/Program.cs | 12 ++++++++++-- src/ModVerify.CliApp/SettingsBuilder.cs | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs index d95f144..6a3b0bd 100644 --- a/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs +++ b/src/ModVerify.CliApp/Options/ModVerifyAppSettings.cs @@ -7,6 +7,8 @@ namespace AET.ModVerifyTool.Options; internal sealed class ModVerifyAppSettings { + public bool Interactive => GameInstallationsSettings.Interactive; + public required VerifyPipelineSettings VerifyPipelineSettings { get; init; } public required GlobalVerifyReportSettings GlobalReportSettings { get; init; } diff --git a/src/ModVerify.CliApp/Program.cs b/src/ModVerify.CliApp/Program.cs index 7aa6802..7c88edb 100644 --- a/src/ModVerify.CliApp/Program.cs +++ b/src/ModVerify.CliApp/Program.cs @@ -80,15 +80,16 @@ private static async Task Run(BaseModVerifyOptions options) logger?.LogDebug($"Raw command line: {Environment.CommandLine}"); + var interactive = false; try { var settings = new SettingsBuilder(coreServices).BuildSettings(options); + interactive = settings.Interactive; var services = CreateAppServices(coreServiceCollection, settings); if (!settings.Offline) await CheckForUpdate(services, logger); - - + var verifier = new ModVerifyApp(settings, services); return await verifier.RunApplication().ConfigureAwait(false); } @@ -105,6 +106,13 @@ private static async Task Run(BaseModVerifyOptions options) #else Log.CloseAndFlush(); #endif + if (interactive) + { + Console.WriteLine(); + ConsoleUtilities.WriteHorizontalLine('-'); + Console.WriteLine("Press any key to exit"); + Console.ReadLine(); + } } } diff --git a/src/ModVerify.CliApp/SettingsBuilder.cs b/src/ModVerify.CliApp/SettingsBuilder.cs index c9b585a..d2ba861 100644 --- a/src/ModVerify.CliApp/SettingsBuilder.cs +++ b/src/ModVerify.CliApp/SettingsBuilder.cs @@ -31,7 +31,6 @@ public ModVerifyAppSettings BuildSettings(BaseModVerifyOptions options) throw new NotSupportedException($"The option '{options.GetType().Name}' is not supported!"); } - private ModVerifyAppSettings BuildFromVerifyVerb(VerifyVerbOption verifyOptions) { var output = Environment.CurrentDirectory;