Start of dependency tracer work.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import logging
|
||||
import pathlib
|
||||
import typing
|
||||
|
||||
from blender_asset_tracer import bpathlib, blendfile
|
||||
from . import result, blocks
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
codes_to_skip = {
|
||||
b'LI', # Library blocks we handle after the blend file itself.
|
||||
b'ID', b'WM', b'SN', # These blocks never have external assets.
|
||||
}
|
||||
|
||||
|
||||
def deps(bfilepath: pathlib.Path) -> typing.Iterator[result.BlockUsage]:
|
||||
log.info('Tracing %s', bfilepath)
|
||||
|
||||
with blendfile.BlendFile(bfilepath) as bfile:
|
||||
for block in asset_holding_blocks(bfile):
|
||||
yield from blocks.from_block(block)
|
||||
|
||||
# TODO: handle library blocks for recursion.
|
||||
|
||||
|
||||
def asset_holding_blocks(bfile: blendfile.BlendFile) -> typing.Iterator[blendfile.BlendFileBlock]:
|
||||
for block in bfile.blocks:
|
||||
assert isinstance(block, blendfile.BlendFileBlock)
|
||||
code = block.code
|
||||
|
||||
# The longer codes are either arbitrary data or data blocks that
|
||||
# don't refer to external assets. The former data blocks will be
|
||||
# visited when we hit the two-letter datablocks that use them.
|
||||
if len(code) > 2 or code in codes_to_skip:
|
||||
continue
|
||||
|
||||
yield block
|
||||
@@ -0,0 +1,49 @@
|
||||
import logging
|
||||
import sys
|
||||
import typing
|
||||
|
||||
from blender_asset_tracer import blendfile
|
||||
from . import result, cdefs
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_warned_about_types = set()
|
||||
|
||||
|
||||
class NoReaderImplemented(NotImplementedError):
|
||||
"""There is no reader implementation for a specific block code."""
|
||||
|
||||
def __init__(self, message: str, code: bytes):
|
||||
super().__init__(message)
|
||||
self.code = code
|
||||
|
||||
|
||||
def from_block(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
|
||||
assert block.code != b'DATA'
|
||||
|
||||
module = sys.modules[__name__]
|
||||
funcname = '_from_block_' + block.code.decode().lower()
|
||||
try:
|
||||
block_reader = getattr(module, funcname)
|
||||
except AttributeError:
|
||||
if block.code not in _warned_about_types:
|
||||
log.warning('No reader implemented for block type %r', block.code.decode())
|
||||
_warned_about_types.add(block.code)
|
||||
return
|
||||
|
||||
yield from block_reader(block)
|
||||
|
||||
|
||||
def _from_block_im(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
|
||||
# old files miss this
|
||||
image_source = block.get(b'source', default=cdefs.IMA_SRC_FILE)
|
||||
if image_source not in {cdefs.IMA_SRC_FILE, cdefs.IMA_SRC_SEQUENCE, cdefs.IMA_SRC_MOVIE}:
|
||||
return
|
||||
if block[b'packedfile']:
|
||||
return
|
||||
|
||||
pathname, field = block.get(b'name', return_field=True)
|
||||
|
||||
# TODO: the receiver should inspect the 'source' property too, and if set
|
||||
# to cdefs.IMA_SRC_SEQUENCE yield the entire sequence of files.
|
||||
yield result.BlockUsage(block, field, pathname)
|
||||
@@ -0,0 +1,47 @@
|
||||
# ***** 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 *****
|
||||
|
||||
"""Constants defined in C."""
|
||||
|
||||
# DNA_sequence_types.h (Sequence.type)
|
||||
SEQ_TYPE_IMAGE = 0
|
||||
SEQ_TYPE_META = 1
|
||||
SEQ_TYPE_SCENE = 2
|
||||
SEQ_TYPE_MOVIE = 3
|
||||
SEQ_TYPE_SOUND_RAM = 4
|
||||
SEQ_TYPE_SOUND_HD = 5
|
||||
SEQ_TYPE_MOVIECLIP = 6
|
||||
SEQ_TYPE_MASK = 7
|
||||
SEQ_TYPE_EFFECT = 8
|
||||
|
||||
IMA_SRC_FILE = 1
|
||||
IMA_SRC_SEQUENCE = 2
|
||||
IMA_SRC_MOVIE = 3
|
||||
|
||||
# DNA_modifier_types.h
|
||||
eModifierType_MeshCache = 46
|
||||
|
||||
# DNA_particle_types.h
|
||||
PART_DRAW_OB = 7
|
||||
PART_DRAW_GR = 8
|
||||
|
||||
# DNA_object_types.h
|
||||
# Object.transflag
|
||||
OB_DUPLIGROUP = 1 << 8
|
||||
|
||||
CACHE_LIBRARY_SOURCE_CACHE = 1
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
class BlockUsage:
|
||||
"""Represents the use of an asset by a data block.
|
||||
|
||||
:ivar block:
|
||||
:type block: blendfile.BlendFileBlock
|
||||
:ivar field:
|
||||
:type field: dna.Field
|
||||
:ivar asset_path:
|
||||
:type asset_path: bpathlib.BlendPath
|
||||
"""
|
||||
|
||||
def __init__(self, block, field, asset_path):
|
||||
self.block_idname = block[b'id', b'name']
|
||||
|
||||
from blender_asset_tracer import blendfile, bpathlib
|
||||
from blender_asset_tracer.blendfile import dna
|
||||
|
||||
assert isinstance(block, blendfile.BlendFileBlock)
|
||||
assert isinstance(field, dna.Field), 'field should be dna.Field, not %r' % type(field)
|
||||
assert isinstance(asset_path, (bytes, bpathlib.BlendPath)), \
|
||||
'asset_path should be BlendPath, not %r' % type(asset_path)
|
||||
|
||||
if isinstance(asset_path, bytes):
|
||||
asset_path = bpathlib.BlendPath(asset_path)
|
||||
|
||||
self.block = block
|
||||
self.field = field
|
||||
self.asset_path = asset_path
|
||||
|
||||
def __repr__(self):
|
||||
return '<BlockUsage name=%r type=%r field=%r asset=%r>' % (
|
||||
self.block_idname, self.block.dna_type_name,
|
||||
self.field.name.name_full.decode(), self.asset_path)
|
||||
Reference in New Issue
Block a user