spineparser

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 20 Imported by: 0

README

spine233-file-parser

Pure Go library for Spine files. Zero third-party dependencies.

  • detect .spine, .skel, and Spine JSON;
  • deserialize and serialize private .spine raw-DEFLATE envelopes;
  • deserialize and serialize .skel headers without changing unknown payload;
  • deserialize and serialize Spine JSON while preserving unknown fields;
  • convert complete .spine ↔ JSON data through the official Spine CLI;
  • keep JSON, binary, strings, metadata, and CLI logs in temporary directories.

.spine uses a private, version-dependent semantic schema. This library preserves its opaque binary payload losslessly. Semantic project conversion uses Spine's officially supported CLI import/export path.

Install

go get github.com/neko233-com/spine233-file-parser
import spineparser "github.com/neko233-com/spine233-file-parser"

.spine binary round trip

source, err := os.ReadFile("character.spine")
if err != nil {
	log.Fatal(err)
}

document, err := spineparser.DeserializeProject(
	source,
	spineparser.InspectOptions{},
)
if err != nil {
	log.Fatal(err)
}

fmt.Println(document.Inspection.SpineVersion)
fmt.Println(document.Inspection.Strings)

encoded, err := spineparser.SerializeProject(
	document,
	spineparser.ProjectSerializeOptions{},
)
if err != nil {
	log.Fatal(err)
}

if err := os.WriteFile("character-copy.spine", encoded, 0o644); err != nil {
	log.Fatal(err)
}

ProjectDocument also implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler:

encoded, err := document.MarshalBinary()

var decoded spineparser.ProjectDocument
err = decoded.UnmarshalBinary(encoded)

Compression bytes may differ after re-encoding, but the decompressed private payload remains byte-for-byte identical.

.skel binary round trip

document, err := spineparser.DeserializeSkeletonBinary(source)
if err != nil {
	log.Fatal(err)
}

document.Header.Width = 1920
encoded, err := spineparser.SerializeSkeletonBinary(document)

SkeletonBinaryDocument preserves the unparsed skeleton payload and rewrites only its header. It also implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.

Spine JSON round trip

document, err := spineparser.DeserializeJSON(source)
if err != nil {
	log.Fatal(err)
}

document.Bones[0].Name = "renamed-root"

encoded, err := spineparser.SerializeJSON(
	document,
	spineparser.JSONSerializeOptions{Indent: "  "},
)

Unknown root, skeleton, bone, and slot fields survive the round trip.

Complete .spine → JSON deserialization

Requires a locally installed and licensed Spine Editor.

result, err := spineparser.DeserializeProjectFile(
	context.Background(),
	"character.spine",
	spineparser.ExportOptions{
		Executable:    "D:/IDE/Spine/Spine.com",
		EditorVersion: "4.3.xx",
	},
)
if err != nil {
	log.Fatal(err)
}

for _, document := range result.Documents {
	fmt.Println(document.FileName)
	fmt.Println(len(document.Data.Bones))
	fmt.Println(document.Data.Animations)
}

ExportProject is the equivalent shorter name.

Complete JSON → .spine serialization

result, err := spineparser.SerializeProjectFile(
	context.Background(),
	document,
	"character-restored.spine",
	spineparser.ImportOptions{
		Executable:   "D:/IDE/Spine/Spine.com",
		SkeletonName: "character",
		JSON: spineparser.JSONSerializeOptions{
			Indent: "  ",
		},
	},
)
if err != nil {
	log.Fatal(err)
}

fmt.Println(result.ProjectPath)

ImportProject is the equivalent shorter name. Use JSON exported with nonessential data when the restored project must retain the maximum available editor metadata.

Temporary diagnostics

Pure file inspection:

result, err := spineparser.InspectFile(
	"character.spine",
	spineparser.InspectFileOptions{},
)
fmt.Println(result.OutputDirectory)

CLI export layout:

spine233-file-parser-<random>/
├─ character.json
└─ diagnostics/
   ├─ character.inspection.json
   ├─ character.strings.txt
   ├─ character.decoded.bin
   └─ character.spine-cli.log

CLI import diagnostics additionally contain character.import.json. Output is intentionally kept after success or failure. Set OutputDirectory for a known location or OmitDecodedBinary to skip the decompressed payload.

Set SPINE_EXECUTABLE instead of passing Executable on every call.

Resource limits

Project decompression is limited to 256 MiB by default:

document, err := spineparser.DeserializeProject(
	source,
	spineparser.InspectOptions{
		MaxUncompressedBytes: 512 * 1024 * 1024,
		MaxStrings:           20_000,
	},
)

License

MIT. Spine is a trademark of Esoteric Software LLC. Spine Editor and Spine Runtimes have their own licenses.

Documentation

Overview

Package spineparser serializes and deserializes Spine project, skeleton binary, and JSON files.

Spine Editor project schemas are private and version-dependent. The package preserves the raw-DEFLATE payload losslessly and uses the official licensed Spine CLI for complete semantic .spine to JSON conversion in either direction.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeProject

func DecodeProject(source []byte, options InspectOptions) ([]byte, error)

DecodeProject returns the decompressed private Spine project stream.

func ScanProjectStrings

func ScanProjectStrings(decoded []byte, maxStrings int) ([]string, error)

ScanProjectStrings finds Kryo ASCII-optimized diagnostic strings.

func SerializeJSON

func SerializeJSON(document *SpineJSON, options JSONSerializeOptions) ([]byte, error)

SerializeJSON writes Spine JSON while preserving unknown fields.

func SerializeProject

func SerializeProject(document *ProjectDocument, options ProjectSerializeOptions) ([]byte, error)

SerializeProject encodes an opaque project payload as a .spine raw-DEFLATE stream.

func SerializeSkeletonBinary

func SerializeSkeletonBinary(document *SkeletonBinaryDocument) ([]byte, error)

SerializeSkeletonBinary rewrites a .skel header and appends the untouched payload.

Types

type Bone

type Bone struct {
	Name   string         `json:"name"`
	Parent string         `json:"parent,omitempty"`
	Data   map[string]any `json:"-"`
}

Bone is an exported skeleton bone. Data retains all version-specific fields.

func (Bone) MarshalJSON

func (b Bone) MarshalJSON() ([]byte, error)

func (*Bone) UnmarshalJSON

func (b *Bone) UnmarshalJSON(data []byte) error

type DiagnosticArtifacts

type DiagnosticArtifacts struct {
	Directory         string `json:"directory"`
	InspectionPath    string `json:"inspectionPath"`
	StringsPath       string `json:"stringsPath"`
	DecodedBinaryPath string `json:"decodedBinaryPath,omitempty"`
	CLILogPath        string `json:"cliLogPath,omitempty"`
}

DiagnosticArtifacts are human-readable and binary troubleshooting files.

type ErrorCode

type ErrorCode string

ErrorCode identifies a stable parser error category.

const (
	ErrInvalidInput   ErrorCode = "INVALID_INPUT"
	ErrInvalidProject ErrorCode = "INVALID_PROJECT"
	ErrInvalidJSON    ErrorCode = "INVALID_JSON"
	ErrInvalidSkel    ErrorCode = "INVALID_SKEL"
	ErrLimitExceeded  ErrorCode = "LIMIT_EXCEEDED"
)

type ExportOptions

type ExportOptions struct {
	InspectFileOptions
	Executable     string
	ExportSettings string
	EditorVersion  string
	Timeout        time.Duration
}

ExportOptions controls official Spine CLI conversion.

type ExportResult

type ExportResult struct {
	Inspection      ProjectInspection   `json:"inspection"`
	Documents       []ExportedDocument  `json:"documents"`
	OutputDirectory string              `json:"outputDirectory"`
	Artifacts       DiagnosticArtifacts `json:"artifacts"`
	Stdout          string              `json:"stdout"`
	Stderr          string              `json:"stderr"`
}

ExportResult contains complete parsed data and kept diagnostic paths.

func DeserializeProjectFile

func DeserializeProjectFile(
	ctx context.Context,
	projectPath string,
	options ExportOptions,
) (*ExportResult, error)

DeserializeProjectFile performs semantic .spine to JSON conversion through the official Spine CLI.

func ExportProject

func ExportProject(ctx context.Context, projectPath string, options ExportOptions) (*ExportResult, error)

ExportProject uses the licensed official Spine CLI for complete Pro data.

type ExportedDocument

type ExportedDocument struct {
	FileName string     `json:"fileName"`
	Path     string     `json:"path"`
	Data     *SpineJSON `json:"data"`
}

ExportedDocument is one skeleton JSON output.

type FileKind

type FileKind string

FileKind describes a recognized Spine file representation.

const (
	FileProject        FileKind = "project"
	FileSkeletonJSON   FileKind = "skeleton-json"
	FileSkeletonBinary FileKind = "skeleton-binary"
	FileUnknown        FileKind = "unknown"
)

func Detect

func Detect(source []byte) FileKind

Detect identifies project, exported JSON, and exported binary files.

type ImportArtifacts

type ImportArtifacts struct {
	DiagnosticArtifacts
	InputJSONPath string `json:"inputJsonPath"`
}

ImportArtifacts are kept inputs, metadata, decoded data, and CLI logs.

type ImportOptions

type ImportOptions struct {
	InspectFileOptions
	Executable    string
	EditorVersion string
	SkeletonName  string
	Timeout       time.Duration
	JSON          JSONSerializeOptions
}

ImportOptions controls semantic Spine JSON to .spine serialization.

type ImportResult

type ImportResult struct {
	ProjectPath     string            `json:"projectPath"`
	OutputDirectory string            `json:"outputDirectory"`
	Inspection      ProjectInspection `json:"inspection"`
	Artifacts       ImportArtifacts   `json:"artifacts"`
	Stdout          string            `json:"stdout"`
	Stderr          string            `json:"stderr"`
}

ImportResult contains the serialized .spine project and diagnostics.

func ImportProject

func ImportProject(
	ctx context.Context,
	document *SpineJSON,
	projectPath string,
	options ImportOptions,
) (*ImportResult, error)

ImportProject serializes semantic Spine JSON to a .spine project through the official licensed Spine CLI.

func SerializeProjectFile

func SerializeProjectFile(
	ctx context.Context,
	document *SpineJSON,
	projectPath string,
	options ImportOptions,
) (*ImportResult, error)

SerializeProjectFile performs semantic JSON to .spine conversion through the official Spine CLI.

type InspectFileOptions

type InspectFileOptions struct {
	InspectOptions
	OutputDirectory   string
	OmitDecodedBinary bool
}

InspectFileOptions controls filesystem diagnostics.

type InspectFileResult

type InspectFileResult struct {
	Inspection      ProjectInspection   `json:"inspection"`
	OutputDirectory string              `json:"outputDirectory"`
	Artifacts       DiagnosticArtifacts `json:"artifacts"`
}

InspectFileResult is a project inspection plus kept diagnostic files.

func InspectFile

func InspectFile(projectPath string, options InspectFileOptions) (*InspectFileResult, error)

InspectFile reads a .spine file and keeps diagnostics in a unique temp directory.

type InspectOptions

type InspectOptions struct {
	MaxUncompressedBytes int64
	MaxStrings           int
}

InspectOptions controls project resource limits.

type JSONSerializeOptions

type JSONSerializeOptions struct {
	// Indent enables pretty printing, for example "  ". Empty means compact.
	Indent string
}

JSONSerializeOptions controls Spine JSON output.

type ParseError

type ParseError struct {
	Code  ErrorCode
	Msg   string
	Cause error
}

ParseError is returned for invalid or unsafe Spine input.

func (*ParseError) Error

func (e *ParseError) Error() string

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

type ProjectDocument

type ProjectDocument struct {
	Inspection ProjectInspection `json:"inspection"`
	Payload    []byte            `json:"payload"`
}

ProjectDocument is the lossless decompressed payload of a private .spine file.

The payload is opaque because Spine's semantic project schema is private. Use ExportProject and ImportProject for semantic JSON conversion.

func DeserializeProject

func DeserializeProject(source []byte, options InspectOptions) (*ProjectDocument, error)

DeserializeProject decodes a .spine envelope without losing private payload bytes.

func (*ProjectDocument) MarshalBinary

func (d *ProjectDocument) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler.

func (*ProjectDocument) UnmarshalBinary

func (d *ProjectDocument) UnmarshalBinary(source []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler.

type ProjectInspection

type ProjectInspection struct {
	Kind              FileKind `json:"kind"`
	Compression       string   `json:"compression"`
	CompressedBytes   int      `json:"compressedBytes"`
	UncompressedBytes int      `json:"uncompressedBytes"`
	SpineVersion      string   `json:"spineVersion,omitempty"`
	Strings           []string `json:"strings"`
}

ProjectInspection contains schema-independent .spine metadata.

func InspectProject

func InspectProject(source []byte, options InspectOptions) (ProjectInspection, error)

InspectProject parses the raw-DEFLATE envelope and diagnostic metadata.

type ProjectSerializeOptions

type ProjectSerializeOptions struct {
	// CompressionLevel accepts compress/flate levels. Nil uses DefaultCompression.
	CompressionLevel *int
}

ProjectSerializeOptions controls the raw-DEFLATE encoder.

type SkeletonBinaryDocument

type SkeletonBinaryDocument struct {
	Format  SkeletonBinaryFormat     `json:"format"`
	Header  SkeletonBinaryInspection `json:"header"`
	Payload []byte                   `json:"payload"`
}

SkeletonBinaryDocument retains the parsed header and untouched binary payload.

func DeserializeSkeletonBinary

func DeserializeSkeletonBinary(source []byte) (*SkeletonBinaryDocument, error)

DeserializeSkeletonBinary parses a .skel header and retains the remaining payload.

func (*SkeletonBinaryDocument) MarshalBinary

func (d *SkeletonBinaryDocument) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler.

func (*SkeletonBinaryDocument) UnmarshalBinary

func (d *SkeletonBinaryDocument) UnmarshalBinary(source []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler.

type SkeletonBinaryFormat

type SkeletonBinaryFormat string

SkeletonBinaryFormat identifies an exported .skel header generation.

const (
	SkeletonBinaryModern SkeletonBinaryFormat = "modern"
	SkeletonBinaryLegacy SkeletonBinaryFormat = "legacy"
)

type SkeletonBinaryInspection

type SkeletonBinaryInspection struct {
	Kind           FileKind `json:"kind"`
	Hash           string   `json:"hash,omitempty"`
	SpineVersion   string   `json:"spineVersion"`
	X              float32  `json:"x"`
	Y              float32  `json:"y"`
	Width          float32  `json:"width"`
	Height         float32  `json:"height"`
	ReferenceScale *float32 `json:"referenceScale,omitempty"`
	Nonessential   bool     `json:"nonessential"`
}

SkeletonBinaryInspection contains an exported .skel header.

func InspectSkeletonBinary

func InspectSkeletonBinary(source []byte) (SkeletonBinaryInspection, error)

InspectSkeletonBinary parses current and legacy exported .skel headers.

type SkeletonInfo

type SkeletonInfo struct {
	Hash   string                     `json:"hash,omitempty"`
	Spine  string                     `json:"spine,omitempty"`
	X      float64                    `json:"x,omitempty"`
	Y      float64                    `json:"y,omitempty"`
	Width  float64                    `json:"width,omitempty"`
	Height float64                    `json:"height,omitempty"`
	FPS    float64                    `json:"fps,omitempty"`
	Images string                     `json:"images,omitempty"`
	Audio  string                     `json:"audio,omitempty"`
	Raw    map[string]json.RawMessage `json:"-"`
}

SkeletonInfo is the exported Spine JSON metadata block.

func (SkeletonInfo) MarshalJSON

func (s SkeletonInfo) MarshalJSON() ([]byte, error)

func (*SkeletonInfo) UnmarshalJSON

func (s *SkeletonInfo) UnmarshalJSON(data []byte) error

type Slot

type Slot struct {
	Name string         `json:"name"`
	Bone string         `json:"bone"`
	Data map[string]any `json:"-"`
}

Slot is an exported skeleton slot. Data retains all version-specific fields.

func (Slot) MarshalJSON

func (s Slot) MarshalJSON() ([]byte, error)

func (*Slot) UnmarshalJSON

func (s *Slot) UnmarshalJSON(data []byte) error

type SpineJSON

type SpineJSON struct {
	Skeleton   *SkeletonInfo              `json:"skeleton,omitempty"`
	Bones      []Bone                     `json:"bones,omitempty"`
	Slots      []Slot                     `json:"slots,omitempty"`
	Skins      json.RawMessage            `json:"skins,omitempty"`
	Events     map[string]json.RawMessage `json:"events,omitempty"`
	Animations map[string]json.RawMessage `json:"animations,omitempty"`
	Raw        map[string]json.RawMessage `json:"-"`
}

SpineJSON is typed where stable and keeps full raw JSON for version-specific data.

func DeserializeJSON

func DeserializeJSON(source []byte) (*SpineJSON, error)

DeserializeJSON parses Spine JSON and preserves unknown fields.

func ParseJSON

func ParseJSON(source []byte) (*SpineJSON, error)

ParseJSON parses standard Spine skeleton JSON.

func (SpineJSON) MarshalJSON

func (s SpineJSON) MarshalJSON() ([]byte, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL