Files
o3de/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsContainer.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

326 lines
10 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
*
*/
#include "ProjectSettingsContainer.h"
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/stringbuffer.h>
#include <AzCore/XML/rapidxml_print.h>
#include <CryCommon/platform.h>
#include <Util/FileUtil.h>
namespace ProjectSettingsTool
{
using namespace AZ;
using StringOutcome = Outcome<void, AZStd::string>;
using XmlDocument = rapidxml::xml_document<char>;
using XmlNode = rapidxml::xml_node<char>;
const int xmlFlags = rapidxml::parse_doctype_node | rapidxml::parse_declaration_node | rapidxml::parse_no_data_nodes;
SettingsError::SettingsError(const AZStd::string& error, const AZStd::string& reason)
{
m_error = error;
m_reason = reason;
}
StringOutcome WriteConfigFile(const AZStd::string& fileName, const AZStd::string& fileContents)
{
using AZ::IO::SystemFile;
const char* filePath = fileName.c_str();
// Attempt to make file writable or check it out in source control
if (CFileUtil::OverwriteFile(filePath))
{
if (!CFileUtil::CreateDirectory(fileName.substr(0, fileName.find_last_of('/')).data()))
{
return AZ::Failure(AZStd::string::format("Could not create the directory for file \"%s\".", filePath));
}
SystemFile settingsFile;
if (!settingsFile.Open(filePath, SystemFile::SF_OPEN_WRITE_ONLY | SystemFile::SF_OPEN_CREATE))
{
return AZ::Failure(AZStd::string::format("Failed to open settings file %s for write.", filePath));
}
if (settingsFile.Write(fileContents.c_str(), fileContents.size()) != fileContents.size())
{
return AZ::Failure(AZStd::string::format("Failed to write to file %s.", filePath));
}
settingsFile.Close();
return AZ::Success();
}
return AZ::Failure(AZStd::string::format("Could not check out or make file writable: \"%s\".", filePath));
}
StringOutcome ReadConfigFile(const AZStd::string& fileName, AZStd::string& fileContents)
{
using AZ::IO::SystemFile;
const char* filePath = fileName.c_str();
if (!SystemFile::Exists(filePath))
{
return AZ::Failure(AZStd::string::format("%s file doesn't exist.", filePath));
}
SystemFile settingsFile;
if (!settingsFile.Open(filePath, SystemFile::SF_OPEN_READ_ONLY))
{
return AZ::Failure(AZStd::string::format("Failed to open settings file %s.", filePath));
}
fileContents = AZStd::string(settingsFile.Length(), '\0');
settingsFile.Read(fileContents.size(), fileContents.data());
settingsFile.Close();
return AZ::Success();
}
ProjectSettingsContainer::ProjectSettingsContainer(const AZStd::string& projectJsonFileName, PlistInitVector& plistPaths)
: m_projectJson(JsonSettings{ projectJsonFileName, "", AZStd::make_unique<rapidjson::Document>() })
{
LoadProjectJsonData();
for (PlatformAndPath& plistInfo : plistPaths)
{
m_pListsMap.insert(AZStd::pair<PlatformId, PlistSettings>(
plistInfo.first,
PlistSettings{ plistInfo.second, "", AZStd::make_unique<XmlDocument>() }));
// We will have to find it then because unique_ptrs are not copy constructible
// And a move constructor would copy large strings
LoadPlist(m_pListsMap.find(plistInfo.first)->second);
}
}
ProjectSettingsContainer::~ProjectSettingsContainer()
{
}
ProjectSettingsContainer::PlistSettings* ProjectSettingsContainer::GetPlistSettingsForPlatform(const Platform& plat)
{
PlistSettings* result = nullptr;
if (plat.m_type == PlatformDataType::Plist)
{
auto iter = m_pListsMap.find(plat.m_id);
if (iter != m_pListsMap.end())
{
result = &iter->second;
}
else
{
AZ_Assert(false, "Failed to find pList for platform.");
}
}
return result;
}
bool ProjectSettingsContainer::IsPlistPlatform(const Platform& plat)
{
if (plat.m_type == PlatformDataType::Plist)
{
auto iter = m_pListsMap.find(plat.m_id);
if (iter != m_pListsMap.end())
{
return true;
}
}
return false;
}
AZ::Outcome<void, SettingsError> ProjectSettingsContainer::GetError()
{
if (!m_errors.empty())
{
SettingsError error = m_errors.front();
m_errors.pop();
return AZ::Failure(error);
}
return AZ::Success();
}
void ProjectSettingsContainer::SavePlatformData(const Platform& plat)
{
PlistSettings* plistSettings = GetPlistSettingsForPlatform(plat);
if (plistSettings != nullptr)
{
SavePlist(*plistSettings);
}
else
{
SaveProjectJsonData();
}
}
void ProjectSettingsContainer::SaveProjectJsonData()
{
// Needed to write a document out to a string
rapidjson::StringBuffer jsonDataBuffer;
// Use pretty writer so it can be read easier
rapidjson::PrettyWriter<rapidjson::StringBuffer> jsonDatawriter(jsonDataBuffer);
m_projectJson.m_document->Accept(jsonDatawriter);
const AZStd::string jsonDataString = jsonDataBuffer.GetString();
StringOutcome outcome = WriteConfigFile(m_projectJson.m_path, jsonDataString);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError("Failed to save project.json", outcome.GetError()));
}
}
void ProjectSettingsContainer::ReloadProjectJsonData()
{
m_projectJson.m_document.reset(new rapidjson::Document);
LoadProjectJsonData();
}
void ProjectSettingsContainer::SavePlistsData()
{
for (AZStd::pair<PlatformId, PlistSettings>& plist : m_pListsMap)
{
LoadPlist(plist.second);
}
}
void ProjectSettingsContainer::SavePlistData(const Platform& plat)
{
PlistSettings* settings = GetPlistSettingsForPlatform(plat);
if (settings != nullptr)
{
SavePlist(*settings);
}
}
void ProjectSettingsContainer::ReloadPlistData()
{
for (AZStd::pair<PlatformId, PlistSettings>& plist : m_pListsMap)
{
PlistSettings& plistSettings = plist.second;
plistSettings.m_document.reset(new XmlDocument());
LoadPlist(plistSettings);
}
}
rapidjson::Document& ProjectSettingsContainer::GetProjectJsonDocument()
{
return *m_projectJson.m_document.get();
}
AZ::Outcome<rapidjson::Value*, void> ProjectSettingsContainer::GetProjectJsonValue(const char* key)
{
// Try to find member
rapidjson::Document& settings = *m_projectJson.m_document;
rapidjson::Value::MemberIterator memberIterator = settings.FindMember(key);
if (memberIterator != settings.MemberEnd())
{
return AZ::Success(&memberIterator->value);
}
else
{
settings.AddMember(rapidjson::Value(key, settings.GetAllocator()),
rapidjson::Value(rapidjson::kNullType), settings.GetAllocator());
return AZ::Success(&settings.FindMember(key)->value);
}
}
AZStd::unique_ptr<PlistDictionary> ProjectSettingsContainer::GetPlistDictionary(const Platform& plat)
{
if (plat.m_type == PlatformDataType::Plist)
{
PlistSettings* settings = GetPlistSettingsForPlatform(plat);
if (settings != nullptr)
{
if (PlistDictionary::ContainsValidDict(settings->m_document.get()))
{
return AZStd::make_unique<PlistDictionary>(settings->m_document.get());
}
else
{
//TODO: Query user if they would like to remake a valid plist then do it
AZStd::string platformName;
switch (plat.m_id)
{
case PlatformId::Ios:
platformName = "iOS";
break;
default:
platformName = "unknown";
break;
}
AZ_Assert(false, "%s pList is in invalid state.", platformName.c_str());
return nullptr;
}
}
}
AZ_Assert(false, "This platform does not use pLists to store data.");
return nullptr;
}
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& ProjectSettingsContainer::GetProjectJsonAllocator()
{
return m_projectJson.m_document->GetAllocator();
}
const char* ProjectSettingsContainer::GetFailedLoadingPlistText()
{
return "Failed to load info.plist";
}
void ProjectSettingsContainer::LoadProjectJsonData()
{
StringOutcome outcome = ReadConfigFile(m_projectJson.m_path, m_projectJson.m_rawData);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError("Failed to load project.json", outcome.GetError()));
}
m_projectJson.m_document->Parse(m_projectJson.m_rawData.c_str());
}
// Loads info.plist for iOS from disk
void ProjectSettingsContainer::LoadPlist(PlistSettings& plistSettings)
{
StringOutcome outcome = ReadConfigFile(plistSettings.m_path, plistSettings.m_rawData);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError(GetFailedLoadingPlistText(), outcome.GetError()));
}
plistSettings.m_document->parse<xmlFlags>(plistSettings.m_rawData.data());
}
void ProjectSettingsContainer::SavePlist(PlistSettings& plistSettings)
{
// Needed to write a document out to a string
AZStd::string xmlDocString;
rapidxml::print(std::back_inserter(xmlDocString), *plistSettings.m_document);
StringOutcome outcome = WriteConfigFile(plistSettings.m_path, xmlDocString);
if (!outcome.IsSuccess())
{
m_errors.push(SettingsError("Failed to save info.pList", outcome.GetError()));
}
}
} // namespace ProjectSettingsTool