166 lines
4.6 KiB
C++
166 lines
4.6 KiB
C++
#pragma once
|
|
|
|
#include "Platform/Win32Utf8.h"
|
|
|
|
#include <shellapi.h>
|
|
|
|
#include <filesystem>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <system_error>
|
|
|
|
namespace XCEngine {
|
|
namespace Editor {
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace detail {
|
|
|
|
inline fs::path NormalizePath(const fs::path& path, const fs::path& basePath = {}) {
|
|
if (path.empty()) {
|
|
return {};
|
|
}
|
|
|
|
fs::path absolutePath = path;
|
|
std::error_code ec;
|
|
if (absolutePath.is_relative()) {
|
|
absolutePath = basePath.empty()
|
|
? fs::absolute(absolutePath, ec)
|
|
: fs::absolute(basePath / absolutePath, ec);
|
|
if (ec) {
|
|
absolutePath = basePath.empty() ? absolutePath : (basePath / path);
|
|
}
|
|
}
|
|
|
|
ec.clear();
|
|
const fs::path canonicalPath = fs::weakly_canonical(absolutePath, ec);
|
|
if (!ec) {
|
|
return canonicalPath.lexically_normal();
|
|
}
|
|
|
|
return absolutePath.lexically_normal();
|
|
}
|
|
|
|
inline bool IsWorkspaceRoot(const fs::path& candidate) {
|
|
std::error_code ec;
|
|
return fs::exists(candidate / L"CMakeLists.txt", ec) &&
|
|
fs::is_directory(candidate / L"editor", ec) &&
|
|
fs::is_directory(candidate / L"engine", ec);
|
|
}
|
|
|
|
inline bool IsEditorProjectRoot(const fs::path& candidate) {
|
|
std::error_code ec;
|
|
return fs::exists(candidate / L"Project.xcproject", ec) ||
|
|
fs::is_directory(candidate / L"Assets", ec);
|
|
}
|
|
|
|
inline fs::path ResolveWorkspaceDefaultProjectRoot(const fs::path& workspaceRoot) {
|
|
if (workspaceRoot.empty()) {
|
|
return {};
|
|
}
|
|
|
|
const fs::path preferredProjectRoot = workspaceRoot / L"project";
|
|
if (IsEditorProjectRoot(preferredProjectRoot)) {
|
|
return NormalizePath(preferredProjectRoot);
|
|
}
|
|
|
|
if (IsEditorProjectRoot(workspaceRoot)) {
|
|
return NormalizePath(workspaceRoot);
|
|
}
|
|
|
|
return NormalizePath(preferredProjectRoot);
|
|
}
|
|
|
|
inline std::optional<fs::path> FindWorkspaceRoot(fs::path start) {
|
|
if (start.empty()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
start = NormalizePath(start);
|
|
for (fs::path current = start; !current.empty(); ) {
|
|
if (IsWorkspaceRoot(current)) {
|
|
return current;
|
|
}
|
|
|
|
const fs::path parent = current.parent_path();
|
|
if (parent == current) {
|
|
break;
|
|
}
|
|
current = parent;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
inline std::optional<fs::path> ParseCommandLineProjectOverride(const fs::path& workingDirectory) {
|
|
int argc = 0;
|
|
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
|
if (!argv || argc <= 1) {
|
|
if (argv) {
|
|
LocalFree(argv);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<fs::path> projectOverride;
|
|
for (int i = 1; i < argc; ++i) {
|
|
const std::wstring arg = argv[i] ? argv[i] : L"";
|
|
if ((arg == L"--project" || arg == L"-p") && i + 1 < argc) {
|
|
projectOverride = NormalizePath(fs::path(argv[++i]), workingDirectory);
|
|
break;
|
|
}
|
|
|
|
static const std::wstring kProjectArgPrefix = L"--project=";
|
|
if (arg.rfind(kProjectArgPrefix, 0) == 0) {
|
|
projectOverride = NormalizePath(fs::path(arg.substr(kProjectArgPrefix.size())), workingDirectory);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalFree(argv);
|
|
return projectOverride;
|
|
}
|
|
|
|
inline std::string PathToUtf8(const fs::path& path) {
|
|
return Platform::WideToUtf8(path.wstring());
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
inline std::string ResolveEditorProjectRootUtf8() {
|
|
std::error_code ec;
|
|
const fs::path workingDirectory = detail::NormalizePath(fs::current_path(ec));
|
|
const fs::path executableDirectory = detail::NormalizePath(fs::path(Platform::Utf8ToWide(Platform::GetExecutableDirectoryUtf8())));
|
|
|
|
if (const auto projectOverride = detail::ParseCommandLineProjectOverride(workingDirectory.empty() ? executableDirectory : workingDirectory)) {
|
|
return detail::PathToUtf8(*projectOverride);
|
|
}
|
|
|
|
if (const auto workspaceRoot = detail::FindWorkspaceRoot(workingDirectory)) {
|
|
return detail::PathToUtf8(detail::ResolveWorkspaceDefaultProjectRoot(*workspaceRoot));
|
|
}
|
|
|
|
if (const auto workspaceRoot = detail::FindWorkspaceRoot(executableDirectory)) {
|
|
return detail::PathToUtf8(detail::ResolveWorkspaceDefaultProjectRoot(*workspaceRoot));
|
|
}
|
|
|
|
if (!workingDirectory.empty()) {
|
|
return detail::PathToUtf8(workingDirectory);
|
|
}
|
|
|
|
return detail::PathToUtf8(executableDirectory);
|
|
}
|
|
|
|
inline bool SetEditorWorkingDirectory(const std::string& projectRootUtf8) {
|
|
if (projectRootUtf8.empty()) {
|
|
return false;
|
|
}
|
|
|
|
std::error_code ec;
|
|
fs::current_path(fs::path(Platform::Utf8ToWide(projectRootUtf8)), ec);
|
|
return !ec;
|
|
}
|
|
|
|
} // namespace Editor
|
|
} // namespace XCEngine
|