diff --git a/doc/blender_file_format/BlendFileDnaExporter_25.py b/doc/blender_file_format/BlendFileDnaExporter_25.py new file mode 100755 index 00000000000..77656f43ae5 --- /dev/null +++ b/doc/blender_file_format/BlendFileDnaExporter_25.py @@ -0,0 +1,477 @@ +#! /usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** + +###################################################### +# +# Name: +# dna.py +# +# Description: +# Creates a browsable DNA output to HTML. +# +# Author: +# Jeroen Bakker +# +# Version: +# v0.1 (12-05-2009) - migration of original source code to python. +# Added code to support blender 2.5 branch +# v0.2 (25-05-2009) - integrated with BlendFileReader.py +# +# Input: +# blender build executable +# +# Output: +# dna.html +# dna.css (will only be created when not existing) +# +# Startup: +# ./blender -P BlendFileDnaExporter.py +# +# Process: +# 1: write blend file with SDNA info +# 2: read blend header from blend file +# 3: seek DNA1 file-block +# 4: read dna record from blend file +# 5: close and eventually delete temp blend file +# 6: export dna to html and css +# 7: quit blender +# +###################################################### + +import struct +import sys +import getopt # command line arguments handling +from string import Template # strings completion + + +# logs +import logging +log = logging.getLogger("BlendFileDnaExporter") + +if '--dna-debug' in sys.argv: + logging.basicConfig(level=logging.DEBUG) +else: + logging.basicConfig(level=logging.INFO) + + +class DNACatalogHTML: + ''' + DNACatalog is a catalog of all information in the DNA1 file-block + ''' + + def __init__(self, catalog, bpy_module = None): + self.Catalog = catalog + self.bpy = bpy_module + + def WriteToHTML(self, handle): + + dna_html_template = """ + + +
+ + +reference | +structure | +type | +name | +offset | +size | +
---|
In this article I will describe the + blend-file-format with a request to tool-makers to support blend-file. + +
+First I'll describe how Blender works with blend-files. You'll notice + why the blend-file-format is not that well documented, as from +Blender's perspective this is not needed. +We look at the global file-structure of a blend-file (the file-header +and file-blocks). +After this is explained, we go deeper to the core of the blend-file, the + DNA-structures. They hold the blue-prints of the blend-file and the key + asset of understanding blend-files. +When that's done we can use these DNA-structures to read information +from elsewhere in the blend-file. + +
++In this article we'll be using the default blend-file from Blender 2.54, + with the goal to read the output resolution from the Scene. +The article is written to be programming language independent and I've +setup a web-site for support. +
+ + ++Loading and saving in Blender is very fast and Blender is known to +have excellent downward and upward compatibility. Ton Roosendaal +demonstrated that in December 2008 by loading a 1.0 blend-file using +Blender 2.48a [ref: http://www.blendernation.com/2008/12/01/blender-dna-rna-and-backward-compatibility/]. +
+ ++Saving complex scenes in Blender is done within seconds. Blender +achieves this by saving data in memory to disk without any +transformations or translations. Blender only adds file-block-headers to + this data. A file-block-header contains clues on how to interpret the +data. After the data, all internally Blender structures are stored. +These structures will act as blue-prints when Blender loads the file. +Blend-files can be different when stored on different hardware platforms + or Blender releases. There is no effort taken to make blend-files +binary the same. Blender creates the blend-files in this manner since +release 1.0. Backward and upwards compatibility is not implemented when +saving the file, this is done during loading. +
+ ++When Blender loads a blend-file, the DNA-structures are read first. +Blender creates a catalog of these DNA-structures. Blender uses this +catalog together with the data in the file, the internal Blender +structures of the Blender release you're using and a lot of +transformation and translation logic to implement the backward and +upward compatibility. In the source code of blender there is actually +logic which can transform and translate every structure used by a +Blender release to the one of the release you're using [ref: http://download.blender.org/source/blender-2.48a.tar.gz + blender/blenloader/intern/readfile.c lines +4946-7960]. The more difference between releases the more logic is +executed. +
+ ++The blend-file-format is not well documented, as it does not differ from + internally used structures and the file can really explain itself. +
+ + ++This section explains how the global file-structure can be read. +
+ +File.blend
+ +File-header
File-block
+Header
Data
File-block
File-block
File-block 'Structure DNA'
+Header ('DNA1')
Data ('SDNA')
+Names ('NAME')
+Types ('TYPE')
+Lengths ('TLEN')
+Structures ('STRC')
+File-Block 'ENDB'
+The first 12 bytes of every blend-file is the file-header. The +file-header has information on Blender (version-number) and the PC the +blend-file was saved on (pointer-size and endianness). This is required +as all data inside the blend-file is ordered in that way, because no +translation or transformation is done during saving. +The next table describes the information in the file-header. +
+ +reference | +structure | +type | +offset | +size |
---|---|---|---|---|
identifier | +char[7] | +File identifier (always 'BLENDER') | +0 | +7 |
pointer-size | +char | +Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes or 32 bit and '-' means 8 bytes or 64 bits. | +7 | +1 |
endianness | +char | +Type of byte ordering used; 'v' means little endian and 'V' means big endian. | +8 | +1 |
version-number | +char[3] | +Version of Blender the file was created in; '254' means version 2.54 | +9 | +3 |
+Endianness addresses the way values are ordered in a sequence of bytes(see the example below): +
+ ++Nowadays, little-endian is the most commonly used. +
+ + ++Endianess Example +
+
+Writing the integer 0x4A3B2C1Dh
, will be ordered:
+
0x4Ah
, 0x3Bh
, 0x2Ch
, 0x1Dh
0x1Dh
, 0x2Ch
, 0x3Bh
, 0x4Ah
+Blender supports little-endian and big-endian.
+This means that when the endianness
+is different between the blend-file and the PC your using, Blender changes it to the byte ordering
+of your PC.
+
+File-header Example +
+ +
+This hex-dump describes a file-header created with blender
2.54.0
on little-endian
hardware with a 32 bits
pointer length.
+ pointer-size version-number
+ | |
+0000 0000: [42 4C 45 4E 44 45 52] [5F] [76] [32 35 34] BLENDER_v254
+ | |
+ identifier endianness
+
+File-blocks contain a "file-block header" and "file-block data". +
+ ++The file-block-header describes: +
+ ++As we can see below, depending on the pointer-size stored in the file-header, a file-block-header +can be 20 or 24 bytes long, hence it is always aligned at 4 bytes. +
+ +reference | +structure | +type | +offset | +size |
---|---|---|---|---|
code | +char[4] | +File-block identifier | +0 | +4 |
size | +integer | +Total length of the data after the file-block-header | +4 | +4 |
old memory address | +void* | +Memory address the structure was located when written to disk | +8 | +pointer-size (4/8) |
SDNA index | +integer | +Index of the SDNA structure | +8+pointer-size | +4 |
count | +integer | +Number of structure located in this file-block | +12+pointer-size | +4 |
+The above table describes how a file-block-header is structured: +
+ +Code
describes different types of file-blocks. The code determines with what logic the data must be read. Size
contains the total length of data after the file-block-header.
+After the data a new file-block starts. The last file-block in the file
+has code 'ENDB'.Old memory address
contains the memory address when the structure
+was last stored. When loading the file the structures can be placed on
+different memory addresses. Blender updates pointers to these structures
+ to the new memory addresses.SDNA index
contains the index in the DNA structures to be used when
+reading this file-block-data. Count
tells how many elements of the specific SDNA structure can be found in the data.+Example +
+
+This hex-dump describes a File-block (= File-block header + File-block data) created with blender
2.54
on little-endian
hardware with a 32 bits
pointer length.
+ file-block
+ identifier='SC' data size=1404 old pointer SDNA index=150
+ | | | |
+0000 4420: [53 43 00 00] [7C 05 00 00] [68 34 FB 0B] [96 00 00 00] SC.. `... ./.. ....
+0000 4430: [01 00 00 00] [xx xx xx xx xx xx xx xx xx xx xx xx .... xxxx xxxx xxxx
+ | |
+ count=1 file-block data (next 1404 bytes)
+
+
'SC'+0x00h
identifies that it is a Scene. +Before we can interpret the data of this file-block we first have to read the DNA structures in the file. +The section "Structure DNA" will show how to do that. +
++Structure DNA is stored in a file-block with code 'DNA1'. It can be just before the 'ENDB' file-block. +
+ +
+The 'DNA1' file-block contains all internal structures of the Blender release the
+file was created in.
+These structure can be described as C-structures: they can hold fields, arrays and
+pointers to other structures, just like a normal C-structure.
+
+
+struct SceneRenderLayer {
+ struct SceneRenderLayer *next, *prev;
+ char name[32];
+ struct Material *mat_override;
+ struct Group *light_override;
+ unsigned int lay;
+ unsigned int lay_zmask;
+ int layflag;
+ int pad;
+ int passflag;
+ int pass_xor;
+};
+
+
+For example,a blend-file created with Blender 2.54 the 'DNA1' file-block is 57796 bytes long and contains 398 structures. +
+ ++The DNA1 file-block header follows the same rules of any other file-block, see the example below. +
+ + ++Example +
+
+This hex-dump describes the file-block 'DNA1' header created with blender
2.54.0
on little-endian
hardware with a 32 bits
pointer length.
+ (file-block
+ identifier='DNA1') data size=57796 old pointer SDNA index=0
+ | | | |
+0004 B060 [44 4E 41 31] [C4 E1 00 00] [C8 00 84 0B] [00 00 00 00] DNA1............
+0004 B070 [01 00 00 00] [53 44 4E 41 4E 41 4D 45 CB 0B 00 00 ....SDNANAME....
+ | |
+ count=1 'DNA1' file-block data (next 57796 bytes)
+
+
+The next section describes how this information is ordered in the data of the 'DNA1' file-block. +
+ +repeat condition | +name | +type | +length | +description | |
---|---|---|---|---|---|
+ | + | identifier | +char[4] | +4 | +'SDNA' |
+ | + | name identifier | +char[4] | +4 | +'NAME' |
+ | + | #names | +integer | +4 | +Number of names follows |
for(#names) | ++ | name | +char[] | +? | +Zero terminating string of name, also contains pointer and simple array definitions (e.g. '*vertex[3]\0') |
+ | + | type identifier | +char[4] | +4 | +'TYPE' this field is aligned at 4 bytes |
+ | + | #types | +integer | +4 | +Number of types follows |
for(#types) | ++ | type | +char[] | +? | +Zero terminating string of type (e.g. 'int\0') |
+ | + | length identifier | +char[4] | +4 | +'TLEN' this field is aligned at 4 bytes |
for(#types) | ++ | length | +short | +2 | +Length in bytes of type (e.g. 4) |
+ | + | structure identifier | +char[4] | +4 | +'STRC' this field is aligned at 4 bytes |
+ | + | #structures | +integer | +4 | +Number of structures follows |
for(#structures) | ++ | structure type | +short | +2 | +Index in types containing the name of the structure |
.. | ++ | #fields | +short | +2 | +Number of fields in this structure |
.. | +for(#field) | +field type | +short | +2 | +Index in type |
for end | +for end | +field name | +short | +2 | +Index in name |
+As you can see, the structures are stored in 4 arrays: names, types, +lengths and structures. Every structure also contains an array of +fields. A field is the combination of a type and a name. From this +information a catalog of all structures can be constructed. +The names are stored as how a C-developer defines them. This means that +the name also defines pointers and arrays. +(When a name starts with '*' it is used as a pointer. when the name +contains for example '[3]' it is used as a array of 3 long.) +In the types you'll find simple-types (like: 'integer', 'char', +'float'), but also complex-types like 'Scene' and 'MetaBall'. +'TLEN' part describes the length of the types. A 'char' is 1 byte, an +'integer' is 4 bytes and a 'Scene' is 1376 bytes long. +
+ ++Note +
++All identifiers, are arrays of 4 chars, hence they are all aligned at 4 bytes. +
++Example +
+
+Created with blender
2.54.0
on little-endian
hardware with a 32 bits
pointer length.
+
+The first names are: *next, *prev, *data, *first, *last, x, y, xmin, xmax, ymin, ymax, *pointer, group, val, val2, type, subtype, flag, name[32], ...
+ file-block-data identifier='SDNA' array-id='NAME' number of names=3019
+ | | |
+0004 B070 01 00 00 00 [53 44 4E 41][4E 41 4D 45] [CB 0B 00 00] ....SDNANAME....
+0004 B080 [2A 6E 65 78 74 00][2A 70 72 65 76 00] [2A 64 61 74 *next.*prev.*dat
+ | | |
+ '*next\0' '*prev\0' '*dat'
+ ....
+ .... (3019 names)
+
+
+Note +
++While reading the DNA you'll will come across some strange +names like '(*doit)()'. These are method pointers and Blender updates +them to the correct methods. +
+
+The first types are: char, uchar, short, ushort, int, long, ulong, float, double, void, Link, LinkData, ListBase, vec2s, vec2f, ...
+ array-id='TYPE'
+ |
+0005 2440 6F 6C 64 5B 34 5D 5B 34 5D 00 00 00 [54 59 50 45] old[4][4]...TYPE
+0005 2450 [C9 01 00 00] [63 68 61 72 00] [75 63 68 61 72 00][73 ....char.uchar.s
+ | | | |
+ number of types=457 'char\0' 'uchar\0' 's'
+ ....
+ .... (457 types)
+
+
+ char uchar ushort short
+ array-id length length length length
+ 'TLEN' 1 1 2 2
+0005 3AA0 45 00 00 00 [54 4C 45 4E] [01 00] [01 00] [02 00] [02 00] E...TLEN........
+ ....
+0005 3AC0 [08 00] [04 00] [08 00] [10 00] [10 00] [14 00] [4C 00] [34 00] ............L.4.
+ 8 4 8
+ ListBase vec2s vec2f ... etc
+ length len length
+ ....
+ .... (457 lengths, same as number of types)
+
+
+ array-id='STRC'
+ |
+0005 3E30 40 00 38 00 60 00 00 00 00 00 00 00 [53 54 52 43] @.8.`.......STRC
+0005 3E40 [8E 01 00 00] [0A 00] [02 00] [0A 00] [00 00] [0A 00] [01 00] ................
+ 398 10 2 10 0 10 0
+ number of index fields index index index index
+ structures in types in types in names in types in names
+ ' '----------------' '-----------------' '
+ ' field 0 field 1 '
+ '--------------------------------------------------------'
+ structure 0
+ ....
+ .... (398 structures, each one describeing own type, and type/name for each field)
+
+
+The DNA structures inside a Blender 2.48 blend-file can be found at http://www.atmind.nl/blender/blender-sdna.html. + +If we understand the DNA part of the file it is now possible to read +information from other parts file-blocks. The next section will tell us +how. +
+ +
+Let us look at the file-block header we have seen earlier:
+
'SC'+0x00h
+Now note that: +
+We can map the Scene structure on the data of the file-blocks.
+But before we can do that, we have to flatten the Scene-structure.
+
+struct Scene {
+ ID id; // 52 bytes long (ID is different a structure)
+ AnimData *adt; // 4 bytes long (pointer to an AnimData structure)
+ Object *camera; // 4 bytes long (pointer to an Object structure)
+ World *world; // 4 bytes long (pointer to an Object structure)
+ ...
+ float cursor[3]; // 12 bytes long (array of 3 floats)
+ ...
+};
+
+
+The first field in the Scene-structure is of type 'ID' with the name 'id'.
+Inside the list of DNA structures there is a structure defined for type 'ID' (structure index 17).
+
+struct ID {
+ void *next, *prev;
+ struct ID *newid;
+ struct Library *lib;
+ char name[24];
+ short us;
+ short flag;
+ int icon_id;
+ IDProperty *properties;
+};
+
+
+The first field in this structure has type 'void' and name '*next'.
+Looking in the structure list there is no structure defined for type 'void': it is a simple type and therefore the data should be read.
+The name '*next' describes a pointer.
+As we see, the first 4 bytes of the data can be mapped to 'id.next'.
+
+Using this method we'll map a structure to its data. If we want to
+read a specific field we know at which offset in the data it is located
+and how much space it takes.
+The next table shows the output of this flattening process for some
+parts of the Scene-structure. Not all rows are described in the table as
+ there is a lot of information in a Scene-structure.
+
reference | +structure | +type | name | +offset | size | +description |
---|---|---|---|---|---|---|
id.next | ID | +void | *next | +0 | +4 | +Refers to the next scene |
id.prev | ID | +void | *prev | +4 | +4 | +Refers to the previous scene |
id.newid | ID | +ID | *newid | +8 | +4 | +|
id.lib | ID | +Library | *lib | +12 | +4 | +|
id.name | ID | +char | name[24] | +16 | +24 | +'SC'+the name of the scene as displayed in Blender |
id.us | ID | +short | us | +40 | +2 | +|
id.flag | ID | +short | flag | 42 | 2 | +|
id.icon_id | ID | +int | icon_id | 44 | +4 | +|
id.properties | ID | +IDProperty | *properties | +48 | +4 | +|
adt | Scene | AnimData | +*adt | +52 | +4 | +|
camera | Scene | +Object | +*camera | +56 | +4 | +Pointer to the current camera |
world | Scene | +World | +*world | +60 | +4 | +Pointer to the current world |
Skipped rows | ||||||
r.xsch | RenderData + | short | xsch | 382 | 2 | +X-resolution of the output when rendered at 100% |
r.ysch | RenderData + | short | ysch | 384 | 2 | +Y-resolution of the output when rendered at 100% |
r.xparts | RenderData + | short | xparts | 386 | 2 | +Number of x-part used by the renderer |
r.yparts | RenderData + | short | yparts | 388 | 2 | +Number of x-part used by the renderer |
Skipped rows | ||||||
gpd | Scene | bGPdata | *gpd | 1376 | 4 | +|
physics_settings.gravity | PhysicsSettings + | float | gravity[3] | 1380 | 12 | +|
physics_settings.flag | PhysicsSettings + | int | flag | 1392 | 4 | +|
physics_settings.quick_cache_step | PhysicsSettings + | int | quick_cache_step | 1396 | 4 | +|
physics_settings.rt | PhysicsSettings + | int | rt | 1400 | 4 | +
+We can now read the X and Y resolution of the Scene: +
+Note +
++An array of chars can mean 2 things. The field contains readable +text or it contains an array of flags (not humanly readable). +
++Note +
++A file-block containing a list refers to the DNA structure and has a count larger +than 1. For example Vertexes and Faces are stored in this way. +
+