diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp index 4dfafa132a..5bad3ecf1c 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp @@ -19,10 +19,7 @@ #include #include -#include -#include -#include -#include +#include void InitWwiseResources() { @@ -217,28 +214,34 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) + TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* node, EACEControlType atlControlType) { if (node) { - const AZStd::string tag(node->getTag()); - TImplControlType type = TagToType(tag); + AZStd::string_view element(node->name()); + TImplControlType type = TagToType(element); if (type != AUDIO_IMPL_INVALID_TYPE) { - AZStd::string name(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute)); - AZStd::string localized(node->getAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute)); + AZStd::string name; + AZStd::string_view localized; - // Legacy Preload support - if (localized.empty()) + if (auto nameAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false); + nameAttr != nullptr) { - localized = node->getAttr(Audio::WwiseXmlTags::Legacy::WwiseLocalizedAttribute); + name = nameAttr->value(); } - bool isLocalized = AZ::StringFunc::Equal(localized.c_str(), "true"); + if (auto localizedAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseLocalizedAttribute, 0, false); + localizedAttr != nullptr) + { + localized = localizedAttr->value(); + } - // If control not found, create a placeholder. - // We want to keep that connection even if it's not in the middleware. - // The user could be using the engine without the wwise project + bool isLocalized = AZ::StringFunc::Equal(localized, "true"); + + // If the control wasn't found, create a placeholder. + // We want to see that connection even if it's not in the middleware. + // User could be viewing the editor without a middleware project. IAudioSystemControl* control = GetControlByName(name, isLocalized); if (!control) { @@ -250,27 +253,26 @@ namespace AudioControls } } - // If it's a switch we actually connect to one of the states within the switch + // If it's a switch we connect to one of the states within the switch if (type == eWCT_WWISE_SWITCH_GROUP || type == eWCT_WWISE_GAME_STATE_GROUP) { - if (node->getChildCount() == 1) + if (auto childNode = node->first_node(); + childNode != nullptr) { - node = node->getChild(0); - if (node) + AZStd::string childName; + if (auto childNameAttr = childNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false); + childNameAttr != nullptr) { - AZStd::string childName(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute)); - - IAudioSystemControl* childControl = GetControlByName(childName, false, control); - if (!childControl) - { - childControl = CreateControl(SControlDef(childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control)); - } - control = childControl; + childName = childNameAttr->value(); } - } - else - { - CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Audio Controls Editor (Wwise): Error reading connection to Wwise control %s", name.c_str()); + + IAudioSystemControl* childControl = GetControlByName(childName, false, control); + if (!childControl) + { + childControl = CreateControl(SControlDef( + childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control)); + } + control = childControl; } } @@ -289,16 +291,19 @@ namespace AudioControls float mult = 1.0f; float shift = 0.0f; - if (node->haveAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute)) + + if (auto multAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseMutiplierAttribute, 0, false); + multAttr != nullptr) { - const AZStd::string multProperty(node->getAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute)); - mult = AZStd::stof(multProperty); + mult = AZStd::stof(AZStd::string(multAttr->value())); } - if (node->haveAttr(Audio::WwiseXmlTags::WwiseShiftAttribute)) + + if (auto shiftAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseShiftAttribute, 0, false); + shiftAttr != nullptr) { - const AZStd::string shiftProperty(node->getAttr(Audio::WwiseXmlTags::WwiseShiftAttribute)); - shift = AZStd::stof(shiftProperty); + shift = AZStd::stof(AZStd::string(shiftAttr->value())); } + connection->m_mult = mult; connection->m_shift = shift; return connection; @@ -308,11 +313,12 @@ namespace AudioControls TStateConnectionPtr connection = AZStd::make_shared(control->GetId()); float value = 0.0f; - if (node->haveAttr(Audio::WwiseXmlTags::WwiseValueAttribute)) + if (auto valueAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseValueAttribute, 0, false); + valueAttr != nullptr) { - const AZStd::string valueProperty(node->getAttr(Audio::WwiseXmlTags::WwiseValueAttribute)); - value = AZStd::stof(valueProperty); + value = AZStd::stof(AZStd::string(valueAttr->value())); } + connection->m_value = value; return connection; } @@ -329,28 +335,50 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - XmlNodeRef CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) + AZ::rapidxml::xml_node* CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) { const IAudioSystemControl* control = GetControl(connection->GetID()); if (control) { + XmlAllocator& xmlAllocator(AudioControls::s_xmlAllocator); + switch (control->GetType()) { case AudioControls::eWCT_WWISE_SWITCH: + [[fallthrough]]; case AudioControls::eWCT_WWISE_SWITCH_GROUP: + [[fallthrough]]; case AudioControls::eWCT_WWISE_GAME_STATE: + [[fallthrough]]; case AudioControls::eWCT_WWISE_GAME_STATE_GROUP: { const IAudioSystemControl* parent = control->GetParent(); if (parent) { - XmlNodeRef switchNode = GetISystem()->CreateXmlNode(TypeToTag(parent->GetType()).data()); - switchNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, parent->GetName().c_str()); + AZStd::string_view parentType = TypeToTag(parent->GetType()); + auto switchNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(parentType.data()) + ); - XmlNodeRef stateNode = switchNode->createNode(Audio::WwiseXmlTags::WwiseValueTag); - stateNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); - switchNode->addChild(stateNode); + auto switchNameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(parent->GetName().c_str()) + ); + auto stateNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + Audio::WwiseXmlTags::WwiseValueTag + ); + + auto stateNameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + switchNode->append_attribute(switchNameAttr); + stateNode->append_attribute(stateNameAttr); + switchNode->append_node(stateNode); return switchNode; } break; @@ -358,51 +386,98 @@ namespace AudioControls case AudioControls::eWCT_WWISE_RTPC: { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); + auto connectionNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(TypeToTag(control->GetType()).data()) + ); + + auto nameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + connectionNode->append_attribute(nameAttr); if (atlControlType == eACET_RTPC) { AZStd::shared_ptr rtpcConnection = AZStd::static_pointer_cast(connection); - if (rtpcConnection->m_mult != 1.0f) + if (rtpcConnection->m_mult != 1.f) { - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute, rtpcConnection->m_mult); + auto multAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseMutiplierAttribute, + xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_mult).c_str()) + ); + + connectionNode->append_attribute(multAttr); } - if (rtpcConnection->m_shift != 0.0f) + + if (rtpcConnection->m_shift != 0.f) { - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseShiftAttribute, rtpcConnection->m_shift); + auto shiftAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseShiftAttribute, + xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_shift).c_str()) + ); + + connectionNode->append_attribute(shiftAttr); } } else if (atlControlType == eACET_SWITCH_STATE) { AZStd::shared_ptr stateConnection = AZStd::static_pointer_cast(connection); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseValueAttribute, stateConnection->m_value); + + auto valueAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseValueAttribute, + xmlAllocator.allocate_string(AZStd::to_string(stateConnection->m_value).c_str()) + ); + + connectionNode->append_attribute(valueAttr); } + return connectionNode; } case AudioControls::eWCT_WWISE_EVENT: - { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); - return connectionNode; - } - + [[fallthrough]]; case AudioControls::eWCT_WWISE_AUX_BUS: { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); + auto connectionNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(TypeToTag(control->GetType()).data()) + ); + + auto nameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + connectionNode->append_attribute(nameAttr); return connectionNode; } case AudioControls::eWCT_WWISE_SOUND_BANK: { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); + auto connectionNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(TypeToTag(control->GetType()).data()) + ); + + auto nameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + connectionNode->append_attribute(nameAttr); + if (control->IsLocalized()) { - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute, "true"); + auto locAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseLocalizedAttribute, + xmlAllocator.allocate_string("true") + ); + + connectionNode->append_attribute(locAttr); } + return connectionNode; } } diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h index 14708a5816..25c621de76 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h @@ -77,8 +77,8 @@ namespace AudioControls EACEControlType ImplTypeToATLType(TImplControlType type) const override; TImplControlTypeMask GetCompatibleTypes(EACEControlType atlControlType) const override; TConnectionPtr CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl) override; - TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) override; - XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override; + TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* node, EACEControlType atlControlType) override; + AZ::rapidxml::xml_node* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override; const AZStd::string_view GetTypeIcon(TImplControlType type) const override; const AZStd::string_view GetTypeIconSelected(TImplControlType type) const override; AZStd::string GetName() const override; diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp b/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp index 30320a567f..1a3ff121f8 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp @@ -17,12 +17,6 @@ #include #include -#include -#include -#include -#include - -using namespace PathUtil; namespace AudioControls { @@ -68,8 +62,7 @@ namespace AudioControls for (const auto& filePath : foundFiles) { AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str()); - AZStd::string fileName; - AZ::StringFunc::Path::GetFullFileName(filePath.c_str(), fileName); + AZ::IO::PathView fileName = filePath.Filename(); if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str())) { @@ -79,15 +72,15 @@ namespace AudioControls // we load only one as all of them should have the // same content (in the future we want to have a // consistency report to highlight if this is not the case) - m_localizationFolder = fileName; + m_localizationFolder.assign(fileName.Native().data(), fileName.Native().size()); LoadSoundBanks(rootFolder, m_localizationFolder, true); isLocalizedLoaded = true; } } - else if (AZ::StringFunc::Find(fileName.c_str(), Audio::Wwise::BankExtension) != AZStd::string::npos - && !AZ::StringFunc::Equal(fileName.c_str(), Audio::Wwise::InitBank)) + else if (fileName.Extension() == Audio::Wwise::BankExtension && !AZ::StringFunc::Equal(fileName.Native(), Audio::Wwise::InitBank)) { - m_audioSystemImpl->CreateControl(SControlDef(fileName, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath)); + m_audioSystemImpl->CreateControl( + SControlDef(AZStd::string{ fileName.Native() }, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath)); } } } @@ -103,14 +96,14 @@ namespace AudioControls if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str())) { - LoadControlsInFolder(filePath); + LoadControlsInFolder(filePath.Native()); } else { // Open the file, read into an xmlDoc, and call LoadControls with the root xml node... AZ_TracePrintf("AudioWwiseLoader", "Loading Xml from '%s'", filePath.c_str()); - Audio::ScopedXmlLoader xmlFileLoader(filePath); + Audio::ScopedXmlLoader xmlFileLoader(filePath.Native()); if (!xmlFileLoader.HasError()) { LoadControl(xmlFileLoader.GetRootNode()); diff --git a/Gems/AudioSystem/Code/Include/Editor/ACETypes.h b/Gems/AudioSystem/Code/Include/Editor/ACETypes.h index cc1e35028c..0bfaa6640c 100644 --- a/Gems/AudioSystem/Code/Include/Editor/ACETypes.h +++ b/Gems/AudioSystem/Code/Include/Editor/ACETypes.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace AudioControls { @@ -39,4 +40,7 @@ namespace AudioControls using FilepathSet = AZStd::set; + using XmlAllocator = AZ::rapidxml::memory_pool<>; + inline static XmlAllocator s_xmlAllocator; + } // namespace AudioControls diff --git a/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h b/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h index 5196e2f0bc..5a9c773a32 100644 --- a/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h +++ b/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h @@ -12,12 +12,10 @@ #include #include #include +#include #include -#include -#include - namespace AudioControls { class IAudioSystemEditor; @@ -117,14 +115,14 @@ namespace AudioControls //! @param node XML node where the connection is defined. //! @param atlControlType The type of the ATL control you are connecting to. //! @return A pointer to the newly created connection. - virtual TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) = 0; + virtual TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* node, EACEControlType atlControlType) = 0; //! When serializing connections between controls this function will be called once per connection to serialize its properties. //! This function should be in sync with CreateConnectionToControl as whatever it's written here will have to be read there. //! @param connection Connection to serialize. //! @param atlControlType Type of the ATL control that has this connection. //! @return XML node with the connection serialized. - virtual XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) = 0; + virtual AZ::rapidxml::xml_node* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) = 0; //! Whenever a connection is removed from an ATL control this function should be called. //! To keep the system informed of which controls have been connected and which ones haven't. diff --git a/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h b/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h index dae411fd00..45997b1262 100644 --- a/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h +++ b/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h @@ -47,6 +47,7 @@ namespace Audio static constexpr const char* ATLInternalNameAttribute = "atl_internal_name"; static constexpr const char* ATLTypeAttribute = "atl_type"; static constexpr const char* ATLConfigGroupAttribute = "atl_config_group_name"; + static constexpr const char* ATLPathAttribute = "path"; static constexpr const char* ATLDataLoadType = "AutoLoad"; diff --git a/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h b/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h index 5f071fef63..37952eaa1a 100644 --- a/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h +++ b/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -20,22 +21,26 @@ namespace Audio /*! * FindFilesInPath */ - static AZStd::vector FindFilesInPath(const AZStd::string_view folderPath, const char* filter) + static AZStd::vector FindFilesInPath(const AZStd::string_view folderPath, const char* filter) { - AZStd::vector foundFiles; + AZStd::vector foundFiles; AZ::IO::FileIOBase::FindFilesCallbackType findFilesCallback = [&foundFiles](const char* file) -> bool { - foundFiles.emplace_back(file); + foundFiles.emplace_back(AZ::IO::FixedMaxPath{ file }.LexicallyNormal()); return true; }; - auto fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO) + if (auto fileIO = AZ::IO::FileIOBase::GetInstance(); + fileIO != nullptr) { AZ::IO::Result result = fileIO->FindFiles(folderPath.data(), filter, findFilesCallback); + if (result == AZ::IO::ResultCode::Success) + { + return AZStd::move(foundFiles); + } } - return foundFiles; + return {}; } /*! diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h index f7c856fb46..9335429fd4 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h @@ -11,13 +11,11 @@ #include #include +#include #include #include -#include -#include - namespace AudioControls { class CATLControlsModel; @@ -25,15 +23,52 @@ namespace AudioControls //-------------------------------------------------------------------------------------------// struct SRawConnectionData { - SRawConnectionData(XmlNodeRef node, bool isValid) - : m_xmlNode(node) - , m_isValid(isValid) - {} + SRawConnectionData(AZ::rapidxml::xml_node* node, bool isValid) + { + m_xmlNode = DeepCopyNode(node); + m_isValid = isValid; + } - XmlNodeRef m_xmlNode; + AZ::rapidxml::xml_node* m_xmlNode{ nullptr }; // indicates if the connection is valid for the currently loaded middleware - bool m_isValid; + bool m_isValid{ false }; + + // Rapid XML provides a 'copy_node' utility that will copy an entire node tree, + // but it only copies pointers of any strings in the node names and values. + // This causes problems with storing raw xml nodes as this class does because strings + // will be pointing into the memory pool of an xml document that has gone out of scope. + // This function is a rewritten version of 'copy_node' that does the deep copy of strings + // into the new destination tree. + static AZ::rapidxml::xml_node* DeepCopyNode(AZ::rapidxml::xml_node* srcNode) + { + AZ::rapidxml::xml_node* destNode = nullptr; + if (srcNode) + { + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + destNode = xmlAlloc.allocate_node(srcNode->type()); + + destNode->name(xmlAlloc.allocate_string(srcNode->name(), srcNode->name_size()), srcNode->name_size()); + destNode->value(xmlAlloc.allocate_string(srcNode->value(), srcNode->value_size()), srcNode->value_size()); + + for (AZ::rapidxml::xml_node* child = srcNode->first_node(); child != nullptr; child = child->next_sibling()) + { + destNode->append_node(DeepCopyNode(child)); + } + + for (AZ::rapidxml::xml_attribute* attr = srcNode->first_attribute(); attr != nullptr; attr = attr->next_attribute()) + { + destNode->append_attribute(xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(attr->name(), attr->name_size()), + xmlAlloc.allocate_string(attr->value(), attr->value_size()), + attr->name_size(), + attr->value_size() + )); + } + } + + return destNode; + } }; using TXmlNodeList = AZStd::vector; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp index b352534510..d19cfe7adb 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp @@ -10,21 +10,31 @@ #include #include +#include + +#include + #include #include #include #include #include -#include -#include + + #include #include #include #include + + +#include +#include #include -#include #include +#include + + #include #include #include @@ -318,19 +328,24 @@ namespace AudioControls // once we can listen to delete messages from Asset system, this can be changed to an EBus handler. const char* controlsPath = nullptr; Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); - AZStd::string sControlsPath(Path::GetEditingGameDataFolder()); - AZ::StringFunc::Path::Join(sControlsPath.c_str(), controlsPath, sControlsPath); - Audio::SAudioManagerRequestData oParseGlobalRequestData(sControlsPath.c_str(), Audio::eADS_GLOBAL); + + AZ::IO::FixedMaxPath controlsFolder{ controlsPath }; + + Audio::SAudioManagerRequestData oParseGlobalRequestData(controlsFolder.c_str(), Audio::eADS_GLOBAL); oConfigDataRequest.pData = &oParseGlobalRequestData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest); // parse the AudioSystem level-specific config data - AZStd::string levelName{ GetIEditor()->GetLevelName().toUtf8().data() }; - AZ::StringFunc::Path::Join(sControlsPath.c_str(), "levels", sControlsPath); - AZ::StringFunc::Path::Join(sControlsPath.c_str(), levelName.c_str(), sControlsPath); - Audio::SAudioManagerRequestData oParseLevelRequestData(sControlsPath.c_str(), Audio::eADS_LEVEL_SPECIFIC); - oConfigDataRequest.pData = &oParseLevelRequestData; - Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest); + AZStd::string levelName; + AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequests::GetLevelName); + if (!levelName.empty() && levelName != "Untitled") + { + controlsFolder /= "levels"; + controlsFolder /= levelName; + Audio::SAudioManagerRequestData oParseLevelRequestData(controlsFolder.c_str(), Audio::eADS_LEVEL_SPECIFIC); + oConfigDataRequest.pData = &oParseLevelRequestData; + Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest); + } // inform the middleware specific plugin that the data has been saved // to disk (in case it needs to update something) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp index c247abf855..e0eba868d6 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp @@ -11,26 +11,21 @@ #include #include +#include #include #include #include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include +#include #include -using namespace PathUtil; - namespace AudioControls { //-------------------------------------------------------------------------------------------// @@ -91,100 +86,81 @@ namespace AudioControls { const CUndoSuspend suspendUndo; - // Get the partial path (relative under asset root) where the controls live. + // Get the relative path (under asset root) where the controls live. const char* controlsPath = nullptr; Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); // Get the full path up to asset root. - AZStd::string controlsFullPath(Path::GetEditingGameDataFolder()); - AZ::StringFunc::Path::Join(controlsFullPath.c_str(), controlsPath, controlsFullPath); + AZ::IO::FixedMaxPath controlsFullPath = AZ::Utils::GetProjectPath(); + controlsFullPath /= controlsPath; // load the global controls - LoadAllLibrariesInFolder(controlsFullPath, ""); + LoadAllLibrariesInFolder(controlsFullPath.Native(), ""); - // load the level specific controls - auto cryPak = gEnv->pCryPak; + AZ::IO::FixedMaxPath searchPath = controlsFullPath / LoaderStrings::LevelsSubFolder; - AZStd::string searchMask; - AZ::StringFunc::Path::Join(controlsFullPath.c_str(), LoaderStrings::LevelsSubFolder, searchMask); - AZ::StringFunc::Path::Join(searchMask.c_str(), "*", searchMask, true, false); - AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(searchMask.c_str()); - if (handle) + auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*"); + + for (const auto& file : foundFiles) { - do + if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(file.c_str())) { - if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory) + AZStd::string levelName{ file.Filename().Native() }; + LoadAllLibrariesInFolder(controlsFullPath.Native(), levelName); + + if (!m_atlControlsModel->ScopeExists(levelName)) { - AZStd::string_view name = handle.m_filename; - if (name != "." && name != "..") - { - LoadAllLibrariesInFolder(controlsFullPath, name); - if (!m_atlControlsModel->ScopeExists(name)) - { - // if the control doesn't exist it - // means it is not a real level in the - // project so it is flagged as LocalOnly - m_atlControlsModel->AddScope(name, true); - } - } + // If the scope doesn't exist it means it is not a real + // level in the project so it's flagged as LocalOnly + m_atlControlsModel->AddScope(levelName, true); } } - while (handle = cryPak->FindNext(handle)); - cryPak->FindClose(handle); } + CreateDefaultControls(); } //-------------------------------------------------------------------------------------------// void CAudioControlsLoader::LoadAllLibrariesInFolder(const AZStd::string_view folderPath, const AZStd::string_view level) { - AZStd::string path(folderPath); - if (path.back() != AZ_CORRECT_FILESYSTEM_SEPARATOR) - { - path.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); - } + AZ::IO::FixedMaxPath searchPath{ folderPath }; if (!level.empty()) { - path.append(LoaderStrings::LevelsSubFolder); - path.append(GetSlash()); - path.append(level); - path.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); + searchPath /= LoaderStrings::LevelsSubFolder; + searchPath /= level; } - AZStd::string searchPath = path + "*.xml"; - auto cryPak = gEnv->pCryPak; - AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(searchPath.c_str()); - if (handle) + auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*.xml"); + + for (auto& file : foundFiles) { - do + Audio::ScopedXmlLoader xmlLoader(file.Native()); + if (xmlLoader.HasError()) { - AZStd::string filename = path + AZStd::string{ static_cast(handle.m_filename) }; - AZ::StringFunc::Path::Normalize(filename); - XmlNodeRef root = GetISystem()->LoadXmlFromFile(filename.c_str()); - if (root) + AZ_Warning("AudioControlsLoader", false, "Unable to load the xml file '%s'", file.c_str()); + continue; + } + + auto xmlRootNode = xmlLoader.GetRootNode(); + if (xmlRootNode && azstricmp(xmlRootNode->name(), Audio::ATLXmlTags::RootNodeTag) == 0) + { + AZ::IO::PathView fileName = file.Filename(); + + AZStd::to_lower(file.Native().begin(), file.Native().end()); + m_loadedFilenames.insert(file.c_str()); + + if (auto nameAttr = xmlRootNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute, 0, false); nameAttr != nullptr) { - AZStd::string tag = root->getTag(); - if (tag == Audio::ATLXmlTags::RootNodeTag) - { - AZStd::to_lower(filename.begin(), filename.end()); - m_loadedFilenames.insert(filename.c_str()); - AZStd::string file = static_cast(handle.m_filename); - if (root->haveAttr(Audio::ATLXmlTags::ATLNameAttribute)) - { - file = root->getAttr(Audio::ATLXmlTags::ATLNameAttribute); - } - AZ::StringFunc::Path::StripExtension(file); - LoadControlsLibrary(root, folderPath, level, file); - } + fileName = nameAttr->value(); } else { - CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "(Audio Controls Editor) Failed parsing ATL Library '%s'", filename.c_str()); + fileName = fileName.Stem(); } - } while (handle = cryPak->FindNext(handle)); - cryPak->FindClose(handle); + LoadControlsLibrary(xmlRootNode, folderPath, level, fileName.Native()); + } } } @@ -233,74 +209,93 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsLoader::LoadControlsLibrary(XmlNodeRef rootNode, [[maybe_unused]] const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName) + void CAudioControlsLoader::LoadControlsLibrary( + const AZ::rapidxml::xml_node* rootNode, + [[maybe_unused]] const AZStd::string_view filePath, + const AZStd::string_view level, + const AZStd::string_view fileName) { QStandardItem* rootFolderItem = AddUniqueFolderPath(m_layoutModel->invisibleRootItem(), QString(fileName.data())); if (rootFolderItem && rootNode) { - const int numControlTypes = rootNode->getChildCount(); - for (int i = 0; i < numControlTypes; ++i) + auto controlTypeNode = rootNode->first_node(); // e.g. "AudioTriggers", "AudioRtpcs", etc + while (controlTypeNode) { - XmlNodeRef node = rootNode->getChild(i); - const int numControls = node->getChildCount(); - for (int j = 0; j < numControls; ++j) + auto controlNode = controlTypeNode->first_node(); // e.g. "ATLTrigger", "ATLRtpc", etc + while (controlNode) { - LoadControl(node->getChild(j), rootFolderItem, level); + LoadControl(controlNode, rootFolderItem, level); + controlNode = controlNode->next_sibling(); } + controlTypeNode = controlTypeNode->next_sibling(); } } } //-------------------------------------------------------------------------------------------// - CATLControl* CAudioControlsLoader::LoadControl(XmlNodeRef node, QStandardItem* folderItem, const AZStd::string_view scope) + CATLControl* CAudioControlsLoader::LoadControl(AZ::rapidxml::xml_node* node, QStandardItem* folderItem, const AZStd::string_view scope) { CATLControl* control = nullptr; - if (node) + + AZStd::string controlPath; + if (auto controlPathAttr = node->first_attribute("path", 0, false); + controlPathAttr != nullptr) { - QStandardItem* parentItem = AddUniqueFolderPath(folderItem, QString(node->getAttr(LoaderStrings::PathAttribute))); - if (parentItem) + controlPath = controlPathAttr->value(); + } + + QStandardItem* parentItem = AddUniqueFolderPath(folderItem, QString(controlPath.c_str())); + if (parentItem) + { + AZStd::string name; + if (auto nameAttr = node->first_attribute(Audio::ATLXmlTags::ATLNameAttribute, 0, false); + nameAttr != nullptr) { - const AZStd::string name = node->getAttr(Audio::ATLXmlTags::ATLNameAttribute); - const EACEControlType controlType = TagToType(node->getTag()); + name = nameAttr->value(); + } - control = m_atlControlsModel->CreateControl(name, controlType); - if (control) + const EACEControlType controlType = TagToType(node->name()); + + control = m_atlControlsModel->CreateControl(name, controlType); + if (control) + { + QStandardItem* item = new QAudioControlItem(QString(control->GetName().c_str()), control); + if (item) { - QStandardItem* item = new QAudioControlItem(QString(control->GetName().c_str()), control); - if (item) - { - parentItem->appendRow(item); - } - - switch (controlType) - { - case eACET_SWITCH: - { - const int numStates = node->getChildCount(); - for (int i = 0; i < numStates; ++i) - { - CATLControl* stateControl = LoadControl(node->getChild(i), item, scope); - if (stateControl) - { - stateControl->SetParent(control); - control->AddChild(stateControl); - } - } - break; - } - case eACET_PRELOAD: - { - LoadPreloadConnections(node, control); - break; - } - default: - { - LoadConnections(node, control); - break; - } - } - control->SetScope(scope); + parentItem->appendRow(item); } + + switch (controlType) + { + case eACET_SWITCH: + { + auto switchStateNode = node->first_node(); + while (switchStateNode) + { + CATLControl* stateControl = LoadControl(switchStateNode, item, scope); + if (stateControl) + { + stateControl->SetParent(control); + control->AddChild(stateControl); + } + + switchStateNode = switchStateNode->next_sibling(); + } + break; + } + case eACET_PRELOAD: + { + LoadPreloadConnections(node, control); + break; + } + default: + { + LoadConnections(node, control); + break; + } + } + + control->SetScope(scope); } } @@ -310,44 +305,37 @@ namespace AudioControls //-------------------------------------------------------------------------------------------// void CAudioControlsLoader::LoadScopes() { - AZStd::string levelsFolderPath; - AZ::StringFunc::Path::Join(Path::GetEditingGameDataFolder().c_str(), LoaderStrings::LevelsSubFolder, levelsFolderPath); - LoadScopesImpl(levelsFolderPath); + AZ::IO::FixedMaxPath levelsFolderPath = AZ::Utils::GetProjectPath(); + levelsFolderPath /= "Levels"; + LoadScopesImpl(levelsFolderPath.Native()); } //-------------------------------------------------------------------------------------------// void CAudioControlsLoader::LoadScopesImpl(const AZStd::string_view levelsFolder) { - AZStd::string search; - AZ::StringFunc::Path::Join(levelsFolder.data(), "*", search, true, false); - auto cryPak = gEnv->pCryPak; - AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(search.c_str()); - if (handle) + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ::IO::FixedMaxPath searchPath{ levelsFolder }; + + auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*"); + for (auto& file : foundFiles) { - do + AZ::IO::PathView filePath{ file }; + AZ::IO::PathView fileName = filePath.Filename(); + if (fileIO->IsDirectory(filePath.Native().data())) { - AZStd::string name = static_cast(handle.m_filename); - if (name != "." && name != ".." && !name.empty()) + LoadScopesImpl((searchPath / fileName).Native()); + } + else + { + AZ::IO::PathView fileExt = filePath.Extension(); + if (fileExt == ".ly" || fileExt == ".cry" || fileExt == ".prefab") { - if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory) - { - AZ::StringFunc::Path::Join(levelsFolder.data(), name.c_str(), search); - LoadScopesImpl(search); - } - else - { - AZStd::string extension; - AZ::StringFunc::Path::GetExtension(name.c_str(), extension, false); - if (extension.compare("cry") == 0 || extension.compare("ly") == 0) - { - AZ::StringFunc::Path::StripExtension(name); - m_atlControlsModel->AddScope(name); - } - } + AZ::IO::PathView fileStem = filePath.Stem(); + // May need to verify that .prefabs are the actual "level" prefab + // i.e. that it matches levels//.prefab + m_atlControlsModel->AddScope(fileStem.Native()); } } - while (handle = cryPak->FindNext(handle)); - cryPak->FindClose(handle); } } @@ -475,100 +463,80 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsLoader::LoadConnections(XmlNodeRef rootNode, CATLControl* control) + void CAudioControlsLoader::LoadConnections(AZ::rapidxml::xml_node* rootNode, CATLControl* control) { - if (!rootNode || !control) + if (control && rootNode && m_audioSystemImpl) { - return; - } - - const int numChildren = rootNode->getChildCount(); - for (int i = 0; i < numChildren; ++i) - { - XmlNodeRef node = rootNode->getChild(i); - const AZStd::string tag = node->getTag(); - if (m_audioSystemImpl) + auto childNode = rootNode->first_node(); + while (childNode) { - TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(node, control->GetType()); + TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(childNode, control->GetType()); if (connection) { control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(node, connection != nullptr)); + + control->m_connectionNodes.push_back(SRawConnectionData(childNode, connection != nullptr)); + + childNode = childNode->next_sibling(); } } } //-------------------------------------------------------------------------------------------// - void CAudioControlsLoader::LoadPreloadConnections(XmlNodeRef node, CATLControl* control) + void CAudioControlsLoader::LoadPreloadConnections(AZ::rapidxml::xml_node* node, CATLControl* control) { - if (!node || !control) + if (!control || !node || !m_audioSystemImpl) { return; } - AZStd::string type = node->getAttr(Audio::ATLXmlTags::ATLTypeAttribute); - if (type.compare(Audio::ATLXmlTags::ATLDataLoadType) == 0) + AZStd::string type; + if (auto typeAttr = node->first_attribute(Audio::ATLXmlTags::ATLTypeAttribute, 0, false); + typeAttr != nullptr) { - control->SetAutoLoad(true); - } - else - { - control->SetAutoLoad(false); + type = typeAttr->value(); } - // Legacy Preload XML parsing... - // Read all the platform definitions for this control - XmlNodeRef platformsGroupNode = node->findChild(Audio::ATLXmlTags::ATLPlatformsTag); - if (platformsGroupNode) + control->SetAutoLoad(type == Audio::ATLXmlTags::ATLDataLoadType); + + auto platformGroupNode = node->first_node(Audio::ATLXmlTags::ATLPlatformsTag, 0, false); + if (platformGroupNode) { + // Legacy preload parsing... // Don't parse the platform groups xml chunk anymore. // Read the connection information for all connected preloads... - const int numChildren = node->getChildCount(); - for (int i = 0; i < numChildren; ++i) + auto configGroupNode = node->first_node(Audio::ATLXmlTags::ATLConfigGroupTag, 0, false); + while (configGroupNode) { - XmlNodeRef groupNode = node->getChild(i); - const AZStd::string tag = groupNode->getTag(); - if (tag.compare(Audio::ATLXmlTags::ATLConfigGroupTag) != 0) - { - continue; - } - - const AZStd::string groupName = groupNode->getAttr(Audio::ATLXmlTags::ATLNameAttribute); - const int numConnections = groupNode->getChildCount(); - for (int j = 0; j < numConnections; ++j) - { - XmlNodeRef connectionNode = groupNode->getChild(j); - if (connectionNode && m_audioSystemImpl) - { - TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType()); - if (connection) - { - control->AddConnection(connection); - } - control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); - } - } - } - } - else - { - // New Preload XML parsing... - const int numChildren = node->getChildCount(); - for (int i = 0; i < numChildren; ++i) - { - XmlNodeRef connectionNode = node->getChild(i); - if (connectionNode && m_audioSystemImpl) + auto connectionNode = configGroupNode->first_node(); + while (connectionNode) { TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType()); if (connection) { control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); + connectionNode = connectionNode->next_sibling(); } + configGroupNode = configGroupNode->next_sibling(); + } + } + else + { + // New format preload parsing... + auto connectionNode = node->first_node(); + while (connectionNode) + { + TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType()); + if (connection) + { + control->AddConnection(connection); + } + control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); + connectionNode = connectionNode->next_sibling(); } } } @@ -586,15 +554,28 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - CATLControl* CAudioControlsLoader::CreateInternalSwitchState(CATLControl* parentControl, const AZStd::string& switchName, const AZStd::string& stateName) + CATLControl* CAudioControlsLoader::CreateInternalSwitchState(CATLControl* parentControl, [[maybe_unused]] const AZStd::string& switchName, const AZStd::string& stateName) { CATLControl* childControl = m_atlControlsModel->CreateControl(stateName, eACET_SWITCH_STATE, parentControl); - XmlNodeRef requestNode = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::ATLSwitchRequestTag); - requestNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, switchName.c_str()); - XmlNodeRef valueNode = requestNode->createNode(Audio::ATLXmlTags::ATLValueTag); - valueNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, stateName.c_str()); - requestNode->addChild(valueNode); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + AZ::rapidxml::xml_node* requestNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLSwitchRequestTag)); + + AZ::rapidxml::xml_attribute* switchNameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(switchName.c_str())); + + requestNode->append_attribute(switchNameAttr); + + AZ::rapidxml::xml_node* valueNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLValueTag)); + + AZ::rapidxml::xml_attribute* stateNameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(stateName.c_str())); + + valueNode->append_attribute(stateNameAttr); + + requestNode->append_node(valueNode); childControl->m_connectionNodes.push_back(SRawConnectionData(requestNode, false)); return childControl; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h index bee55289e3..290327b946 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h @@ -10,12 +10,11 @@ #pragma once #include +#include #include #include -#include - #include class QStandardItemModel; @@ -38,11 +37,11 @@ namespace AudioControls private: void LoadAllLibrariesInFolder(const AZStd::string_view folderPath, const AZStd::string_view level); - void LoadControlsLibrary(XmlNodeRef rootNode, const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName); - CATLControl* LoadControl(XmlNodeRef node, QStandardItem* folderItem, const AZStd::string_view scope); + void LoadControlsLibrary(const AZ::rapidxml::xml_node* rootNode, const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName); + CATLControl* LoadControl(AZ::rapidxml::xml_node* node, QStandardItem* folderItem, const AZStd::string_view scope); - void LoadPreloadConnections(XmlNodeRef node, CATLControl* control); - void LoadConnections(XmlNodeRef rootNode, CATLControl* control); + void LoadPreloadConnections(AZ::rapidxml::xml_node* node, CATLControl* control); + void LoadConnections(AZ::rapidxml::xml_node* rootNode, CATLControl* control); void CreateDefaultControls(); QStandardItem* AddControl(CATLControl* control, QStandardItem* folderItem); diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp index bb7409e179..80f05804f1 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp @@ -9,29 +9,28 @@ #include +#include +#include #include #include +#include +#include #include #include -#include #include #include #include + #include #include #include -#include -#include -#include #include #include #include -using namespace PathUtil; - namespace AudioControls { namespace WriterStrings @@ -80,6 +79,18 @@ namespace AudioControls index = index.sibling(++i, 0); } + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + AZStd::for_each( + m_foundLibraryPaths.begin(), m_foundLibraryPaths.end(), + [fileIO](AZStd::string& libraryPath) -> void + { + AZStd::optional newLength = fileIO->ConvertToAlias(libraryPath.data(), libraryPath.size()); + if (newLength) + { + libraryPath.resize_no_construct(*newLength); + } + AZStd::to_lower(libraryPath.begin(), libraryPath.end()); + }); // Delete libraries that don't exist anymore from disk FilepathSet librariesToDelete; @@ -103,7 +114,10 @@ namespace AudioControls //-------------------------------------------------------------------------------------------// void CAudioControlsWriter::WriteLibrary(const AZStd::string_view libraryName, QModelIndex root) { - if (root.isValid()) + const char* controlsPath = nullptr; + Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); + + if (root.isValid() && controlsPath) { TLibraryStorage library; int i = 0; @@ -114,68 +128,63 @@ namespace AudioControls child = root.model()->index(++i, 0, root); } - const char* controlsPath = nullptr; - Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); - for (auto& libraryPair : library) { - AZStd::string libraryPath; + AZ::IO::FixedMaxPath libraryPath{ controlsPath }; const AZStd::string& scope = libraryPair.first; if (scope.empty()) { // no scope, file at the root level - libraryPath.append(controlsPath); - AZ::StringFunc::Path::Join(libraryPath.c_str(), libraryName.data(), libraryPath); - libraryPath.append(WriterStrings::LibraryExtension); + libraryPath /= libraryName; + libraryPath.ReplaceExtension(WriterStrings::LibraryExtension); } else { // with scope, inside level folder - libraryPath.append(controlsPath); - libraryPath.append(WriterStrings::LevelsSubFolder); - AZ::StringFunc::Path::Join(libraryPath.c_str(), scope.c_str(), libraryPath); - AZ::StringFunc::Path::Join(libraryPath.c_str(), libraryName.data(), libraryPath); - libraryPath.append(WriterStrings::LibraryExtension); + libraryPath /= AZ::IO::FixedMaxPath{ WriterStrings::LevelsSubFolder } / scope / libraryName; + libraryPath.ReplaceExtension(WriterStrings::LibraryExtension); } - // should be able to change this back to GamePathToFullPath once a path normalization bug has been fixed: - AZStd::string fullFilePath; - AZ::StringFunc::Path::Join(Path::GetEditingGameDataFolder().c_str(), libraryPath.c_str(), fullFilePath); - AZStd::to_lower(fullFilePath.begin(), fullFilePath.end()); + AZ::IO::FixedMaxPath fullFilePath = AZ::Utils::GetProjectPath(); + fullFilePath /= libraryPath; m_foundLibraryPaths.insert(fullFilePath.c_str()); const SLibraryScope& libScope = libraryPair.second; if (libScope.m_isDirty) { - XmlNodeRef fileNode = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::RootNodeTag); - fileNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, libraryName.data()); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + AZ::rapidxml::xml_node* fileNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::RootNodeTag)); + + AZ::rapidxml::xml_attribute* nameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(libraryName.data())); + + fileNode->append_attribute(nameAttr); for (int ii = 0; ii < eACET_NUM_TYPES; ++ii) { - if (ii != eACET_SWITCH_STATE) // switch_states are written inside the switches + if (libScope.m_nodes[ii] && libScope.m_nodes[ii]->first_node() != nullptr) { - if (libScope.m_nodes[ii]->getChildCount() > 0) - { - fileNode->addChild(libScope.m_nodes[ii]); - } + fileNode->append_node(libScope.m_nodes[ii]); } } - if (QFileInfo::exists(fullFilePath.c_str())) + if (auto fileInfo = QFileInfo(fullFilePath.c_str()); + fileInfo.exists()) { - const DWORD fileAttributes = GetFileAttributes(fullFilePath.c_str()); - if (fileAttributes & FILE_ATTRIBUTE_READONLY) + if (!fileInfo.isWritable()) { - // file is read-only - CheckOutFile(fullFilePath); + // file exists and is read-only + CheckOutFile(fullFilePath.Native()); } - fileNode->saveToFile(fullFilePath.c_str()); + + [[maybe_unused]] bool writeOk = WriteXmlToFile(fullFilePath.Native(), fileNode); } else { - // save the file, CheckOutFile will add it, since it's new - fileNode->saveToFile(fullFilePath.c_str()); - CheckOutFile(fullFilePath); + // since it's a new file, save the file first, CheckOutFile will add it + [[maybe_unused]] bool writeOk = WriteXmlToFile(fullFilePath.Native(), fileNode); + CheckOutFile(fullFilePath.Native()); } } } @@ -249,14 +258,63 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::WriteControlToXml(XmlNodeRef node, CATLControl* control, const AZStd::string_view path) + bool CAudioControlsWriter::WriteXmlToFile(const AZStd::string_view filepath, AZ::rapidxml::xml_node* rootNode) { + if (!rootNode) + { + return false; + } + + using namespace AZ::IO; + AZStd::string docString; + ByteContainerStream stringStream(&docString); + + AZ::rapidxml::xml_document xmlDoc; + xmlDoc.append_node(rootNode); + + RapidXMLStreamWriter streamWriter(&stringStream); + AZ::rapidxml::print(streamWriter.Iterator(), xmlDoc); + streamWriter.FlushCache(); + + constexpr int openMode = + (SystemFile::SF_OPEN_WRITE_ONLY | SystemFile::SF_OPEN_CREATE | SystemFile::SF_OPEN_CREATE_PATH); + + if (SystemFile fileOut; + fileOut.Open(filepath.data(), openMode)) + { + auto bytesWritten = fileOut.Write(docString.data(), docString.size()); + return (bytesWritten == docString.size()); + } + return false; + } + + //-------------------------------------------------------------------------------------------// + void CAudioControlsWriter::WriteControlToXml(AZ::rapidxml::xml_node* node, CATLControl* control, const AZStd::string_view path) + { + if (!node || !control) + { + return; + } + + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + const EACEControlType type = control->GetType(); - XmlNodeRef childNode = node->createNode(TypeToTag(type).data()); - childNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, control->GetName().c_str()); + AZStd::string_view typeName = TypeToTag(type); + + AZ::rapidxml::xml_node* childNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(typeName.data())); + + AZ::rapidxml::xml_attribute* nameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(control->GetName().c_str())); + + childNode->append_attribute(nameAttr); + if (!path.empty()) { - childNode->setAttr("path", path.data()); + AZ::rapidxml::xml_attribute* pathAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLPathAttribute), xmlAlloc.allocate_string(path.data())); + + childNode->append_attribute(pathAttr); } if (type == eACET_SWITCH) @@ -271,7 +329,11 @@ namespace AudioControls { if (control->IsAutoLoad()) { - childNode->setAttr(Audio::ATLXmlTags::ATLTypeAttribute, Audio::ATLXmlTags::ATLDataLoadType); + AZ::rapidxml::xml_attribute* loadAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLTypeAttribute), + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLDataLoadType)); + + childNode->append_attribute(loadAttr); } // New Preloads XML... @@ -282,38 +344,39 @@ namespace AudioControls WriteConnectionsToXml(childNode, control); } - node->addChild(childNode); + node->append_node(childNode); } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::WriteConnectionsToXml(XmlNodeRef node, CATLControl* control) + void CAudioControlsWriter::WriteConnectionsToXml(AZ::rapidxml::xml_node* node, CATLControl* control) { - if (control && m_audioSystemImpl) + if (node && control && m_audioSystemImpl) { TXmlNodeList otherNodes = control->m_connectionNodes; - auto end = AZStd::remove_if(otherNodes.begin(), otherNodes.end(), - [](const SRawConnectionData& node) + auto end = AZStd::remove_if( + otherNodes.begin(), otherNodes.end(), + [](const SRawConnectionData& connection) { - return node.m_isValid; + return connection.m_isValid; } ); otherNodes.erase(end, otherNodes.end()); for (auto& connectionNode : otherNodes) { - node->addChild(connectionNode.m_xmlNode); + node->append_node(SRawConnectionData::DeepCopyNode(connectionNode.m_xmlNode)); } const size_t size = control->ConnectionCount(); for (size_t i = 0; i < size; ++i) { - TConnectionPtr connection = control->GetConnectionAt(i); - if (connection) + if (TConnectionPtr connection = control->GetConnectionAt(i); + connection != nullptr) { - XmlNodeRef childNode = m_audioSystemImpl->CreateXMLNodeFromConnection(connection, control->GetType()); - if (childNode) + if (auto childNode = m_audioSystemImpl->CreateXMLNodeFromConnection(connection, control->GetType()); + childNode != nullptr) { - node->addChild(childNode); + node->append_node(childNode); control->m_connectionNodes.push_back(SRawConnectionData(childNode, true)); } } @@ -322,24 +385,24 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::CheckOutFile(const AZStd::string& filepath) + void CAudioControlsWriter::CheckOutFile(const AZStd::string_view filepath) { IEditor* editor = GetIEditor(); IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr; if (fileUtil) { - fileUtil->CheckoutFile(filepath.c_str(), nullptr); + fileUtil->CheckoutFile(filepath.data(), nullptr); } } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::DeleteLibraryFile(const AZStd::string& filepath) + void CAudioControlsWriter::DeleteLibraryFile(const AZStd::string_view filepath) { IEditor* editor = GetIEditor(); IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr; if (fileUtil) { - fileUtil->DeleteFromSourceControl(filepath.c_str(), nullptr); + fileUtil->DeleteFromSourceControl(filepath.data(), nullptr); } } diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h index 9e3978b94b..1414f6725d 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h @@ -16,10 +16,9 @@ #include #include #include -#include + #include -#include class QStandardItemModel; @@ -33,15 +32,16 @@ namespace AudioControls { SLibraryScope() { - m_nodes[eACET_TRIGGER] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::TriggersNodeTag); - m_nodes[eACET_RTPC] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::RtpcsNodeTag); - m_nodes[eACET_SWITCH] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::SwitchesNodeTag); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + m_nodes[eACET_TRIGGER] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::TriggersNodeTag); + m_nodes[eACET_RTPC] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::RtpcsNodeTag); + m_nodes[eACET_SWITCH] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::SwitchesNodeTag); m_nodes[eACET_SWITCH_STATE] = nullptr; - m_nodes[eACET_ENVIRONMENT] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::EnvironmentsNodeTag); - m_nodes[eACET_PRELOAD] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::PreloadsNodeTag); + m_nodes[eACET_ENVIRONMENT] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::EnvironmentsNodeTag); + m_nodes[eACET_PRELOAD] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::PreloadsNodeTag); } - XmlNodeRef m_nodes[eACET_NUM_TYPES]; + AZ::rapidxml::xml_node* m_nodes[eACET_NUM_TYPES]; bool m_isDirty = false; }; @@ -56,12 +56,13 @@ namespace AudioControls private: void WriteLibrary(const AZStd::string_view libraryName, QModelIndex root); void WriteItem(QModelIndex index, const AZStd::string& path, TLibraryStorage& library, bool isParentModified); - void WriteControlToXml(XmlNodeRef node, CATLControl* control, const AZStd::string_view path); - void WriteConnectionsToXml(XmlNodeRef node, CATLControl* control); + void WriteControlToXml(AZ::rapidxml::xml_node* node, CATLControl* control, const AZStd::string_view path); + void WriteConnectionsToXml(AZ::rapidxml::xml_node* node, CATLControl* control); bool IsItemModified(QModelIndex index); - void CheckOutFile(const AZStd::string& filepath); - void DeleteLibraryFile(const AZStd::string& filepath); + bool WriteXmlToFile(const AZStd::string_view filepath, AZ::rapidxml::xml_node* rootNode); + void CheckOutFile(const AZStd::string_view filepath); + void DeleteLibraryFile(const AZStd::string_view filepath); CATLControlsModel* m_atlModel; QStandardItemModel* m_layoutModel; diff --git a/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h b/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h index ce77125443..4d322a49d9 100644 --- a/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h +++ b/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h @@ -31,7 +31,7 @@ namespace AudioControls iconFile = ":/Icons/Switch_Icon.svg"; break; case AudioControls::eACET_SWITCH_STATE: - iconFile = ":/Icons/Property_Icon.svg"; + iconFile = ":/Icons/Property_Icon.png"; break; case AudioControls::eACET_ENVIRONMENT: iconFile = ":/Icons/Environment_Icon.svg"; @@ -41,7 +41,7 @@ namespace AudioControls break; default: // should make a "default"/empty icon... - iconFile = ":/Icons/RTPC_Icon.svg"; + iconFile = ":/Icons/Unassigned.svg"; } QIcon icon(iconFile); diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp b/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp index ca9cb214e2..7010012e87 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp @@ -1005,14 +1005,14 @@ namespace Audio AZStd::string searchPath; AZ::StringFunc::Path::Join(m_rootPath.c_str(), folderPath, searchPath); - AZStd::vector foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); + auto foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); for (const auto& file : foundFiles) { AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(file.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", file.c_str()); g_audioLogger.Log(eALT_ALWAYS, "Loading Audio Controls Library: '%s'", file.c_str()); - Audio::ScopedXmlLoader xmlFileLoader(file); + Audio::ScopedXmlLoader xmlFileLoader(file.Native()); if (xmlFileLoader.HasError()) { continue; @@ -1053,14 +1053,14 @@ namespace Audio AZStd::string searchPath; AZ::StringFunc::Path::Join(m_rootPath.c_str(), folderPath, searchPath); - AZStd::vector foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); + auto foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); for (const auto& file : foundFiles) { AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(file.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", file.c_str()); g_audioLogger.Log(eALT_ALWAYS, "Loading Audio Preloads Library: '%s'", file.c_str()); - Audio::ScopedXmlLoader xmlFileLoader(file); + Audio::ScopedXmlLoader xmlFileLoader(file.Native()); if (xmlFileLoader.HasError()) { continue;