120 lines
3.3 KiB
C
120 lines
3.3 KiB
C
|
|
#pragma once
|
||
|
|
|
||
|
|
#include <filesystem>
|
||
|
|
#include <fstream>
|
||
|
|
#include <optional>
|
||
|
|
#include <string>
|
||
|
|
|
||
|
|
namespace XCEngine {
|
||
|
|
namespace Editor {
|
||
|
|
namespace ProjectFileUtils {
|
||
|
|
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
|
||
|
|
struct ProjectDescriptor {
|
||
|
|
std::string name;
|
||
|
|
std::string startupScene;
|
||
|
|
};
|
||
|
|
|
||
|
|
inline fs::path GetProjectFilePath(const std::string& projectRoot) {
|
||
|
|
return fs::path(projectRoot) / "Project.xcproject";
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::string GetProjectName(const std::string& projectRoot) {
|
||
|
|
const fs::path rootPath(projectRoot);
|
||
|
|
const fs::path folderName = rootPath.filename().empty() ? rootPath.parent_path().filename() : rootPath.filename();
|
||
|
|
return folderName.empty() ? "XCEngineProject" : folderName.string();
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::string Trim(const std::string& value) {
|
||
|
|
const size_t begin = value.find_first_not_of(" \t\r\n");
|
||
|
|
if (begin == std::string::npos) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
const size_t end = value.find_last_not_of(" \t\r\n");
|
||
|
|
return value.substr(begin, end - begin + 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool SaveProjectDescriptor(const std::string& projectRoot, const ProjectDescriptor& descriptor) {
|
||
|
|
std::error_code ec;
|
||
|
|
fs::create_directories(fs::path(projectRoot), ec);
|
||
|
|
|
||
|
|
std::ofstream output(GetProjectFilePath(projectRoot), std::ios::out | std::ios::trunc);
|
||
|
|
if (!output.is_open()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
output << "version=1\n";
|
||
|
|
output << "name=" << descriptor.name << "\n";
|
||
|
|
output << "startup_scene=" << descriptor.startupScene << "\n";
|
||
|
|
return output.good();
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::optional<ProjectDescriptor> LoadProjectDescriptor(const std::string& projectRoot) {
|
||
|
|
std::ifstream input(GetProjectFilePath(projectRoot));
|
||
|
|
if (!input.is_open()) {
|
||
|
|
return std::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
ProjectDescriptor descriptor;
|
||
|
|
descriptor.name = GetProjectName(projectRoot);
|
||
|
|
|
||
|
|
std::string line;
|
||
|
|
while (std::getline(input, line)) {
|
||
|
|
const size_t equalsPos = line.find('=');
|
||
|
|
if (equalsPos == std::string::npos) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
const std::string key = Trim(line.substr(0, equalsPos));
|
||
|
|
const std::string value = Trim(line.substr(equalsPos + 1));
|
||
|
|
if (key == "name") {
|
||
|
|
descriptor.name = value;
|
||
|
|
} else if (key == "startup_scene") {
|
||
|
|
descriptor.startupScene = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return descriptor;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::string MakeProjectRelativePath(const std::string& projectRoot, const std::string& fullPath) {
|
||
|
|
if (projectRoot.empty() || fullPath.empty()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
std::error_code ec;
|
||
|
|
const fs::path root = fs::weakly_canonical(fs::path(projectRoot), ec);
|
||
|
|
ec.clear();
|
||
|
|
const fs::path target = fs::weakly_canonical(fs::path(fullPath), ec);
|
||
|
|
if (ec) {
|
||
|
|
return fullPath;
|
||
|
|
}
|
||
|
|
|
||
|
|
ec.clear();
|
||
|
|
const fs::path relative = fs::relative(target, root, ec);
|
||
|
|
if (ec) {
|
||
|
|
return fullPath;
|
||
|
|
}
|
||
|
|
|
||
|
|
return relative.lexically_normal().generic_string();
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::string ResolveProjectPath(const std::string& projectRoot, const std::string& pathValue) {
|
||
|
|
if (pathValue.empty()) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
const fs::path path(pathValue);
|
||
|
|
if (path.is_absolute()) {
|
||
|
|
return path.lexically_normal().string();
|
||
|
|
}
|
||
|
|
|
||
|
|
return (fs::path(projectRoot) / path).lexically_normal().string();
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace ProjectFileUtils
|
||
|
|
} // namespace Editor
|
||
|
|
} // namespace XCEngine
|