连接器客户端

This commit is contained in:
tianmo
2026-09-05 23:35:45 +08:00
parent d10807d877
commit 41151b7f8b
28 changed files with 2195 additions and 12 deletions
+86
View File
@@ -0,0 +1,86 @@
#include "platform.hpp"
#include "config.hpp"
#include <cstdlib>
#include <filesystem>
namespace connector {
namespace {
std::string environment(const char* name) {
const char* value = std::getenv(name);
return value ? value : "";
}
void append_path_candidates(std::vector<std::filesystem::path>& 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()) candidates.push_back(std::filesystem::u8path(item) / name);
if (end == std::string::npos) break;
start = end + 1;
}
}
} // namespace
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(std::filesystem::u8path(path), error);
return !error && std::filesystem::is_regular_file(status);
}
std::string find_spine_executable() {
std::vector<std::filesystem::path> candidates;
const std::string environment_path = environment("SPINE_EXECUTABLE");
if (!environment_path.empty()) candidates.push_back(std::filesystem::u8path(environment_path));
const ConnectorConfig config = load_config();
if (!config.spine_executable.empty()) candidates.push_back(std::filesystem::u8path(config.spine_executable));
#ifdef _WIN32
for (const char* variable : {"ProgramFiles", "ProgramFiles(x86)", "LOCALAPPDATA"}) {
const std::string root = environment(variable);
if (root.empty()) continue;
candidates.push_back(std::filesystem::u8path(root) / "Spine" / "Spine.com");
candidates.push_back(std::filesystem::u8path(root) / "Esoteric Software" / "Spine" / "Spine.com");
candidates.push_back(std::filesystem::u8path(root) / "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()) candidates.push_back(std::filesystem::u8path(home) / "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 "";
}
} // namespace connector