Welcome to mirror list, hosted at ThFree Co, Russian Federation.

BlendFileDnaExporter_25.py « blender_file_format « doc - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 21260ea7ebc1d74c93cb29b439c9e4243c913123 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
#!/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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
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 = """
            <!DOCTYPE html PUBLIC -//W3C//DTD HTML 4.01 Transitional//EN http://www.w3.org/TR/html4/loose.dtd>
            <html>
            <head>
                <link rel="stylesheet" type="text/css" href="dna.css" media="screen, print" />
                <meta http-equiv="Content-Type" content="text/html"; charset="ISO-8859-1" />
                <title>The mystery of the blend</title>
            </head>
            <body>
                <div class=title>
                    Blender ${version}<br/>
                    Internal SDNA structures
                </div>
                Architecture: ${bitness} ${endianness}<br/>
                Build revision: <a href="https://svn.blender.org/svnroot/bf-blender/!svn/bc/${revision}/trunk/">${revision}</a><br/>
                File format reference: <a href="mystery_of_the_blend.html">The mystery of the blend</a> by Jeroen Bakker<br/>
                <h1>Index of blender structures</h1>
                <ul class=multicolumn>
                    ${structs_list}
                </ul>
                ${structs_content}
            </body>
            </html>"""

        header = self.Catalog.Header
        bpy = self.bpy

        # ${version} and ${revision}
        if bpy:
            version = '.'.join(map(str, bpy.app.version))
            revision = bpy.app.build_hash
        else:
            version = str(header.Version)
            revision = 'Unknown'

        # ${bitness}
        if header.PointerSize == 8:
            bitness = '64 bit'
        else:
            bitness = '32 bit'

        # ${endianness}
        if header.LittleEndianness:
            endianess = 'Little endianness'
        else:
            endianess = 'Big endianness'

        # ${structs_list}
        log.debug("Creating structs index")
        structs_list = ''
        list_item = '<li class="multicolumn">({0}) <a href="#{1}">{1}</a></li>\n'
        structureIndex = 0
        for structure in self.Catalog.Structs:
            structs_list += list_item.format(structureIndex, structure.Type.Name)
            structureIndex += 1

        # ${structs_content}
        log.debug("Creating structs content")
        structs_content = ''
        for structure in self.Catalog.Structs:
            log.debug(structure.Type.Name)
            structs_content += self.Structure(structure)

        d = dict(
            version=version,
            revision=revision,
            bitness=bitness,
            endianness=endianess,
            structs_list=structs_list,
            structs_content=structs_content
        )

        dna_html = Template(dna_html_template).substitute(d)
        dna_html = self.format(dna_html)
        handle.write(dna_html)

    def Structure(self, structure):
        struct_table_template = """
            <table><a name="${struct_name}"></a>
                <caption><a href="#${struct_name}">${struct_name}</a></caption>
                <thead>
                    <tr>
                        <th>reference</th>
                        <th>structure</th>
                        <th>type</th>
                        <th>name</th>
                        <th>offset</th>
                        <th>size</th>
                    </tr>
                </thead>
                <tbody>
                ${fields}
                </tbody>
            </table>
            <label>Total size: ${size} bytes</label><br/>
            <label>(<a href="#top">top</a>)</label><br/>"""

        d = dict(
            struct_name=structure.Type.Name,
            fields=self.StructureFields(structure, None, 0),
            size=str(structure.Type.Size)
        )

        struct_table = Template(struct_table_template).substitute(d)
        return struct_table

    def StructureFields(self, structure, parentReference, offset):
        fields = ''
        for field in structure.Fields:
            fields += self.StructureField(field, structure, parentReference, offset)
            offset += field.Size(self.Catalog.Header)
        return fields

    def StructureField(self, field, structure, parentReference, offset):
        structure_field_template = """
            <tr>
                <td>${reference}</td>
                <td>${struct}</td>
                <td>${type}</td>
                <td>${name}</td>
                <td>${offset}</td>
                <td>${size}</td>
            </tr>"""

        if field.Type.Structure is None or field.Name.IsPointer():

            # ${reference}
            reference = field.Name.AsReference(parentReference)

            # ${struct}
            if parentReference is not None:
                struct = '<a href="#{0}">{0}</a>'.format(structure.Type.Name)
            else:
                struct = structure.Type.Name

            # ${type}
            type = field.Type.Name

            # ${name}
            name = field.Name.Name

            # ${offset}
            # offset already set

            # ${size}
            size = field.Size(self.Catalog.Header)

            d = dict(
                reference=reference,
                struct=struct,
                type=type,
                name=name,
                offset=offset,
                size=size
            )

            structure_field = Template(structure_field_template).substitute(d)

        elif field.Type.Structure is not None:
            reference = field.Name.AsReference(parentReference)
            structure_field = self.StructureFields(field.Type.Structure, reference, offset)

        return structure_field

    def indent(self, input, dent, startswith=''):
        output = ''
        if dent < 0:
            for line in input.split('\n'):
                dent = abs(dent)
                output += line[dent:] + '\n'   # unindent of a desired amount
        elif dent == 0:
            for line in input.split('\n'):
                output += line.lstrip() + '\n'  # remove indentation completely
        elif dent > 0:
            for line in input.split('\n'):
                output += ' ' * dent + line + '\n'
        return output

    def format(self, input):
        diff = {
            '\n<!DOCTYPE': '<!DOCTYPE',
            '\n</ul>': '</ul>',
            '<a name': '\n<a name',
            '<tr>\n': '<tr>',
            '<tr>': '  <tr>',
            '</th>\n': '</th>',
            '</td>\n': '</td>',
            '<tbody>\n': '<tbody>'
        }
        output = self.indent(input, 0)
        for key, value in diff.items():
            output = output.replace(key, value)
        return output

    def WriteToCSS(self, handle):
        '''
        Write the Cascading stylesheet template to the handle
        It is expected that the handle is a Filehandle
        '''
        css = """
            @CHARSET "ISO-8859-1";

            body {
                font-family: verdana;
                font-size: small;
            }

            div.title {
                font-size: large;
                text-align: center;
            }

            h1 {
                page-break-before: always;
            }

            h1, h2 {
                background-color: #D3D3D3;
                color:#404040;
                margin-right: 3%;
                padding-left: 40px;
            }

            h1:hover{
                background-color: #EBEBEB;
            }

            h3 {
                padding-left: 40px;
            }

            table {
                border-width: 1px;
                border-style: solid;
                border-color: #000000;
                border-collapse: collapse;
                width: 94%;
                margin: 20px 3% 10px;
            }

            caption {
                margin-bottom: 5px;
            }

            th {
                background-color: #000000;
                color:#ffffff;
                padding-left:5px;
                padding-right:5px;
            }

            tr {
            }

            td {
                border-width: 1px;
                border-style: solid;
                border-color: #a0a0a0;
                padding-left:5px;
                padding-right:5px;
            }

            label {
                float:right;
                margin-right: 3%;
            }

            ul.multicolumn {
                list-style:none;
                float:left;
                padding-right:0px;
                margin-right:0px;
            }

            li.multicolumn {
                float:left;
                width:200px;
                margin-right:0px;
            }

            a {
                color:#a000a0;
                text-decoration:none;
            }

            a:hover {
                color:#a000a0;
                text-decoration:underline;
            }
            """

        css = self.indent(css, 0)

        handle.write(css)


def usage():
    print("\nUsage: \n\tblender2.5 --background -noaudio --python BlendFileDnaExporter_25.py [-- [options]]")
    print("Options:")
    print("\t--dna-keep-blend:      doesn't delete the produced blend file DNA export to html")
    print("\t--dna-debug:           sets the logging level to DEBUG (lots of additional info)")
    print("\t--dna-versioned        saves version information in the html and blend filenames")
    print("\t--dna-overwrite-css    overwrite dna.css, useful when modifying css in the script")
    print("Examples:")
    print("\tdefault:       % blender2.5 --background -noaudio --python BlendFileDnaExporter_25.py")
    print("\twith options:  % blender2.5 --background -noaudio --python BlendFileDnaExporter_25.py -- --dna-keep-blend --dna-debug\n")


######################################################
# Main
######################################################

def main():

    import os, os.path

    try:
        bpy = __import__('bpy')

        # Files
        if '--dna-versioned' in sys.argv:
            blender_version = '_'.join(map(str, bpy.app.version))
            filename = 'dna-{0}-{1}_endian-{2}-{3}'.format(sys.arch, sys.byteorder, blender_version, bpy.app.build_hash)
        else:
            filename = 'dna'
        dir = os.path.dirname(__file__)
        Path_Blend = os.path.join(dir, filename + '.blend')  # temporary blend file
        Path_HTML = os.path.join(dir, filename + '.html')  # output html file
        Path_CSS = os.path.join(dir, 'dna.css')           # output css file

        # create a blend file for dna parsing
        if not os.path.exists(Path_Blend):
            log.info("1: write temp blend file with SDNA info")
            log.info("   saving to: " + Path_Blend)
            try:
                bpy.ops.wm.save_as_mainfile(filepath=Path_Blend, copy=True, compress=False)
            except:
                log.error("Filename {0} does not exist and can't be created... quitting".format(Path_Blend))
                return
        else:
            log.info("1: found blend file with SDNA info")
            log.info("   " + Path_Blend)

        # read blend header from blend file
        log.info("2: read file:")

        if not dir in sys.path:
            sys.path.append(dir)
        import BlendFileReader

        handle = BlendFileReader.openBlendFile(Path_Blend)
        blendfile = BlendFileReader.BlendFile(handle)
        catalog = DNACatalogHTML(blendfile.Catalog, bpy)

        # close temp file
        handle.close()

        # deleting or not?
        if '--dna-keep-blend' in sys.argv:
            # keep the blend, useful for studying hexdumps
            log.info("5: closing blend file:")
            log.info("   {0}".format(Path_Blend))
        else:
            # delete the blend
            log.info("5: close and delete temp blend:")
            log.info("   {0}".format(Path_Blend))
            os.remove(Path_Blend)

        # export dna to xhtml
        log.info("6: export sdna to xhtml file: %r" % Path_HTML)
        handleHTML = open(Path_HTML, "w")
        catalog.WriteToHTML(handleHTML)
        handleHTML.close()

        # only write the css when doesn't exist or at explicit request
        if not os.path.exists(Path_CSS) or '--dna-overwrite-css' in sys.argv:
            handleCSS = open(Path_CSS, "w")
            catalog.WriteToCSS(handleCSS)
            handleCSS.close()

        # quit blender
        if not bpy.app.background:
            log.info("7: quit blender")
            bpy.ops.wm.exit_blender()

    except ImportError:
        log.warning("  skipping, not running in Blender")
        usage()
        sys.exit(2)


if __name__ == '__main__':
    main()