#pragma once #include #include #include #include namespace XCEngine { namespace Editor { namespace ProjectFileUtils { namespace fs = std::filesystem; struct ProjectDescriptor { std::string name; std::string startupScene; }; struct GraphicsSettingsDescriptor { std::string renderPipelineAssetAssembly; std::string renderPipelineAssetNamespace; std::string renderPipelineAssetClass; bool HasRenderPipelineAsset() const { return !renderPipelineAssetAssembly.empty() && !renderPipelineAssetClass.empty(); } }; inline fs::path GetProjectFilePath(const std::string& projectRoot) { return fs::path(projectRoot) / "Project.xcproject"; } inline fs::path GetProjectSettingsDirectory(const std::string& projectRoot) { return fs::path(projectRoot) / "ProjectSettings"; } inline fs::path GetGraphicsSettingsFilePath(const std::string& projectRoot) { return GetProjectSettingsDirectory(projectRoot) / "GraphicsSettings.asset"; } 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 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 bool SaveProjectGraphicsSettings( const std::string& projectRoot, const GraphicsSettingsDescriptor& descriptor) { std::error_code ec; fs::create_directories(GetProjectSettingsDirectory(projectRoot), ec); std::ofstream output( GetGraphicsSettingsFilePath(projectRoot), std::ios::out | std::ios::trunc); if (!output.is_open()) { return false; } output << "version=1\n"; output << "render_pipeline_asset_assembly=" << descriptor.renderPipelineAssetAssembly << "\n"; output << "render_pipeline_asset_namespace=" << descriptor.renderPipelineAssetNamespace << "\n"; output << "render_pipeline_asset_class=" << descriptor.renderPipelineAssetClass << "\n"; return output.good(); } inline std::optional LoadProjectGraphicsSettings( const std::string& projectRoot) { std::ifstream input(GetGraphicsSettingsFilePath(projectRoot)); if (!input.is_open()) { return std::nullopt; } GraphicsSettingsDescriptor descriptor; 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 == "render_pipeline_asset_assembly") { descriptor.renderPipelineAssetAssembly = value; } else if (key == "render_pipeline_asset_namespace") { descriptor.renderPipelineAssetNamespace = value; } else if (key == "render_pipeline_asset_class") { descriptor.renderPipelineAssetClass = 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