导出数据转spine

This commit is contained in:
tianmo committed 2026-09-07 21:29:15 +08:00
1 parent d258d4f435
commit 3a7c0b4092
65 files changed
+6000

No files matched your search

@@ -0,0 +1,141 @@
#include "scan/SpineDataVersionDetector.hpp"
#include <cstdint>
#include <fstream>
#include <regex>
namespace dts {
namespace {
constexpr std::size_t maxJsonProbeBytes = 1024 * 1024;
constexpr std::size_t maxBinaryStringBytes = 4096;
std::optional<std::uint32_t> readVarint(std::istream& input) {
std::uint32_t value = 0;
for (int shift = 0; shift < 35; shift += 7) {
const int byte = input.get();
if (byte == std::char_traits<char>::eof()) {
return std::nullopt;
}
value |= static_cast<std::uint32_t>(byte & 0x7f) << shift;
if ((byte & 0x80) == 0) {
return value;
}
}
return std::nullopt;
}
std::optional<std::string> readBinaryString(std::istream& input) {
const auto encodedLength = readVarint(input);
if (!encodedLength || *encodedLength == 0) {
return std::nullopt;
}
const std::size_t length = *encodedLength - 1;
if (length > maxBinaryStringBytes) {
return std::nullopt;
}
std::string value(length, '\0');
if (length > 0 && !input.read(value.data(), static_cast<std::streamsize>(length))) {
return std::nullopt;
}
return value;
}
VersionDetectionResult finish(SkeletonDataFormat format, const SpineVersion& version) {
VersionDetectionResult result;
result.format = format;
result.version = version;
result.status = version.isSupported()
? VersionDetectionStatus::Detected
: VersionDetectionStatus::UnsupportedVersion;
result.message = version.isSupported()
? "Spine data version detected."
: "Spine data version is outside the supported range.";
return result;
}
VersionDetectionResult inspectJson(std::ifstream& input) {
std::string text(maxJsonProbeBytes, '\0');
input.read(text.data(), static_cast<std::streamsize>(text.size()));
text.resize(static_cast<std::size_t>(input.gcount()));
static const std::regex bones(R"("bones"\s*:)");
static const std::regex version(
R"regex("spine"\s*:\s*"(\d+\.\d+(?:\.\d+)?(?:-[^"]+)?)")regex");
std::smatch match;
if (!std::regex_search(text, bones) || !std::regex_search(text, match, version)) {
return {SkeletonDataFormat::Json, VersionDetectionStatus::InvalidData, std::nullopt,
"JSON does not contain the minimum Spine skeleton schema."};
}
const auto parsed = SpineVersion::parse(match[1].str());
if (!parsed) {
return {SkeletonDataFormat::Json, VersionDetectionStatus::InvalidData, std::nullopt,
"Spine JSON version is invalid."};
}
return finish(SkeletonDataFormat::Json, *parsed);
}
VersionDetectionResult inspectBinary(std::ifstream& input) {
const auto hash = readBinaryString(input);
const auto versionText = readBinaryString(input);
if (hash && versionText) {
if (const auto version = SpineVersion::parse(*versionText))
return finish(SkeletonDataFormat::Binary, *version);
}
input.clear();
input.seekg(0);
std::string header(256, '\0');
input.read(header.data(), static_cast<std::streamsize>(header.size()));
header.resize(static_cast<std::size_t>(input.gcount()));
static const std::regex embeddedVersion(
R"regex((3\.8(?:\.\d+)?(?:-[A-Za-z0-9.-]+)?|4\.[0-3](?:\.\d+)?(?:-[A-Za-z0-9.-]+)?))regex");
std::smatch match;
if (std::regex_search(header, match, embeddedVersion)) {
if (const auto version = SpineVersion::parse(match[1].str()))
return finish(SkeletonDataFormat::Binary, *version);
}
return {SkeletonDataFormat::Binary, VersionDetectionStatus::InvalidData, std::nullopt,
"SKEL header does not contain a valid Spine version."};
}
} // namespace
VersionDetectionResult SpineDataVersionDetector::inspect(const std::filesystem::path& path) const {
std::ifstream input(path, std::ios::binary);
if (!input) {
return {SkeletonDataFormat::Unknown, VersionDetectionStatus::FileMissing, std::nullopt,
"Skeleton data file could not be opened."};
}
char first = '\0';
while (input.get(first)) {
if (first != ' ' && first != '\t' && first != '\r' && first != '\n') {
break;
}
}
input.clear();
input.seekg(0);
return first == '{' ? inspectJson(input) : inspectBinary(input);
}
std::string toString(SkeletonDataFormat format) {
switch (format) {
case SkeletonDataFormat::Json: return "json";
case SkeletonDataFormat::Binary: return "skel";
case SkeletonDataFormat::Unknown: return "unknown";
}
return "unknown";
}
std::string toString(VersionDetectionStatus status) {
switch (status) {
case VersionDetectionStatus::Detected: return "detected";
case VersionDetectionStatus::FileMissing: return "file-missing";
case VersionDetectionStatus::InvalidData: return "invalid-data";
case VersionDetectionStatus::UnsupportedVersion: return "unsupported-version";
}
return "unknown";
}
} // namespace dts
@@ -0,0 +1,36 @@
#pragma once
#include "domain/SpineVersion.hpp"
#include <filesystem>
#include <optional>
#include <string>
namespace dts {
enum class SkeletonDataFormat { Json, Binary, Unknown };
enum class VersionDetectionStatus {
Detected,
FileMissing,
InvalidData,
UnsupportedVersion
};
struct VersionDetectionResult {
SkeletonDataFormat format = SkeletonDataFormat::Unknown;
VersionDetectionStatus status = VersionDetectionStatus::InvalidData;
std::optional<SpineVersion> version;
std::string message;
};
class SpineDataVersionDetector {
public:
[[nodiscard]] VersionDetectionResult inspect(const std::filesystem::path& path) const;
};
[[nodiscard]] std::string toString(SkeletonDataFormat format);
[[nodiscard]] std::string toString(VersionDetectionStatus status);
} // namespace dts