导出数据转spine
This commit is contained in:
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
|
||||
Reference in new issue
Block a user