diff --git a/src/ZoneCode/Game/T6/T6_Commands.txt b/src/ZoneCode/Game/T6/T6_Commands.txt index 0956f1c6..c710bda0 100644 --- a/src/ZoneCode/Game/T6/T6_Commands.txt +++ b/src/ZoneCode/Game/T6/T6_Commands.txt @@ -378,6 +378,7 @@ set condition basemap never; // GfxImageLoadDef use GfxImageLoadDef; +set action LoadImageData(GfxImageLoadDef, GfxImage); set arraysize data resourceSize; // ========================================= diff --git a/src/ZoneCodeGenerator/Domain/CustomAction.cs b/src/ZoneCodeGenerator/Domain/CustomAction.cs new file mode 100644 index 00000000..c6bed7f7 --- /dev/null +++ b/src/ZoneCodeGenerator/Domain/CustomAction.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace ZoneCodeGenerator.Domain +{ + class CustomAction + { + public string ActionName { get; } + public IList ParameterTypes { get; } + + public CustomAction(string actionName, IEnumerable parameterTypes) + { + ActionName = actionName; + ParameterTypes = new List(parameterTypes); + } + } +} \ No newline at end of file diff --git a/src/ZoneCodeGenerator/Domain/Information/StructureInformation.cs b/src/ZoneCodeGenerator/Domain/Information/StructureInformation.cs index ff3b1a22..15210f6b 100644 --- a/src/ZoneCodeGenerator/Domain/Information/StructureInformation.cs +++ b/src/ZoneCodeGenerator/Domain/Information/StructureInformation.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ZoneCodeGenerator.Domain.FastFileStructure; +using System.Collections.Generic; using ZoneCodeGenerator.Generating.Computations; -using ZoneCodeGenerator.Persistence; namespace ZoneCodeGenerator.Domain.Information { @@ -31,6 +27,8 @@ namespace ZoneCodeGenerator.Domain.Information public bool ArrayReferenceExists { get; set; } public bool ReferenceFromNonDefaultNormalBlockExists { get; set; } + public CustomAction PostLoadAction { get; set; } + public bool IsLeaf { get; set; } public List NameChain { get; set; } @@ -47,6 +45,7 @@ namespace ZoneCodeGenerator.Domain.Information ReferenceFromNonDefaultNormalBlockExists = false; Usages = new List(); OrderedMembers = new List(); + PostLoadAction = null; IsLeaf = true; NameChain = null; } diff --git a/src/ZoneCodeGenerator/Generating/RenderingContext.cs b/src/ZoneCodeGenerator/Generating/RenderingContext.cs index 1121074d..5e7d897e 100644 --- a/src/ZoneCodeGenerator/Generating/RenderingContext.cs +++ b/src/ZoneCodeGenerator/Generating/RenderingContext.cs @@ -11,10 +11,12 @@ namespace ZoneCodeGenerator.Generating { public class UsedType { + public bool MembersLoaded { get; set; } public DataType Type { get; } public StructureInformation Information { get; } public bool IsContextAsset { get; set; } + public bool NonRuntimeReferenceExists { get; set; } public bool NonEmbeddedReferenceExists { get; set; } public bool ArrayReferenceExists { get; set; } public bool PointerArrayReferenceExists { get; set; } @@ -22,9 +24,11 @@ namespace ZoneCodeGenerator.Generating public UsedType(DataType type, StructureInformation information) { + MembersLoaded = false; Type = type; Information = information; IsContextAsset = false; + NonRuntimeReferenceExists = false; NonEmbeddedReferenceExists = false; ArrayReferenceExists = false; PointerArrayReferenceExists = false; @@ -33,17 +37,16 @@ namespace ZoneCodeGenerator.Generating } public string Game { get; set; } - public StructureInformation Asset { get; set; } + public StructureInformation Asset { get; private set; } private readonly IDictionary usedTypes; public IEnumerable UsedTypes => usedTypes.Values; - public IEnumerable UsedStructures => UsedTypes - .Select(type => type.Information) - .Where(information => information != null) - .Distinct(); + public IEnumerable UsedStructures => UsedTypes + .Where(usedType => usedType.Information != null && usedType.Information.Type == usedType.Type); - public IEnumerable ReferencedAssets => UsedTypes.Where(type => type.Information != null && type.Information.IsAsset && type.Information != Asset); + public IEnumerable ReferencedAssets => UsedTypes.Where(type => + type.Information != null && type.Information.IsAsset && type.Information != Asset); public IList Blocks { get; private set; } @@ -51,30 +54,45 @@ namespace ZoneCodeGenerator.Generating Blocks.FirstOrDefault(block => block.IsNormal); public FastFileBlock DefaultTempBlock => Blocks.FirstOrDefault(block => block.IsDefault && block.IsTemp) ?? - Blocks.FirstOrDefault(block => block.IsTemp); + Blocks.FirstOrDefault(block => block.IsTemp); + + public bool HasActions => UsedTypes.Any(type => + (type.Information == null || !type.Information.IsAsset || type.IsContextAsset) && type.NonRuntimeReferenceExists && + type.Information?.PostLoadAction != null); private RenderingContext() { usedTypes = new Dictionary(); } - private void AddToContext(StructureInformation structureInformation) + private UsedType GetBaseType(UsedType usedType) { - if (usedTypes.ContainsKey(structureInformation.Type)) - return; + if (usedType.Type is DataTypeTypedef typeDef) + { + while (typeDef.TypeDefinition.Type is DataTypeTypedef anotherTypeDef) + { + typeDef = anotherTypeDef; + } - var newUsage = new UsedType(structureInformation.Type, structureInformation); - usedTypes.Add(newUsage.Type, newUsage); + if (!usedTypes.ContainsKey(typeDef.TypeDefinition.Type)) + { + var result = new UsedType(typeDef.TypeDefinition.Type, usedType.Information); + usedTypes.Add(typeDef.TypeDefinition.Type, result); - if (structureInformation.IsAsset && structureInformation != Asset) - return; + return result; + } + return usedTypes[typeDef.TypeDefinition.Type]; + } + + return null; + } + + private void AddMembersToContext(StructureInformation structureInformation) + { foreach (var member in structureInformation.OrderedMembers .Where(member => !member.Computations.ShouldIgnore)) { - if(member.StructureType != null) - AddToContext(member.StructureType); - UsedType usedType; if (!usedTypes.ContainsKey(member.Member.VariableType.Type)) { @@ -86,6 +104,16 @@ namespace ZoneCodeGenerator.Generating usedType = usedTypes[member.Member.VariableType.Type]; } + var baseUsedType = GetBaseType(usedType); + + if (!member.Computations.IsRuntimeBlock) + { + usedType.NonRuntimeReferenceExists = true; + + if (baseUsedType != null) + baseUsedType.NonRuntimeReferenceExists = true; + } + if (member.Computations.ContainsNonEmbeddedReference) usedType.NonEmbeddedReferenceExists = true; @@ -99,20 +127,34 @@ namespace ZoneCodeGenerator.Generating if (member.IsReusable) usedType.PointerArrayReferenceIsReusable = true; } + + if (usedType.Information != null && !usedType.Information.IsAsset && !member.Computations.IsRuntimeBlock && + !usedType.MembersLoaded) + { + usedType.MembersLoaded = true; + AddMembersToContext(usedType.Information); + } } } + private void MakeAsset(StructureInformation asset) + { + Asset = asset; + var usage = new UsedType(asset.Type, asset) {IsContextAsset = true}; + usedTypes.Add(usage.Type, usage); + + AddMembersToContext(asset); + } + public static RenderingContext BuildContext(CUISession session, StructureInformation asset) { var context = new RenderingContext { - Asset = asset, Game = session.Game, Blocks = session.Repository.GetAllFastFileBlocks().ToList() }; - context.AddToContext(asset); - context.usedTypes[asset.Type].IsContextAsset = true; + context.MakeAsset(asset); return context; } diff --git a/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg b/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg index cd631e11..cd4df5ec 100644 --- a/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg +++ b/src/ZoneCodeGenerator/Generating/Templates/ZoneLoad.stg @@ -45,12 +45,18 @@ header(context) ::= << #include "Loading/AssetLoader.h" #include "Game/$context.Game$/$context.Game$.h" +$if(context.HasActions)$ +#include "Game/$context.Game$/XAssets/$Lower(context.Asset.Type.Name)$/$Lower(context.Asset.Type.Name)$_actions.h" +$endif$ #include namespace $context.Game$ { class $LoaderClassName(context.Asset)$ final : public AssetLoader { + $if(context.HasActions)$ + Actions_$context.Asset.Type.Name$ m_actions; + $endif$ $VariableDeclaration(context.Asset.Type)$ $PointerVariableDeclaration(context.Asset.Type)$ @@ -60,8 +66,8 @@ namespace $context.Game$ $endif$}$$\\$ $context.UsedTypes:{usedType | $if(usedType.PointerArrayReferenceExists)$$HeaderPtrArrayLoadMethodDeclaration(usedType.Type)$$\n$$endif$}$ - $context.UsedTypes:{usedType | $if(usedType.ArrayReferenceExists && usedType.Information && !usedType.Information.IsLeaf)$$HeaderArrayLoadMethodDeclaration(usedType.Type)$$\n$$endif$}$ - $context.UsedStructures:{usedStructure | $if(!usedStructure.IsLeaf && !usedStructure.IsAsset)$$HeaderLoadMethodDeclaration(usedStructure)$$\n$$endif$}$ + $context.UsedTypes:{usedType | $if(usedType.ArrayReferenceExists && usedType.Information && !usedType.Information.IsLeaf && usedType.NonRuntimeReferenceExists)$$HeaderArrayLoadMethodDeclaration(usedType.Type)$$\n$$endif$}$ + $context.UsedStructures:{usedStructure | $if(usedStructure.NonRuntimeReferenceExists && !usedStructure.Information.IsLeaf && !usedStructure.Information.IsAsset)$$HeaderLoadMethodDeclaration(usedStructure.Information)$$\n$$endif$}$ $HeaderLoadMethodDeclaration(context.Asset)$ $HeaderTempPtrLoadMethodDeclaration(context.Asset)$ $HeaderAssetLoadMethodDeclaration(context.Asset)$ @@ -274,7 +280,8 @@ var$type.Name$Ptr = nullptr; ConstructorMethod(context) ::= << $LoaderClassName(context.Asset)$::$LoaderClassName(context.Asset)$(IZoneScriptStringProvider* scriptStringProvider, Zone* zone, IZoneInputStream* stream) - : AssetLoader($context.Asset.AssetEnumEntry.Name$, scriptStringProvider, zone, stream) + : AssetLoader($context.Asset.AssetEnumEntry.Name$, scriptStringProvider, zone, stream)$\\$ + $if(context.HasActions)$, m_actions(zone)$endif$ { $VariableInitialization(context.Asset.Type)$ $PointerVariableInitialization(context.Asset.Type)$ @@ -341,8 +348,8 @@ using namespace $context.Game$; $ConstructorMethod(context)$ $context.UsedTypes:{usedType | $if(usedType.PointerArrayReferenceExists)$$LoadPointerArrayMethod(context, usedType.Type, usedType.Information, usedType.PointerArrayReferenceIsReusable)$$\n$$\n$$endif$}$ -$context.UsedTypes:{usedType | $if(usedType.ArrayReferenceExists && usedType.Information && !usedType.Information.IsLeaf)$$LoadArrayMethod(context, usedType.Type, usedType.Information)$$\n$$\n$$endif$}$ -$context.UsedStructures:{usedStructure | $if(!usedStructure.IsLeaf && !usedStructure.IsAsset)$$LoadMethod(context, usedStructure)$$\n$$\n$$endif$}$ +$context.UsedTypes:{usedType | $if(usedType.ArrayReferenceExists && usedType.Information && !usedType.Information.IsLeaf && usedType.NonRuntimeReferenceExists)$$LoadArrayMethod(context, usedType.Type, usedType.Information)$$\n$$\n$$endif$}$ +$context.UsedStructures:{usedStructure | $if(usedStructure.NonRuntimeReferenceExists && !usedStructure.Information.IsLeaf && !usedStructure.Information.IsAsset)$$LoadMethod(context, usedStructure.Information)$$\n$$\n$$endif$}$ $LoadMethod(context, context.Asset)$ $LoadTempPtrMethod(context, context.Asset)$ diff --git a/src/ZoneCodeGenerator/Parsing/CommandFile/Impl/CommandParserState.cs b/src/ZoneCodeGenerator/Parsing/CommandFile/Impl/CommandParserState.cs index b2a1b999..91567e83 100644 --- a/src/ZoneCodeGenerator/Parsing/CommandFile/Impl/CommandParserState.cs +++ b/src/ZoneCodeGenerator/Parsing/CommandFile/Impl/CommandParserState.cs @@ -15,6 +15,7 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Impl { private static readonly ITokenTest[] tests = { + new TestAction(), new TestArrayCount(), new TestArraySize(), new TestAsset(), diff --git a/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestAction.cs b/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestAction.cs new file mode 100644 index 00000000..19cc8a54 --- /dev/null +++ b/src/ZoneCodeGenerator/Parsing/CommandFile/Tests/TestAction.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ZoneCodeGenerator.Domain; +using ZoneCodeGenerator.Domain.Information; +using ZoneCodeGenerator.Parsing.Matching; +using ZoneCodeGenerator.Parsing.Matching.Matchers; +using ZoneCodeGenerator.Parsing.Testing; + +namespace ZoneCodeGenerator.Parsing.CommandFile.Tests +{ + class TestAction : AbstractTokenTest + { + private const string TokenTypename = "typeName"; + private const string TagTypename = "typeName"; + + private const string TokenActionName = "actionName"; + + private const string TokenActionArgument = "actionArgument"; + private const string TagActionArguments = "actionArguments"; + + private static readonly TokenMatcher actionArguments = new MatcherGroupAnd( + new MatcherLiteral("("), + new MatcherGroupOptional(new MatcherGroupAnd( + new MatcherTypename().WithName(TokenActionArgument), + new MatcherGroupLoop(MatcherGroupLoop.LoopMode.ZeroOneMultiple, new MatcherGroupAnd( + new MatcherLiteral(","), + new MatcherTypename().WithName(TokenActionArgument) + )) + )), + new MatcherLiteral(")") + ).WithTag(TagActionArguments); + + private static readonly TokenMatcher[] matchers = + { + new MatcherLiteral("set"), + new MatcherLiteral("action"), + new MatcherGroupOr( + new MatcherGroupAnd( + new MatcherName().WithName(TokenActionName), + new MatcherWithTag(TagActionArguments) + ), + new MatcherGroupAnd( + new MatcherTypename().WithName(TokenTypename).WithTag(TagTypename), + new MatcherName().WithName(TokenActionName), + new MatcherWithTag(TagActionArguments) + ) + ), + new MatcherLiteral(";") + }; + + public TestAction() : base(matchers) + { + AddTaggedMatcher(actionArguments); + } + + protected override void ProcessMatch(ICommandParserState state) + { + StructureInformation selectedStructure; + if (PeekTag().Equals(TagTypename)) + { + NextTag(); + var typeName = NextMatch(TokenTypename); + var typeNameParts = typeName.Split(new[] {"::"}, StringSplitOptions.None); + + if (state.DataTypeInUse != null && + state.GetMembersFromParts(typeNameParts, state.DataTypeInUse, out var selectedMembers)) + { + selectedStructure = state.DataTypeInUse; + } + else if (state.GetTypenameAndMembersFromParts(typeNameParts, out selectedStructure, out selectedMembers)) + { + // Do nothing + } + else + { + throw new TestFailedException($"Could not find type '{typeName}'."); + } + + if (selectedMembers.Any()) + { + selectedStructure = selectedMembers.Last().StructureType ?? + throw new TestFailedException("Member is not a data type with members."); + } + } + else + { + selectedStructure = state.DataTypeInUse ?? throw new TestFailedException("No type found for action."); + } + + var actionName = NextMatch(TokenActionName); + if (string.IsNullOrEmpty(actionName)) + { + throw new TestFailedException("Action needs a valid name!"); + } + + var parameterTypes = new List(); + string parameter; + while ((parameter = NextMatch(TokenActionArgument)) != null) + { + var parameterDataType = state.Repository.GetDataTypeByName(parameter); + + if (parameterDataType == null) + { + throw new TestFailedException($"Could not find type '{parameter}'."); + } + + parameterTypes.Add(parameterDataType); + } + + selectedStructure.PostLoadAction = new CustomAction(actionName, parameterTypes); + } + } +} \ No newline at end of file diff --git a/src/ZoneLoading/Game/T6/XAssets/gfximage/gfximage_actions.cpp b/src/ZoneLoading/Game/T6/XAssets/gfximage/gfximage_actions.cpp new file mode 100644 index 00000000..ef1a98fa --- /dev/null +++ b/src/ZoneLoading/Game/T6/XAssets/gfximage/gfximage_actions.cpp @@ -0,0 +1,10 @@ +#include "gfximage_actions.h" + +Actions_GfxImage::Actions_GfxImage(Zone* zone) + : AssetLoadingActions(zone) +{ +} + +void Actions_GfxImage::LoadImageData(T6::GfxImageLoadDef* loadDef, T6::GfxImage* image) +{ +} diff --git a/src/ZoneLoading/Game/T6/XAssets/gfximage/gfximage_actions.h b/src/ZoneLoading/Game/T6/XAssets/gfximage/gfximage_actions.h new file mode 100644 index 00000000..8a7cccf9 --- /dev/null +++ b/src/ZoneLoading/Game/T6/XAssets/gfximage/gfximage_actions.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Loading/AssetLoadingActions.h" +#include "Game/T6/T6.h" + +class Actions_GfxImage final : public AssetLoadingActions +{ +public: + explicit Actions_GfxImage(Zone* zone); + + void LoadImageData(T6::GfxImageLoadDef* loadDef, T6::GfxImage* image); +}; \ No newline at end of file diff --git a/src/ZoneLoading/Loading/AssetLoadingActions.cpp b/src/ZoneLoading/Loading/AssetLoadingActions.cpp new file mode 100644 index 00000000..2908a982 --- /dev/null +++ b/src/ZoneLoading/Loading/AssetLoadingActions.cpp @@ -0,0 +1,6 @@ +#include "AssetLoadingActions.h" + +AssetLoadingActions::AssetLoadingActions(Zone* zone) +{ + m_zone = zone; +} diff --git a/src/ZoneLoading/Loading/AssetLoadingActions.h b/src/ZoneLoading/Loading/AssetLoadingActions.h new file mode 100644 index 00000000..5f62066d --- /dev/null +++ b/src/ZoneLoading/Loading/AssetLoadingActions.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Zone/Zone.h" + +class AssetLoadingActions +{ +protected: + Zone* m_zone; + +public: + explicit AssetLoadingActions(Zone* zone); +}; \ No newline at end of file