#include "platform.hpp" #include "config.hpp" #include #include namespace connector { namespace { std::string environment(const char* name) { const char* value = std::getenv(name); return value ? value : ""; } void append_path_candidates(std::vector& candidates) { const std::string value = environment("PATH"); #ifdef _WIN32 constexpr char delimiter = ';'; constexpr const char* name = "Spine.com"; #else constexpr char delimiter = ':'; constexpr const char* name = "Spine"; #endif std::size_t start = 0; while (start <= value.size()) { const auto end = value.find(delimiter, start); std::string item = value.substr(start, end - start); if (item.size() >= 2 && item.front() == '"' && item.back() == '"') item = item.substr(1, item.size() - 2); if (!item.empty()) { const auto path = to_path(item); if (!path.empty()) candidates.push_back(path / name); } if (end == std::string::npos) break; start = end + 1; } } } // namespace std::filesystem::path to_path(const std::string& value) { try { return std::filesystem::u8path(value); } catch (...) { // 输入不是合法 UTF-8 时回退到本地代码页构造。 try { return std::filesystem::path(value); } catch (...) { // 本地代码页也无法转换时返回空路径,由调用方跳过。 return std::filesystem::path(); } } } std::string platform_name() { #ifdef _WIN32 return "win32"; #elif defined(__APPLE__) return "darwin"; #else return "linux"; #endif } bool valid_spine_executable(const std::string& path) { if (path.empty()) return false; std::error_code error; const auto status = std::filesystem::status(to_path(path), error); return !error && std::filesystem::is_regular_file(status); } std::string find_spine_executable() { try { std::vector candidates; const std::string environment_path = environment("SPINE_EXECUTABLE"); if (!environment_path.empty()) { const auto path = to_path(environment_path); if (!path.empty()) candidates.push_back(path); } const ConnectorConfig config = load_config(); if (!config.spine_executable.empty()) { const auto path = to_path(config.spine_executable); if (!path.empty()) candidates.push_back(path); } #ifdef _WIN32 for (const char* variable : {"ProgramFiles", "ProgramFiles(x86)", "LOCALAPPDATA"}) { const std::string root = environment(variable); if (root.empty()) continue; const auto root_path = to_path(root); if (root_path.empty()) continue; candidates.push_back(root_path / "Spine" / "Spine.com"); candidates.push_back(root_path / "Esoteric Software" / "Spine" / "Spine.com"); candidates.push_back(root_path / "Programs" / "Spine" / "Spine.com"); } #elif defined(__APPLE__) candidates.emplace_back("/Applications/Spine.app/Contents/MacOS/Spine"); const std::string home = environment("HOME"); if (!home.empty()) { const auto path = to_path(home); if (!path.empty()) candidates.push_back(path / "Applications" / "Spine.app" / "Contents" / "MacOS" / "Spine"); } #else candidates.emplace_back("/opt/Spine/Spine.sh"); candidates.emplace_back("/usr/local/bin/Spine"); candidates.emplace_back("/usr/bin/Spine"); #endif append_path_candidates(candidates); for (const auto& candidate : candidates) { const std::string path = candidate.u8string(); if (valid_spine_executable(path)) return path; } return ""; } catch (...) { // 任何异常都不应导致程序崩溃,返回未找到。 return ""; } } } // namespace connector