ZoneCodeGenerator: Add possibility to define custom actions that should be performed after loading a certain Structure

This commit is contained in:
Jan 2020-01-03 17:25:10 +01:00
parent c995a12001
commit bdb0958c16
11 changed files with 251 additions and 30 deletions

View File

@ -378,6 +378,7 @@ set condition basemap never;
// GfxImageLoadDef // GfxImageLoadDef
use GfxImageLoadDef; use GfxImageLoadDef;
set action LoadImageData(GfxImageLoadDef, GfxImage);
set arraysize data resourceSize; set arraysize data resourceSize;
// ========================================= // =========================================

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace ZoneCodeGenerator.Domain
{
class CustomAction
{
public string ActionName { get; }
public IList<DataType> ParameterTypes { get; }
public CustomAction(string actionName, IEnumerable<DataType> parameterTypes)
{
ActionName = actionName;
ParameterTypes = new List<DataType>(parameterTypes);
}
}
}

View File

@ -1,9 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using ZoneCodeGenerator.Domain.FastFileStructure;
using ZoneCodeGenerator.Generating.Computations; using ZoneCodeGenerator.Generating.Computations;
using ZoneCodeGenerator.Persistence;
namespace ZoneCodeGenerator.Domain.Information namespace ZoneCodeGenerator.Domain.Information
{ {
@ -31,6 +27,8 @@ namespace ZoneCodeGenerator.Domain.Information
public bool ArrayReferenceExists { get; set; } public bool ArrayReferenceExists { get; set; }
public bool ReferenceFromNonDefaultNormalBlockExists { get; set; } public bool ReferenceFromNonDefaultNormalBlockExists { get; set; }
public CustomAction PostLoadAction { get; set; }
public bool IsLeaf { get; set; } public bool IsLeaf { get; set; }
public List<MemberInformation> NameChain { get; set; } public List<MemberInformation> NameChain { get; set; }
@ -47,6 +45,7 @@ namespace ZoneCodeGenerator.Domain.Information
ReferenceFromNonDefaultNormalBlockExists = false; ReferenceFromNonDefaultNormalBlockExists = false;
Usages = new List<StructureInformation>(); Usages = new List<StructureInformation>();
OrderedMembers = new List<MemberInformation>(); OrderedMembers = new List<MemberInformation>();
PostLoadAction = null;
IsLeaf = true; IsLeaf = true;
NameChain = null; NameChain = null;
} }

View File

@ -11,10 +11,12 @@ namespace ZoneCodeGenerator.Generating
{ {
public class UsedType public class UsedType
{ {
public bool MembersLoaded { get; set; }
public DataType Type { get; } public DataType Type { get; }
public StructureInformation Information { get; } public StructureInformation Information { get; }
public bool IsContextAsset { get; set; } public bool IsContextAsset { get; set; }
public bool NonRuntimeReferenceExists { get; set; }
public bool NonEmbeddedReferenceExists { get; set; } public bool NonEmbeddedReferenceExists { get; set; }
public bool ArrayReferenceExists { get; set; } public bool ArrayReferenceExists { get; set; }
public bool PointerArrayReferenceExists { get; set; } public bool PointerArrayReferenceExists { get; set; }
@ -22,9 +24,11 @@ namespace ZoneCodeGenerator.Generating
public UsedType(DataType type, StructureInformation information) public UsedType(DataType type, StructureInformation information)
{ {
MembersLoaded = false;
Type = type; Type = type;
Information = information; Information = information;
IsContextAsset = false; IsContextAsset = false;
NonRuntimeReferenceExists = false;
NonEmbeddedReferenceExists = false; NonEmbeddedReferenceExists = false;
ArrayReferenceExists = false; ArrayReferenceExists = false;
PointerArrayReferenceExists = false; PointerArrayReferenceExists = false;
@ -33,17 +37,16 @@ namespace ZoneCodeGenerator.Generating
} }
public string Game { get; set; } public string Game { get; set; }
public StructureInformation Asset { get; set; } public StructureInformation Asset { get; private set; }
private readonly IDictionary<DataType, UsedType> usedTypes; private readonly IDictionary<DataType, UsedType> usedTypes;
public IEnumerable<UsedType> UsedTypes => usedTypes.Values; public IEnumerable<UsedType> UsedTypes => usedTypes.Values;
public IEnumerable<StructureInformation> UsedStructures => UsedTypes public IEnumerable<UsedType> UsedStructures => UsedTypes
.Select(type => type.Information) .Where(usedType => usedType.Information != null && usedType.Information.Type == usedType.Type);
.Where(information => information != null)
.Distinct();
public IEnumerable<UsedType> ReferencedAssets => UsedTypes.Where(type => type.Information != null && type.Information.IsAsset && type.Information != Asset); public IEnumerable<UsedType> ReferencedAssets => UsedTypes.Where(type =>
type.Information != null && type.Information.IsAsset && type.Information != Asset);
public IList<FastFileBlock> Blocks { get; private set; } public IList<FastFileBlock> Blocks { get; private set; }
@ -53,28 +56,43 @@ namespace ZoneCodeGenerator.Generating
public FastFileBlock DefaultTempBlock => Blocks.FirstOrDefault(block => block.IsDefault && block.IsTemp) ?? 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() private RenderingContext()
{ {
usedTypes = new Dictionary<DataType, UsedType>(); usedTypes = new Dictionary<DataType, UsedType>();
} }
private void AddToContext(StructureInformation structureInformation) private UsedType GetBaseType(UsedType usedType)
{ {
if (usedTypes.ContainsKey(structureInformation.Type)) if (usedType.Type is DataTypeTypedef typeDef)
return; {
while (typeDef.TypeDefinition.Type is DataTypeTypedef anotherTypeDef)
{
typeDef = anotherTypeDef;
}
var newUsage = new UsedType(structureInformation.Type, structureInformation); if (!usedTypes.ContainsKey(typeDef.TypeDefinition.Type))
usedTypes.Add(newUsage.Type, newUsage); {
var result = new UsedType(typeDef.TypeDefinition.Type, usedType.Information);
usedTypes.Add(typeDef.TypeDefinition.Type, result);
if (structureInformation.IsAsset && structureInformation != Asset) return result;
return; }
return usedTypes[typeDef.TypeDefinition.Type];
}
return null;
}
private void AddMembersToContext(StructureInformation structureInformation)
{
foreach (var member in structureInformation.OrderedMembers foreach (var member in structureInformation.OrderedMembers
.Where(member => !member.Computations.ShouldIgnore)) .Where(member => !member.Computations.ShouldIgnore))
{ {
if(member.StructureType != null)
AddToContext(member.StructureType);
UsedType usedType; UsedType usedType;
if (!usedTypes.ContainsKey(member.Member.VariableType.Type)) if (!usedTypes.ContainsKey(member.Member.VariableType.Type))
{ {
@ -86,6 +104,16 @@ namespace ZoneCodeGenerator.Generating
usedType = usedTypes[member.Member.VariableType.Type]; 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) if (member.Computations.ContainsNonEmbeddedReference)
usedType.NonEmbeddedReferenceExists = true; usedType.NonEmbeddedReferenceExists = true;
@ -99,20 +127,34 @@ namespace ZoneCodeGenerator.Generating
if (member.IsReusable) if (member.IsReusable)
usedType.PointerArrayReferenceIsReusable = true; 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) public static RenderingContext BuildContext(CUISession session, StructureInformation asset)
{ {
var context = new RenderingContext var context = new RenderingContext
{ {
Asset = asset,
Game = session.Game, Game = session.Game,
Blocks = session.Repository.GetAllFastFileBlocks().ToList() Blocks = session.Repository.GetAllFastFileBlocks().ToList()
}; };
context.AddToContext(asset); context.MakeAsset(asset);
context.usedTypes[asset.Type].IsContextAsset = true;
return context; return context;
} }

View File

@ -45,12 +45,18 @@ header(context) ::= <<
#include "Loading/AssetLoader.h" #include "Loading/AssetLoader.h"
#include "Game/$context.Game$/$context.Game$.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 <string> #include <string>
namespace $context.Game$ namespace $context.Game$
{ {
class $LoaderClassName(context.Asset)$ final : public AssetLoader class $LoaderClassName(context.Asset)$ final : public AssetLoader
{ {
$if(context.HasActions)$
Actions_$context.Asset.Type.Name$ m_actions;
$endif$
$VariableDeclaration(context.Asset.Type)$ $VariableDeclaration(context.Asset.Type)$
$PointerVariableDeclaration(context.Asset.Type)$ $PointerVariableDeclaration(context.Asset.Type)$
@ -60,8 +66,8 @@ namespace $context.Game$
$endif$}$$\\$ $endif$}$$\\$
$context.UsedTypes:{usedType | $if(usedType.PointerArrayReferenceExists)$$HeaderPtrArrayLoadMethodDeclaration(usedType.Type)$$\n$$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.UsedTypes:{usedType | $if(usedType.ArrayReferenceExists && usedType.Information && !usedType.Information.IsLeaf && usedType.NonRuntimeReferenceExists)$$HeaderArrayLoadMethodDeclaration(usedType.Type)$$\n$$endif$}$
$context.UsedStructures:{usedStructure | $if(!usedStructure.IsLeaf && !usedStructure.IsAsset)$$HeaderLoadMethodDeclaration(usedStructure)$$\n$$endif$}$ $context.UsedStructures:{usedStructure | $if(usedStructure.NonRuntimeReferenceExists && !usedStructure.Information.IsLeaf && !usedStructure.Information.IsAsset)$$HeaderLoadMethodDeclaration(usedStructure.Information)$$\n$$endif$}$
$HeaderLoadMethodDeclaration(context.Asset)$ $HeaderLoadMethodDeclaration(context.Asset)$
$HeaderTempPtrLoadMethodDeclaration(context.Asset)$ $HeaderTempPtrLoadMethodDeclaration(context.Asset)$
$HeaderAssetLoadMethodDeclaration(context.Asset)$ $HeaderAssetLoadMethodDeclaration(context.Asset)$
@ -274,7 +280,8 @@ var$type.Name$Ptr = nullptr;
ConstructorMethod(context) ::= << ConstructorMethod(context) ::= <<
$LoaderClassName(context.Asset)$::$LoaderClassName(context.Asset)$(IZoneScriptStringProvider* scriptStringProvider, Zone* zone, IZoneInputStream* stream) $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)$ $VariableInitialization(context.Asset.Type)$
$PointerVariableInitialization(context.Asset.Type)$ $PointerVariableInitialization(context.Asset.Type)$
@ -341,8 +348,8 @@ using namespace $context.Game$;
$ConstructorMethod(context)$ $ConstructorMethod(context)$
$context.UsedTypes:{usedType | $if(usedType.PointerArrayReferenceExists)$$LoadPointerArrayMethod(context, usedType.Type, usedType.Information, usedType.PointerArrayReferenceIsReusable)$$\n$$\n$$endif$}$ $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.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.IsLeaf && !usedStructure.IsAsset)$$LoadMethod(context, usedStructure)$$\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)$ $LoadMethod(context, context.Asset)$
$LoadTempPtrMethod(context, context.Asset)$ $LoadTempPtrMethod(context, context.Asset)$

View File

@ -15,6 +15,7 @@ namespace ZoneCodeGenerator.Parsing.CommandFile.Impl
{ {
private static readonly ITokenTest<ICommandParserState>[] tests = private static readonly ITokenTest<ICommandParserState>[] tests =
{ {
new TestAction(),
new TestArrayCount(), new TestArrayCount(),
new TestArraySize(), new TestArraySize(),
new TestAsset(), new TestAsset(),

View File

@ -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<ICommandParserState>
{
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<DataType>();
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);
}
}
}

View File

@ -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)
{
}

View File

@ -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);
};

View File

@ -0,0 +1,6 @@
#include "AssetLoadingActions.h"
AssetLoadingActions::AssetLoadingActions(Zone* zone)
{
m_zone = zone;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "Zone/Zone.h"
class AssetLoadingActions
{
protected:
Zone* m_zone;
public:
explicit AssetLoadingActions(Zone* zone);
};