This is mostly the same as blender/blender!140195. The header parsing code has
been updated to be able to read old and new .blend file headers.
There is a new test file which is the same as the existing `basic_file.blend`,
but saved with the new header format. A new unit test has been added to check
that this file is read correctly as well.
Pull Request: https://projects.blender.org/blender/blender-asset-tracer/pulls/92893
Add functions to interpret the data in a `BlendFileBlock` as either `bytes`
or `string`. This is used to obtain the contents of a `char*` (instead
of an embedded `char[N]` array).
---
The blendfile module within BAT supports reading data from structs, such
as the Count property on an array modifier. However, blendfile only
supports modifying structs with type "char". This patch adds support for
writing structs of more types in blendfile blocks. Now, writing is
supported for ushort, short, uint, int, float, and ulong types.
The use case that inspired this patch was an instance where a file had
several array modifiers that prevented the file from being opened in
Blender on machines without large amounts of RAM. A solution using the
blendfile module may look like:
```
from blender_asset_tracer import blendfile
from pathlib import Path
b = blendfile.open_cached(Path('flag.blend'), mode='rb+')
for block in b.blocks:
if 'ArrayModifierData' in block.__str__():
try:
print('previous:', block.get(b'count'))
block.set(b'count', 1)
print('current:', block.get(b'count'))
except KeyError:
continue
b.close()
```
This would fail with the exception
`blender_asset_tracer.blendfile.exceptions.NoWriterImplemented: Setting
type Struct(b'int') is not supported for ArrayModifierData.count`. With
this patch, the above code succeeds and the count struct can be set to
a lower number that allows the file to be opened.
This solution implements missing functionality without adding any new
interfaces. A few details are:
* When deciding what type to write to the struct, the value is inferred
from what is given by the caller. If the caller gives a Python int, the
exact type is inferred from the DNA type ID. If they give a float, a
float is written. Otherwise, the existing logic is used to determine
whether to write a string or byte sequence.
* A \_write method was added to dna\_io.py that takes a Python struct
object and a value to write a byte sequence to the file object. This
method is used by public methods appropriately named to indicate what
type they will write.
* The check for whether the caller is trying to write an unsupported
type is left in place, but it has been changed to include types which
are now supported.
* Tests have been added that provide a mock file object, call the new
methods, and confirm that the correct bytes were written.
Reviewed By: sybren
Differential Revision: https://developer.blender.org/D14374
File permissions are no longer copied when packing. This means that
read-only source files can still be packed without errors, even when
they have to be rewritten (due to changed paths).
Reviewed by: sybren
Differential Revision: https://developer.blender.org/D13128
BAT now can take advantage of the `zstandard` module to handle Blender
3.0+ compressed blend files.
If the module is not installed, the blend files cannot be opened but
GZip-compressed and uncompressed files can still be handled.
Due to issues with library overrides and unsynced pointers, it's quite
common for the Blender Animation Studio to get crashes of BAT. To avoid
these, Strict Pointer Mode is disabled when using BAT from the CLI.
Blender Cloud add-on will also get a similar update, so that there also
the Strict Pointer Mode is disabled.
Add a 'strict pointer mode' to the `BlendFile` class, which is enabled
by default. This allows users of the `BlendFile` class to decide whether
a bad pointer (i.e. one that points to a non-existing datablock) returns
`None` or raises a `SegmentationFault` exception.
This commit fixes a bunch of issues at the same time, as they are all
related to path handling:
- `pathlib.Path.resolve()` or `.absolute()` are replaced by
`bpathlib.make_absolute()`. The latter does NOT follow symlinks and does
NOT network mounts from a drive letter to UNC notation. This also has
advantages on non-Windows sytems, as it allows BAT-packing a directory
structure with symlinked files (such as a Shaman checkout).
- Better handling of drive letters, and of paths that cross drive
boundaries.
- Better testing of Windows-specific cases when running the tests on
Windows, and of POSIX-specific cases on other platforms.
Thanks to @wisaac for starting this patch in D6676.
Thanks to @jbakker for pointing out the drive letter issue. This fixes
T70655.
Not sure when/how that happens, but it happened on a lighting file of the
Spring project, when linking in a nested collection of which parts where
also linked in from other blend files.
Function declarations that have no type annotations at all will be skipped
by mypy. Adding an explicit `-> None` tells mypy to run on those functions
too.
This does introduce some not-so-nice things, like having to annotate each
`__init__` function with `-> None`. However, the benefits of having static
type checking in a complex bit of software like BAT outweigh the downsides.
Due to the huge number of BlendFileBlock objects created for packing a
production-size blend file, using slots here actually makes the
dependency tracer significantly (p<0.001) faster. In my test case the
speed improvement was 16% for a 'bam list' command.
This simplifies blend file handling, ensuring that blend files are only
opened once. Otherwise it would be harder to handle things like dependency
diamonds (libraries that are referenced via multiple paths through multiple
other libraries).
The expansion process follows pointers and library links to construct
the full set of actually-used data blocks. This set consists of all data
blocks in the initial blend file, and all *actually linked-to* data
blocks in linked blend files.
I've also removed non-recursive dependency listing.
Evaluated only once, so safe to call multiple times without producing
excessive disk I/O. Returns None instead of raising KeyError when there is
no (b'id', b'name') field.