198 lines
7.0 KiB
C++
198 lines
7.0 KiB
C++
#include "MetaCoreFoundation/MetaCoreProject.h"
|
|
|
|
#include <array>
|
|
#include <fstream>
|
|
#include <regex>
|
|
#include <sstream>
|
|
|
|
namespace MetaCore {
|
|
|
|
namespace {
|
|
|
|
[[nodiscard]] std::string MetaCorePathToPortableString(const std::filesystem::path& path) {
|
|
return path.lexically_normal().generic_string();
|
|
}
|
|
|
|
[[nodiscard]] std::optional<std::string> MetaCoreReadTextFile(const std::filesystem::path& path) {
|
|
std::ifstream input(path);
|
|
if (!input.is_open()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::ostringstream stream;
|
|
stream << input.rdbuf();
|
|
return stream.str();
|
|
}
|
|
|
|
[[nodiscard]] std::optional<std::string> MetaCoreFindJsonStringValue(
|
|
const std::string& document,
|
|
const std::string& key
|
|
) {
|
|
const std::regex pattern("\\\"" + key + "\\\"\\s*:\\s*\\\"([^\\\"]*)\\\"");
|
|
std::smatch match;
|
|
if (!std::regex_search(document, match, pattern)) {
|
|
return std::nullopt;
|
|
}
|
|
return match[1].str();
|
|
}
|
|
|
|
[[nodiscard]] std::vector<std::string> MetaCoreFindJsonStringArray(
|
|
const std::string& document,
|
|
const std::string& key
|
|
) {
|
|
const std::regex arrayPattern("\\\"" + key + "\\\"\\s*:\\s*\\[([^\\]]*)\\]");
|
|
std::smatch arrayMatch;
|
|
if (!std::regex_search(document, arrayMatch, arrayPattern)) {
|
|
return {};
|
|
}
|
|
|
|
std::vector<std::string> results;
|
|
const std::regex stringPattern("\\\"([^\\\"]*)\\\"");
|
|
const std::string arrayBody = arrayMatch[1].str();
|
|
for (std::sregex_iterator iterator(arrayBody.begin(), arrayBody.end(), stringPattern), end; iterator != end; ++iterator) {
|
|
results.push_back((*iterator)[1].str());
|
|
}
|
|
return results;
|
|
}
|
|
|
|
[[nodiscard]] std::string MetaCoreEscapeJsonString(const std::string& value) {
|
|
std::string escaped;
|
|
escaped.reserve(value.size());
|
|
for (char character : value) {
|
|
switch (character) {
|
|
case '\\':
|
|
escaped += "\\\\";
|
|
break;
|
|
case '"':
|
|
escaped += "\\\"";
|
|
break;
|
|
default:
|
|
escaped += character;
|
|
break;
|
|
}
|
|
}
|
|
return escaped;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::filesystem::path MetaCoreGetProjectFilePath(const std::filesystem::path& projectRoot) {
|
|
return projectRoot / "MetaCore.project.json";
|
|
}
|
|
|
|
std::optional<std::filesystem::path> MetaCoreReadProjectRootFromEnvironment() {
|
|
char* projectPathRaw = nullptr;
|
|
std::size_t projectPathLength = 0;
|
|
if (_dupenv_s(&projectPathRaw, &projectPathLength, "METACORE_PROJECT_PATH") != 0 || projectPathRaw == nullptr) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::filesystem::path projectPath(projectPathRaw);
|
|
free(projectPathRaw);
|
|
|
|
if (projectPath.filename() == "MetaCore.project.json") {
|
|
projectPath = projectPath.parent_path();
|
|
}
|
|
|
|
if (std::filesystem::exists(MetaCoreGetProjectFilePath(projectPath))) {
|
|
std::error_code errorCode;
|
|
return std::filesystem::weakly_canonical(projectPath, errorCode);
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::filesystem::path> MetaCoreDiscoverProjectRoot(const std::filesystem::path& startDirectory) {
|
|
if (const auto environmentProjectRoot = MetaCoreReadProjectRootFromEnvironment(); environmentProjectRoot.has_value()) {
|
|
return environmentProjectRoot;
|
|
}
|
|
|
|
std::error_code errorCode;
|
|
std::filesystem::path current = std::filesystem::weakly_canonical(startDirectory, errorCode);
|
|
if (current.empty()) {
|
|
current = startDirectory;
|
|
}
|
|
|
|
while (!current.empty()) {
|
|
const std::array<std::filesystem::path, 3> candidates = {
|
|
current,
|
|
current / "SandboxProject",
|
|
current / "TestProject"
|
|
};
|
|
|
|
for (const auto& candidate : candidates) {
|
|
if (std::filesystem::exists(MetaCoreGetProjectFilePath(candidate))) {
|
|
return std::filesystem::weakly_canonical(candidate, errorCode);
|
|
}
|
|
}
|
|
|
|
const std::filesystem::path parent = current.parent_path();
|
|
if (parent == current) {
|
|
break;
|
|
}
|
|
current = parent;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<MetaCoreProjectFileDocument> MetaCoreReadProjectFile(const std::filesystem::path& projectFilePath) {
|
|
const auto projectDocument = MetaCoreReadTextFile(projectFilePath);
|
|
if (!projectDocument.has_value()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
MetaCoreProjectFileDocument document;
|
|
if (const auto name = MetaCoreFindJsonStringValue(*projectDocument, "name"); name.has_value()) {
|
|
document.Name = *name;
|
|
}
|
|
if (const auto version = MetaCoreFindJsonStringValue(*projectDocument, "version"); version.has_value()) {
|
|
document.Version = *version;
|
|
}
|
|
if (const auto startupScene = MetaCoreFindJsonStringValue(*projectDocument, "startup_scene"); startupScene.has_value()) {
|
|
document.StartupScenePath = std::filesystem::path(*startupScene).lexically_normal();
|
|
}
|
|
if (const auto runtimeDir = MetaCoreFindJsonStringValue(*projectDocument, "runtime_directory"); runtimeDir.has_value()) {
|
|
document.RuntimeDirectory = std::filesystem::path(*runtimeDir).lexically_normal();
|
|
}
|
|
if (const auto uiDir = MetaCoreFindJsonStringValue(*projectDocument, "ui_directory"); uiDir.has_value()) {
|
|
document.UiDirectory = std::filesystem::path(*uiDir).lexically_normal();
|
|
}
|
|
if (const auto buildDir = MetaCoreFindJsonStringValue(*projectDocument, "build_directory"); buildDir.has_value()) {
|
|
document.BuildDirectory = std::filesystem::path(*buildDir).lexically_normal();
|
|
}
|
|
for (const std::string& scenePath : MetaCoreFindJsonStringArray(*projectDocument, "scenes")) {
|
|
document.ScenePaths.push_back(std::filesystem::path(scenePath).lexically_normal());
|
|
}
|
|
|
|
return document;
|
|
}
|
|
|
|
bool MetaCoreWriteProjectFile(
|
|
const std::filesystem::path& projectFilePath,
|
|
const MetaCoreProjectFileDocument& document
|
|
) {
|
|
std::ofstream output(projectFilePath, std::ios::trunc);
|
|
if (!output.is_open()) {
|
|
return false;
|
|
}
|
|
|
|
output << "{\n";
|
|
output << " \"name\": \"" << MetaCoreEscapeJsonString(document.Name) << "\",\n";
|
|
output << " \"version\": \"" << MetaCoreEscapeJsonString(document.Version) << "\",\n";
|
|
output << " \"runtime_directory\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.RuntimeDirectory)) << "\",\n";
|
|
output << " \"ui_directory\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.UiDirectory)) << "\",\n";
|
|
output << " \"build_directory\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.BuildDirectory)) << "\",\n";
|
|
output << " \"startup_scene\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.StartupScenePath)) << "\",\n";
|
|
output << " \"scenes\": [\n";
|
|
for (std::size_t index = 0; index < document.ScenePaths.size(); ++index) {
|
|
output << " \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.ScenePaths[index])) << "\"";
|
|
output << (index + 1 < document.ScenePaths.size() ? ",\n" : "\n");
|
|
}
|
|
output << " ]\n";
|
|
output << "}\n";
|
|
return true;
|
|
}
|
|
|
|
} // namespace MetaCore
|