Blender is an open source suite for 3D modelling, sculpting,
animation, compositing, rendering, preparation of assets for its own
game engine and exporting to others, etc. .blend
is its own binary
format that saves whole state of suite: current scene, animations,
all software settings, extensions, etc.
Internally, .blend format is a hybrid semi-self-descriptive
format. On top level, it contains a simple header and a sequence of
file blocks, which more or less follow typical TLV
pattern. Pre-last
block would be a structure with code DNA1
, which is a essentially
a machine-readable schema of all other structures used in this file.
This page hosts a formal specification of .blend file format of Blender using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
use strict;
use warnings;
use IO::KaitaiStruct 0.009_000;
use Encode;
########################################################################
package BlenderBlend;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
our $PTR_SIZE_BITS_64 = 45;
our $PTR_SIZE_BITS_32 = 95;
our $ENDIAN_BE = 86;
our $ENDIAN_LE = 118;
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{hdr} = BlenderBlend::Header->new($self->{_io}, $self, $self->{_root});
$self->{blocks} = ();
while (!$self->{_io}->is_eof()) {
push @{$self->{blocks}}, BlenderBlend::FileBlock->new($self->{_io}, $self, $self->{_root});
}
}
sub sdna_structs {
my ($self) = @_;
return $self->{sdna_structs} if ($self->{sdna_structs});
$self->{sdna_structs} = @{$self->blocks()}[(scalar(@{$self->blocks()}) - 2)]->body()->structs();
return $self->{sdna_structs};
}
sub hdr {
my ($self) = @_;
return $self->{hdr};
}
sub blocks {
my ($self) = @_;
return $self->{blocks};
}
########################################################################
package BlenderBlend::DnaStruct;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{idx_type} = $self->{_io}->read_u2le();
$self->{num_fields} = $self->{_io}->read_u2le();
$self->{fields} = ();
my $n_fields = $self->num_fields();
for (my $i = 0; $i < $n_fields; $i++) {
push @{$self->{fields}}, BlenderBlend::DnaField->new($self->{_io}, $self, $self->{_root});
}
}
sub type {
my ($self) = @_;
return $self->{type} if ($self->{type});
$self->{type} = @{$self->_parent()->types()}[$self->idx_type()];
return $self->{type};
}
sub idx_type {
my ($self) = @_;
return $self->{idx_type};
}
sub num_fields {
my ($self) = @_;
return $self->{num_fields};
}
sub fields {
my ($self) = @_;
return $self->{fields};
}
########################################################################
package BlenderBlend::FileBlock;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{code} = Encode::decode("ASCII", $self->{_io}->read_bytes(4));
$self->{len_body} = $self->{_io}->read_u4le();
$self->{mem_addr} = $self->{_io}->read_bytes($self->_root()->hdr()->psize());
$self->{sdna_index} = $self->{_io}->read_u4le();
$self->{count} = $self->{_io}->read_u4le();
my $_on = $self->code();
if ($_on eq "DNA1") {
$self->{_raw_body} = $self->{_io}->read_bytes($self->len_body());
my $io__raw_body = IO::KaitaiStruct::Stream->new($self->{_raw_body});
$self->{body} = BlenderBlend::Dna1Body->new($io__raw_body, $self, $self->{_root});
}
else {
$self->{body} = $self->{_io}->read_bytes($self->len_body());
}
}
sub sdna_struct {
my ($self) = @_;
return $self->{sdna_struct} if ($self->{sdna_struct});
if ($self->sdna_index() != 0) {
$self->{sdna_struct} = @{$self->_root()->sdna_structs()}[$self->sdna_index()];
}
return $self->{sdna_struct};
}
sub code {
my ($self) = @_;
return $self->{code};
}
sub len_body {
my ($self) = @_;
return $self->{len_body};
}
sub mem_addr {
my ($self) = @_;
return $self->{mem_addr};
}
sub sdna_index {
my ($self) = @_;
return $self->{sdna_index};
}
sub count {
my ($self) = @_;
return $self->{count};
}
sub body {
my ($self) = @_;
return $self->{body};
}
sub _raw_body {
my ($self) = @_;
return $self->{_raw_body};
}
########################################################################
package BlenderBlend::Dna1Body;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{id} = $self->{_io}->read_bytes(4);
$self->{name_magic} = $self->{_io}->read_bytes(4);
$self->{num_names} = $self->{_io}->read_u4le();
$self->{names} = ();
my $n_names = $self->num_names();
for (my $i = 0; $i < $n_names; $i++) {
push @{$self->{names}}, Encode::decode("UTF-8", $self->{_io}->read_bytes_term(0, 0, 1, 1));
}
$self->{padding_1} = $self->{_io}->read_bytes(((4 - $self->_io()->pos()) % 4));
$self->{type_magic} = $self->{_io}->read_bytes(4);
$self->{num_types} = $self->{_io}->read_u4le();
$self->{types} = ();
my $n_types = $self->num_types();
for (my $i = 0; $i < $n_types; $i++) {
push @{$self->{types}}, Encode::decode("UTF-8", $self->{_io}->read_bytes_term(0, 0, 1, 1));
}
$self->{padding_2} = $self->{_io}->read_bytes(((4 - $self->_io()->pos()) % 4));
$self->{tlen_magic} = $self->{_io}->read_bytes(4);
$self->{lengths} = ();
my $n_lengths = $self->num_types();
for (my $i = 0; $i < $n_lengths; $i++) {
push @{$self->{lengths}}, $self->{_io}->read_u2le();
}
$self->{padding_3} = $self->{_io}->read_bytes(((4 - $self->_io()->pos()) % 4));
$self->{strc_magic} = $self->{_io}->read_bytes(4);
$self->{num_structs} = $self->{_io}->read_u4le();
$self->{structs} = ();
my $n_structs = $self->num_structs();
for (my $i = 0; $i < $n_structs; $i++) {
push @{$self->{structs}}, BlenderBlend::DnaStruct->new($self->{_io}, $self, $self->{_root});
}
}
sub id {
my ($self) = @_;
return $self->{id};
}
sub name_magic {
my ($self) = @_;
return $self->{name_magic};
}
sub num_names {
my ($self) = @_;
return $self->{num_names};
}
sub names {
my ($self) = @_;
return $self->{names};
}
sub padding_1 {
my ($self) = @_;
return $self->{padding_1};
}
sub type_magic {
my ($self) = @_;
return $self->{type_magic};
}
sub num_types {
my ($self) = @_;
return $self->{num_types};
}
sub types {
my ($self) = @_;
return $self->{types};
}
sub padding_2 {
my ($self) = @_;
return $self->{padding_2};
}
sub tlen_magic {
my ($self) = @_;
return $self->{tlen_magic};
}
sub lengths {
my ($self) = @_;
return $self->{lengths};
}
sub padding_3 {
my ($self) = @_;
return $self->{padding_3};
}
sub strc_magic {
my ($self) = @_;
return $self->{strc_magic};
}
sub num_structs {
my ($self) = @_;
return $self->{num_structs};
}
sub structs {
my ($self) = @_;
return $self->{structs};
}
########################################################################
package BlenderBlend::Header;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{magic} = $self->{_io}->read_bytes(7);
$self->{ptr_size_id} = $self->{_io}->read_u1();
$self->{endian} = $self->{_io}->read_u1();
$self->{version} = Encode::decode("ASCII", $self->{_io}->read_bytes(3));
}
sub psize {
my ($self) = @_;
return $self->{psize} if ($self->{psize});
$self->{psize} = ($self->ptr_size_id() == $BlenderBlend::PTR_SIZE_BITS_64 ? 8 : 4);
return $self->{psize};
}
sub magic {
my ($self) = @_;
return $self->{magic};
}
sub ptr_size_id {
my ($self) = @_;
return $self->{ptr_size_id};
}
sub endian {
my ($self) = @_;
return $self->{endian};
}
sub version {
my ($self) = @_;
return $self->{version};
}
########################################################################
package BlenderBlend::DnaField;
our @ISA = 'IO::KaitaiStruct::Struct';
sub from_file {
my ($class, $filename) = @_;
my $fd;
open($fd, '<', $filename) or return undef;
binmode($fd);
return new($class, IO::KaitaiStruct::Stream->new($fd));
}
sub new {
my ($class, $_io, $_parent, $_root) = @_;
my $self = IO::KaitaiStruct::Struct->new($_io);
bless $self, $class;
$self->{_parent} = $_parent;
$self->{_root} = $_root || $self;;
$self->_read();
return $self;
}
sub _read {
my ($self) = @_;
$self->{idx_type} = $self->{_io}->read_u2le();
$self->{idx_name} = $self->{_io}->read_u2le();
}
sub type {
my ($self) = @_;
return $self->{type} if ($self->{type});
$self->{type} = @{$self->_parent()->_parent()->types()}[$self->idx_type()];
return $self->{type};
}
sub name {
my ($self) = @_;
return $self->{name} if ($self->{name});
$self->{name} = @{$self->_parent()->_parent()->names()}[$self->idx_name()];
return $self->{name};
}
sub idx_type {
my ($self) = @_;
return $self->{idx_type};
}
sub idx_name {
my ($self) = @_;
return $self->{idx_name};
}
1;