308 lines
9.7 KiB
C++
308 lines
9.7 KiB
C++
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
|
|
|
#include "ProjectInfo.h"
|
|
#include "Engine/Platform/FileSystem.h"
|
|
#include "Engine/Platform/File.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Engine/Globals.h"
|
|
#include "Engine/Core/Math/Quaternion.h"
|
|
#include "Engine/Serialization/JsonWriters.h"
|
|
#include "Engine/Serialization/JsonTools.h"
|
|
#include <ThirdParty/pugixml/pugixml.hpp>
|
|
using namespace pugi;
|
|
|
|
Array<ProjectInfo*> ProjectInfo::ProjectsCache;
|
|
|
|
void ShowProjectLoadError(const Char* errorMsg, const String& projectRootFolder)
|
|
{
|
|
Platform::Error(String::Format(TEXT("Failed to load project. {0}\nPath: '{1}'"), errorMsg, projectRootFolder));
|
|
}
|
|
|
|
Vector3 GetVector3FromXml(const xml_node& parent, const Char* name, const Vector3& defaultValue)
|
|
{
|
|
const auto node = parent.child(name);
|
|
if (!node.empty())
|
|
{
|
|
const auto x = node.child_value(TEXT("X"));
|
|
const auto y = node.child_value(TEXT("Y"));
|
|
const auto z = node.child_value(TEXT("Z"));
|
|
if (x && y && z)
|
|
{
|
|
Vector3 v;
|
|
if (!StringUtils::Parse(x, &v.X) && !StringUtils::Parse(y, &v.Y) && !StringUtils::Parse(z, &v.Z))
|
|
{
|
|
return v;
|
|
}
|
|
}
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
int32 GetIntFromXml(const xml_node& parent, const Char* name, const int32 defaultValue)
|
|
{
|
|
const auto node = parent.child_value(name);
|
|
if (node)
|
|
{
|
|
int32 v;
|
|
if (!StringUtils::Parse(node, &v))
|
|
{
|
|
return v;
|
|
}
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
bool ProjectInfo::SaveProject()
|
|
{
|
|
// Serialize object to Json
|
|
rapidjson_flax::StringBuffer buffer;
|
|
PrettyJsonWriter writerObj(buffer);
|
|
auto& stream = *(JsonWriter*)&writerObj;
|
|
stream.StartObject();
|
|
{
|
|
stream.JKEY("Name");
|
|
stream.String(Name);
|
|
|
|
stream.JKEY("Version");
|
|
stream.String(Version.ToString());
|
|
|
|
stream.JKEY("Company");
|
|
stream.String(Company);
|
|
|
|
stream.JKEY("Copyright");
|
|
stream.String(Copyright);
|
|
|
|
stream.JKEY("GameTarget");
|
|
stream.String(GameTarget);
|
|
|
|
stream.JKEY("EditorTarget");
|
|
stream.String(EditorTarget);
|
|
|
|
stream.JKEY("References");
|
|
stream.StartArray();
|
|
for (auto& reference : References)
|
|
{
|
|
stream.StartObject();
|
|
stream.JKEY("Name");
|
|
stream.String(reference.Name);
|
|
stream.EndObject();
|
|
}
|
|
stream.EndArray();
|
|
|
|
if (DefaultScene.IsValid())
|
|
{
|
|
stream.JKEY("DefaultScene");
|
|
stream.Guid(DefaultScene);
|
|
}
|
|
|
|
if (DefaultSceneSpawn != Ray(Vector3::Zero, Vector3::Forward))
|
|
{
|
|
stream.JKEY("DefaultSceneSpawn");
|
|
stream.Ray(DefaultSceneSpawn);
|
|
}
|
|
|
|
stream.JKEY("MinEngineVersion");
|
|
stream.String(MinEngineVersion.ToString());
|
|
|
|
if (EngineNickname.HasChars())
|
|
{
|
|
stream.JKEY("EngineNickname");
|
|
stream.String(EngineNickname);
|
|
}
|
|
}
|
|
stream.EndObject();
|
|
|
|
// Write to file
|
|
return File::WriteAllBytes(ProjectPath, (const byte*)buffer.GetString(), (int32)buffer.GetSize());
|
|
}
|
|
|
|
bool ProjectInfo::LoadProject(const String& projectPath)
|
|
{
|
|
// Load Json file
|
|
StringAnsi fileData;
|
|
if (File::ReadAllText(projectPath, fileData))
|
|
{
|
|
ShowProjectLoadError(TEXT("Failed to read file contents."), projectPath);
|
|
return true;
|
|
}
|
|
|
|
// Parse Json data
|
|
rapidjson_flax::Document document;
|
|
document.Parse(fileData.Get(), fileData.Length());
|
|
if (document.HasParseError())
|
|
{
|
|
ShowProjectLoadError(TEXT("Failed to parse project contents. Ensure to have valid Json format."), projectPath);
|
|
return true;
|
|
}
|
|
|
|
// Parse properties
|
|
Name = JsonTools::GetString(document, "Name", String::Empty);
|
|
ProjectPath = projectPath;
|
|
ProjectFolderPath = StringUtils::GetDirectoryName(projectPath);
|
|
const auto versionMember = document.FindMember("Version");
|
|
if (versionMember != document.MemberEnd())
|
|
{
|
|
auto& version = versionMember->value;
|
|
if (version.IsString())
|
|
{
|
|
Version::Parse(version.GetText(), &Version);
|
|
}
|
|
else if (version.IsObject())
|
|
{
|
|
Version = ::Version(
|
|
JsonTools::GetInt(version, "Major", 0),
|
|
JsonTools::GetInt(version, "Minor", 0),
|
|
JsonTools::GetInt(version, "Build", 0));
|
|
}
|
|
}
|
|
if (Version.Revision() == 0)
|
|
Version = ::Version(Version.Major(), Version.Minor(), Version.Build());
|
|
if (Version.Build() == 0 && Version.Revision() == -1)
|
|
Version = ::Version(Version.Major(), Version.Minor());
|
|
Company = JsonTools::GetString(document, "Company", String::Empty);
|
|
Copyright = JsonTools::GetString(document, "Copyright", String::Empty);
|
|
GameTarget = JsonTools::GetString(document, "GameTarget", String::Empty);
|
|
EditorTarget = JsonTools::GetString(document, "EditorTarget", String::Empty);
|
|
EngineNickname = JsonTools::GetString(document, "EngineNickname", String::Empty);
|
|
const auto referencesMember = document.FindMember("References");
|
|
if (referencesMember != document.MemberEnd())
|
|
{
|
|
const auto& references = referencesMember->value.GetArray();
|
|
References.Resize(references.Size());
|
|
for (int32 i = 0; i < References.Count(); i++)
|
|
{
|
|
auto& reference = References[i];
|
|
auto& value = references[i];
|
|
reference.Name = JsonTools::GetString(value, "Name", String::Empty);
|
|
|
|
String referencePath;
|
|
if (reference.Name.StartsWith(TEXT("$(EnginePath)")))
|
|
{
|
|
// Relative to engine root
|
|
referencePath = Globals::StartupFolder / reference.Name.Substring(14);
|
|
}
|
|
else if (reference.Name.StartsWith(TEXT("$(ProjectPath)")))
|
|
{
|
|
// Relative to project root
|
|
referencePath = ProjectFolderPath / reference.Name.Substring(15);
|
|
}
|
|
else if (!FileSystem::IsRelative(reference.Name))
|
|
{
|
|
// Relative to workspace
|
|
referencePath = Globals::StartupFolder / reference.Name;
|
|
}
|
|
else
|
|
{
|
|
// Absolute
|
|
referencePath = reference.Name;
|
|
}
|
|
StringUtils::PathRemoveRelativeParts(referencePath);
|
|
|
|
// Load referenced project
|
|
reference.Project = Load(referencePath);
|
|
if (reference.Project == nullptr)
|
|
{
|
|
LOG(Error, "Faield to load referenced project ({0}, from {1})", reference.Name, referencePath);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
DefaultScene = JsonTools::GetGuid(document, "DefaultScene");
|
|
DefaultSceneSpawn = JsonTools::GetRay(document, "DefaultSceneSpawn", Ray(Vector3::Zero, Vector3::Forward));
|
|
const auto minEngineVersionMember = document.FindMember("MinEngineVersion");
|
|
if (minEngineVersionMember != document.MemberEnd())
|
|
{
|
|
auto& minEngineVersion = minEngineVersionMember->value;
|
|
if (minEngineVersion.IsString())
|
|
{
|
|
Version::Parse(minEngineVersion.GetText(), &MinEngineVersion);
|
|
}
|
|
else if (minEngineVersionMember->value.IsObject())
|
|
{
|
|
MinEngineVersion = ::Version(
|
|
JsonTools::GetInt(minEngineVersion, "Major", 0),
|
|
JsonTools::GetInt(minEngineVersion, "Minor", 0),
|
|
JsonTools::GetInt(minEngineVersion, "Build", 0));
|
|
}
|
|
}
|
|
|
|
// Validate properties
|
|
if (Name.Length() == 0)
|
|
{
|
|
ShowProjectLoadError(TEXT("Missing project name."), projectPath);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ProjectInfo::LoadOldProject(const String& projectPath)
|
|
{
|
|
// Open Xml file
|
|
xml_document doc;
|
|
const xml_parse_result result = doc.load_file(*projectPath);
|
|
if (result.status)
|
|
{
|
|
ShowProjectLoadError(TEXT("Xml file parsing error."), projectPath);
|
|
return true;
|
|
}
|
|
|
|
// Get root node
|
|
const xml_node root = doc.child(TEXT("Project"));
|
|
if (!root)
|
|
{
|
|
ShowProjectLoadError(TEXT("Missing Project root node in xml file."), projectPath);
|
|
return true;
|
|
}
|
|
|
|
// Load data
|
|
Name = root.child_value(TEXT("Name"));
|
|
ProjectPath = projectPath;
|
|
ProjectFolderPath = StringUtils::GetDirectoryName(projectPath);
|
|
DefaultScene = Guid::Empty;
|
|
const auto defaultScene = root.child_value(TEXT("DefaultSceneId"));
|
|
if (defaultScene)
|
|
{
|
|
Guid::Parse(defaultScene, DefaultScene);
|
|
}
|
|
DefaultSceneSpawn.Position = GetVector3FromXml(root, TEXT("DefaultSceneSpawnPos"), Vector3::Zero);
|
|
DefaultSceneSpawn.Direction = Quaternion::Euler(GetVector3FromXml(root, TEXT("DefaultSceneSpawnDir"), Vector3::Zero)) * Vector3::Forward;
|
|
MinEngineVersion = ::Version(0, 0, GetIntFromXml(root, TEXT("MinVersionSupported"), 0));
|
|
|
|
// Always reference engine project
|
|
auto& flaxReference = References.AddOne();
|
|
flaxReference.Name = TEXT("$(EnginePath)/Flax.flaxproj");
|
|
flaxReference.Project = Load(Globals::StartupFolder / TEXT("Flax.flaxproj"));
|
|
if (!flaxReference.Project)
|
|
{
|
|
ShowProjectLoadError(TEXT("Failed to load Flax Engien project."), projectPath);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ProjectInfo* ProjectInfo::Load(const String& path)
|
|
{
|
|
// Try to reuse loaded file
|
|
for (int32 i = 0; i < ProjectsCache.Count(); i++)
|
|
{
|
|
if (ProjectsCache[i]->ProjectPath == path)
|
|
return ProjectsCache[i];
|
|
}
|
|
|
|
// Load
|
|
auto project = New<ProjectInfo>();
|
|
if (project->LoadProject(path))
|
|
{
|
|
Delete(project);
|
|
return nullptr;
|
|
}
|
|
|
|
// Cache project
|
|
ProjectsCache.Add(project);
|
|
return project;
|
|
}
|