Add 'strict pointer mode' to BlendFile
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 is contained in:
parent
803c38dac1
commit
087ff25c76
@ -106,6 +106,12 @@ class BlendFile:
|
||||
|
||||
log = log.getChild("BlendFile")
|
||||
|
||||
strict_pointer_mode = True
|
||||
"""Raise exceptions.SegmentationFault when dereferencing an unknown pointer.
|
||||
|
||||
Set to False to disable this exception, and to return None instead.
|
||||
"""
|
||||
|
||||
def __init__(self, path: pathlib.Path, mode="rb") -> None:
|
||||
"""Create a BlendFile instance for the blend file at the path.
|
||||
|
||||
@ -401,15 +407,21 @@ class BlendFile:
|
||||
|
||||
return abspath
|
||||
|
||||
def dereference_pointer(self, address: int) -> "BlendFileBlock":
|
||||
"""Return the pointed-to block, or raise SegmentationFault."""
|
||||
def dereference_pointer(self, address: int) -> typing.Optional["BlendFileBlock"]:
|
||||
"""Return the pointed-to block, or raise SegmentationFault.
|
||||
|
||||
When BlendFile.strict_pointer_mode is False, the exception will not be
|
||||
thrown, but None will be returned.
|
||||
"""
|
||||
|
||||
try:
|
||||
return self.block_from_addr[address]
|
||||
except KeyError:
|
||||
raise exceptions.SegmentationFault(
|
||||
"address does not exist", address
|
||||
) from None
|
||||
if self.strict_pointer_mode:
|
||||
raise exceptions.SegmentationFault(
|
||||
"address does not exist", address
|
||||
) from None
|
||||
return None
|
||||
|
||||
def struct(self, name: bytes) -> dna.Struct:
|
||||
index = self.sdna_index_from_id[name]
|
||||
@ -755,7 +767,11 @@ class BlendFileBlock:
|
||||
address = endian.read_pointer(fileobj, ps)
|
||||
if address == 0:
|
||||
continue
|
||||
yield self.bfile.dereference_pointer(address)
|
||||
dereferenced = self.bfile.dereference_pointer(address)
|
||||
if dereferenced is None:
|
||||
# This can happen when strict pointer mode is disabled.
|
||||
continue
|
||||
yield dereferenced
|
||||
|
||||
def iter_fixed_array_of_pointers(
|
||||
self, path: dna.FieldPath
|
||||
@ -786,7 +802,12 @@ class BlendFileBlock:
|
||||
if not address:
|
||||
# Fixed-size arrays contain 0-pointers.
|
||||
continue
|
||||
yield self.bfile.dereference_pointer(address)
|
||||
|
||||
dereferenced = self.bfile.dereference_pointer(address)
|
||||
if dereferenced is None:
|
||||
# This can happen when strict pointer mode is disabled.
|
||||
continue
|
||||
yield dereferenced
|
||||
|
||||
def __getitem__(self, path: dna.FieldPath):
|
||||
return self.get(path)
|
||||
|
||||
@ -512,6 +512,14 @@ class Packer:
|
||||
|
||||
# Find the same block in the newly copied file.
|
||||
block = bfile.dereference_pointer(usage.block.addr_old)
|
||||
|
||||
# Pointers can point to a non-existing data block, in which case
|
||||
# either a SegmentationFault exception is thrown, or None is
|
||||
# returned, based on the strict pointer mode set on the
|
||||
# BlendFile class. Since this block was already meant to be
|
||||
# rewritten, it was found before.
|
||||
assert block is not None
|
||||
|
||||
if usage.path_full_field is None:
|
||||
dir_field = usage.path_dir_field
|
||||
assert dir_field is not None
|
||||
|
||||
@ -217,6 +217,15 @@ class PointerTest(AbstractBlendFileTest):
|
||||
with self.assertRaises(exceptions.SegmentationFault):
|
||||
scene.get_pointer(b"ed")
|
||||
|
||||
def test_disabled_strict_pointer_mode(self):
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
ed_ptr = scene.get(b"ed")
|
||||
del self.bf.block_from_addr[ed_ptr]
|
||||
|
||||
self.bf.strict_pointer_mode = False
|
||||
dereferenced = scene.get_pointer(b"ed")
|
||||
self.assertIsNone(dereferenced)
|
||||
|
||||
def test_abs_offset(self):
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
ed = scene.get_pointer(b"ed")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user