You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Legacy/CryCommon/LyShine/UiSerializeHelpers.h

524 lines
21 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Sfmt.h>
#include <AzCore/Component/Entity.h>
#include <LyShine/UiAssetTypes.h>
#include <LyShine/UiBase.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace LyShine
{
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to convert an AZStd::string field to a simple asset reference
// Inline to avoid DLL linkage issues
template<typename T>
inline bool ConvertSubElementFromAzStringToAssetRef(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* subElementName)
{
int index = classElement.FindElement(AZ_CRC(subElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
AZStd::string oldData;
if (!elementNode.GetData(oldData))
{
// Error, old subElement was not a string or not valid
AZ_Error("Serialization", false, "Cannot get string data for element %s.", subElementName);
return false;
}
// Remove old version.
classElement.RemoveElement(index);
// Add a new element for the new data.
int simpleAssetRefIndex = classElement.AddElement<AzFramework::SimpleAssetReference<T> >(context, subElementName);
if (simpleAssetRefIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for simpleAssetRefIndex %s", subElementName);
return false;
}
// add a sub element for the SimpleAssetReferenceBase within the SimpleAssetReference
AZ::SerializeContext::DataElementNode& simpleAssetRefNode = classElement.GetSubElement(simpleAssetRefIndex);
int simpleAssetRefBaseIndex = simpleAssetRefNode.AddElement<AzFramework::SimpleAssetReferenceBase>(context, "BaseClass1");
if (simpleAssetRefBaseIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element BaseClass1");
return false;
}
// add a sub element for the AssetPath within the SimpleAssetReference
AZ::SerializeContext::DataElementNode& simpleAssetRefBaseNode = simpleAssetRefNode.GetSubElement(simpleAssetRefBaseIndex);
int assetPathElementIndex = simpleAssetRefBaseNode.AddElement<AZStd::string>(context, "AssetPath");
if (assetPathElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element AssetPath");
return false;
}
AZStd::string newData(oldData.c_str());
simpleAssetRefBaseNode.GetSubElement(assetPathElementIndex).SetData(context, newData);
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to convert a char to a uint32_t
// Inline to avoid DLL linkage issues
inline bool ConvertSubElementFromCharToUInt32(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* subElementName)
{
int index = classElement.FindElement(AZ_CRC(subElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
char oldData;
if (!elementNode.GetData(oldData))
{
// Error, old subElement was not a CryString
AZ_Error("Serialization", false, "Element %s is not a char.", subElementName);
return false;
}
// Remove old version.
classElement.RemoveElement(index);
// Add a new element for the new data.
int newElementIndex = classElement.AddElement<uint32_t>(context, subElementName);
if (newElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element %s", subElementName);
return false;
}
uint32_t newData = oldData;
classElement.GetSubElement(newElementIndex).SetData(context, newData);
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to get the value for a named sub element
template<typename T>
inline bool GetSubElementValue(
AZ::SerializeContext::DataElementNode& classElement,
const char* elementName, T& value)
{
int index = classElement.FindElement(AZ_CRC(elementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
if (!elementNode.GetData(value))
{
return false;
}
}
else
{
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to make a ColorF out of an RGB color and an alpha
inline ColorF MakeColorF(AZ::Vector3 color, float alpha)
{
return ColorF(color.GetX(), color.GetY(), color.GetZ(), alpha);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to make an AZ::Vector3 from the rgb elements of a ColorF
inline AZ::Vector3 MakeColorVector3(ColorF color)
{
return AZ::Vector3(color.r, color.g, color.b);
}
// Helper function to make an AZ::Color from a ColorF
inline AZ::Color MakeColorAZColor(ColorF color)
{
return AZ::Color(color.r, color.g, color.b, color.a);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to convert a ColorF field to a Vector3 color and a float
// alpha.
// Inline to avoid DLL linkage issues
inline bool ConvertSubElementFromColorToColorPlusAlpha(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* colorElementName,
const char* alphaElementName)
{
int index = classElement.FindElement(AZ_CRC(colorElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
ColorF oldData;
if (!(GetSubElementValue(elementNode, "r", oldData.r) &&
GetSubElementValue(elementNode, "g", oldData.g) &&
GetSubElementValue(elementNode, "b", oldData.b) &&
GetSubElementValue(elementNode, "a", oldData.a)))
{
// Error, old subElement was not a ColorF
AZ_Error("Serialization", false, "Element %s is not a ColorF.", colorElementName);
return false;
}
// Remove old version.
classElement.RemoveElement(index);
// Add new elements for the new data.
int newColorElementIndex = classElement.AddElement<AZ::Vector3>(context, colorElementName);
if (newColorElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element %s", colorElementName);
return false;
}
int newAlphaElementIndex = classElement.AddElement<float>(context, alphaElementName);
if (newAlphaElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element %s", alphaElementName);
return false;
}
AZ::Vector3 newColorData(oldData.r, oldData.g, oldData.b);
classElement.GetSubElement(newColorElementIndex).SetData(context, newColorData);
float newAlphaData(oldData.a);
classElement.GetSubElement(newAlphaElementIndex).SetData(context, newAlphaData);
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to convert a AZ::Vector3 field to a AZ::Color
// Inline to avoid DLL linkage issues
inline bool ConvertSubElementFromVector3ToAzColor(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* colorElementName)
{
int index = classElement.FindElement(AZ_CRC(colorElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
AZ::Vector3 oldData;
if (!elementNode.GetData(oldData))
{
// Error, old subElement was not a Vector3 or not valid
AZ_Error("Serialization", false, "Cannot get Vector3 data for element %s.", colorElementName);
return false;
}
// Remove old version.
classElement.RemoveElement(index);
// Add new elements for the new data.
int newColorElementIndex = classElement.AddElement<AZ::Color>(context, colorElementName);
if (newColorElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element %s", colorElementName);
return false;
}
AZ::Color newColorData = AZ::Color::CreateFromVector3(oldData);
classElement.GetSubElement(newColorElementIndex).SetData(context, newColorData);
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to convert a ColorF field to a AZ::Color
// Inline to avoid DLL linkage issues
inline bool ConvertSubElementFromColorFToAzColor(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* colorElementName)
{
int index = classElement.FindElement(AZ_CRC(colorElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
ColorF oldData;
if (!(GetSubElementValue(elementNode, "r", oldData.r) &&
GetSubElementValue(elementNode, "g", oldData.g) &&
GetSubElementValue(elementNode, "b", oldData.b) &&
GetSubElementValue(elementNode, "a", oldData.a)))
{
// Error, old subElement was not a ColorF
AZ_Error("Serialization", false, "Element %s is not a ColorF.", colorElementName);
return false;
}
// Remove old version.
classElement.RemoveElement(index);
// Add new elements for the new data.
int newColorElementIndex = classElement.AddElement<AZ::Color>(context, colorElementName);
if (newColorElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element %s", colorElementName);
return false;
}
classElement.GetSubElement(newColorElementIndex).SetData(context, MakeColorAZColor(oldData));
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to convert a Vec2 field to a AZ::Vector2.
// Inline to avoid DLL linkage issues
inline bool ConvertSubElementFromVec2ToVector2(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* vec2ElementName)
{
int index = classElement.FindElement(AZ_CRC(vec2ElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(index);
Vec2 oldData;
if (!(GetSubElementValue(elementNode, "x", oldData.x) &&
GetSubElementValue(elementNode, "y", oldData.y)))
{
// Error, old subElement was not a Vec2
AZ_Error("Serialization", false, "Element %s is not a Vec2.", vec2ElementName);
return false;
}
// Remove old version.
classElement.RemoveElement(index);
// Add new elements for the new data.
int newVector2ElementIndex = classElement.AddElement<AZ::Vector2>(context, vec2ElementName);
if (newVector2ElementIndex == -1)
{
// Error adding the new sub element
AZ_Error("Serialization", false, "AddElement failed for converted element %s", vec2ElementName);
return false;
}
AZ::Vector2 newVector2Data(oldData.x, oldData.y);
classElement.GetSubElement(newVector2ElementIndex).SetData(context, newVector2Data);
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to move a sub-element from one DataElementNode to another
// DataElementNode and rename it
inline bool MoveElement(
[[maybe_unused]] AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& srcElement,
AZ::SerializeContext::DataElementNode& dstElement,
const char* srcSubElementName,
const char* dstSubElementName)
{
// find the sub-element (the field we will move)
int srcSubElementIndex = srcElement.FindElement(AZ_CRC(srcSubElementName));
if (srcSubElementIndex != -1)
{
// add a copy of the sub-element into the base class
AZ::SerializeContext::DataElementNode subElementNode = srcElement.GetSubElement(srcSubElementIndex);
subElementNode.SetName(dstSubElementName);
dstElement.AddElement(subElementNode);
// remove the sub-element from its original location
srcElement.RemoveElement(srcSubElementIndex);
}
// if the field did not exist then we do not report an error
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to VersionConverter to remove leading forward slashes from asset path
inline bool RemoveLeadingForwardSlashesFromAssetPath(
AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement,
const char* simpleAssetRefSubElementName)
{
int index = classElement.FindElement(AZ_CRC(simpleAssetRefSubElementName));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& simpleAssetRefNode = classElement.GetSubElement(index);
index = simpleAssetRefNode.FindElement(AZ_CRC("BaseClass1", 0xd4925735));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& baseClassNode = simpleAssetRefNode.GetSubElement(index);
index = baseClassNode.FindElement(AZ_CRC("AssetPath", 0x2c355179));
if (index != -1)
{
AZ::SerializeContext::DataElementNode& assetPathNode = baseClassNode.GetSubElement(index);
AZStd::string assetPath;
if (!assetPathNode.GetData(assetPath))
{
AZ_Error("Serialization", false, "Element AssetPath is not a AZStd::string.");
return false;
}
// Count leading forward slashes
int i = 0;
while ((i < assetPath.length()) && (assetPath[i] == '/'))
{
i++;
}
if (i > 0)
{
// Remove leading forward slashes
assetPath.erase(0, i);
if (!assetPathNode.SetData(context, assetPath))
{
AZ_Error("Serialization", false, "Unable to set AssetPath data.");
return false;
}
}
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to find a component with the given UUID in an entity
inline AZ::SerializeContext::DataElementNode* FindComponentNode(AZ::SerializeContext::DataElementNode& entityNode, const AZ::Uuid& uuid)
{
// get the component vector node
int componentsIndex = entityNode.FindElement(AZ_CRC("Components", 0xee48f5fd));
if (componentsIndex == -1)
{
return nullptr;
}
AZ::SerializeContext::DataElementNode& componentsNode = entityNode.GetSubElement(componentsIndex);
// search the components vector for the first component with the given ID
int numComponents = componentsNode.GetNumSubElements();
for (int compIndex = 0; compIndex < numComponents; ++compIndex)
{
AZ::SerializeContext::DataElementNode& componentNode = componentsNode.GetSubElement(compIndex);
if (componentNode.GetId() == uuid)
{
return &componentNode;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to create an EntityId node for a newly created entity node
inline bool CreateEntityIdNode(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& entityNode)
{
// Generate a new EntityId and extract the raw u64
AZ::EntityId newEntityId = AZ::Entity::MakeId();
AZ::u64 rawId = static_cast<AZ::u64>(newEntityId);
// Create the EntityId node within this entity
int entityIdIndex = entityNode.AddElement<AZ::EntityId>(context, "Id");
if (entityIdIndex == -1)
{
return false;
}
AZ::SerializeContext::DataElementNode& elementIdNode = entityNode.GetSubElement(entityIdIndex);
// Create the sub node of the EntityID that actually stores the u64
int u64Index = elementIdNode.AddElementWithData(context, "id", rawId);
if (u64Index == -1)
{
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function to create the AZ::Component base class for a newly created component
inline bool CreateComponentBaseClassNode(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& componentNode)
{
// Generate a new component ID (using same technique that the AZ::Component uses)
AZ::ComponentId compId = AZ::Sfmt::GetInstance().Rand64();
// Create the base class node within this component node
int baseClassIndex = componentNode.AddElement<AZ::Component>(context, "BaseClass1");
if (baseClassIndex == -1)
{
return false;
}
AZ::SerializeContext::DataElementNode& baseClassNode = componentNode.GetSubElement(baseClassIndex);
// Create the sub node of the component base class that stores the u64 component id
int u64Index = baseClassNode.AddElementWithData(context, "Id", compId);
if (u64Index == -1)
{
return false;
}
return true;
}
}