#include "MetaCoreFoundation/MetaCoreProject.h" #include #include #include #include namespace MetaCore { namespace { [[nodiscard]] std::string MetaCorePathToPortableString(const std::filesystem::path& path) { return path.lexically_normal().generic_string(); } [[nodiscard]] std::optional 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 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 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 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 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 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 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 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