You're breathtaking!
This commit is contained in:
307
Source/Editor/ProjectInfo.cpp
Normal file
307
Source/Editor/ProjectInfo.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user