Skip to content

Instantly share code, notes, and snippets.

@dertom95
Last active October 7, 2023 15:16
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dertom95/ceb071e196a5d571fe62 to your computer and use it in GitHub Desktop.
Save dertom95/ceb071e196a5d571fe62 to your computer and use it in GitHub Desktop.
Blender: Access DNA-Datablocks from python
# ##### 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 LICENSE BLOCK #####
# <pep8 compliant>
"""
Experimental module (for developers only), exposes DNA data via python
uses no blender/python modules, pure python + autogenerated ctypes api.
code by ideasman42
(EDIT dertom: added bpy and setup to put _dna property to some types for instant dna-access)
Exposes:
* pydna.main: main database access.
* pydna.types: a module of all DNA ctypes structures.
* Utility method: CAST(dna_type)
* Utility method for ListBase: ITER(dna_type)
* Show structs: printStruct() or printStructs(["Image","Object"])
Example:
import pydna
pydna.setup() # adds to _dna property to some types (see setup-def)
ob = bpy.data.objects[0]
print ("Name:" + ob._dna.id.name)
"""
import ctypes
import struct
import bpy
def _api():
def is_ctypes_subclass(other, main_class):
while other:
if other is main_class:
return True
other = getattr(other, "_type_", None)
return False
def is_ctypes_initialized(other):
_other = other
while other:
if hasattr(other, "_fields_"):
return True
other = getattr(other, "_type_", None)
print(_other, "NOT INIT")
return False
def is_ctypes_base(other):
while type(getattr(other, "_type_", "")) != str:
other = other._type_
return other
class MixIn:
pass
blend_cdll = ctypes.CDLL("")
blend_lib = ctypes.LibraryLoader("")
def blend_parse_dna():
# from dna.c
sdna_str_pt = blend_cdll.DNAstr
sdna_len_pt = blend_cdll.DNAlen
# cast
sdna_len_pt = ctypes.c_void_p.from_address(ctypes.addressof(sdna_len_pt))
sdna_len = ctypes.c_int.from_address(sdna_len_pt.value)
blend_sdna = ctypes.string_at(sdna_str_pt, sdna_len)
ofs = 0
assert(blend_sdna[ofs:ofs + 8] == b'SDNANAME')
ofs += 8
sdna_names_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
ofs += 4
blend_sdna_names = blend_sdna[ofs:].split(b'\0', sdna_names_len)
blend_sdna_remainder = blend_sdna_names.pop(-1) # last item is not a name.
ofs = len(blend_sdna) - len(blend_sdna_remainder)
ofs = (ofs + 3) & ~3
assert(blend_sdna[ofs:ofs + 4] == b'TYPE')
ofs += 4
sdna_types_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
ofs += 4
blend_sdna_types = blend_sdna[ofs:].split(b'\0', sdna_types_len)
blend_sdna_remainder = blend_sdna_types.pop(-1)
ofs = len(blend_sdna) - len(blend_sdna_remainder)
ofs = (ofs + 3) & ~3
assert(blend_sdna[ofs:ofs + 4] == b'TLEN')
ofs += 4
blend_sdna_typelens = struct.unpack("%dh" % sdna_types_len, blend_sdna[ofs:ofs + (sdna_types_len * 2)])
ofs += sdna_types_len * 2
ofs = (ofs + 3) & ~3
# array of pointers to short arrays
assert(blend_sdna[ofs:ofs + 4] == b'STRC')
ofs += 4
sdna_structs_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
ofs += 4
blend_sdna_structs = []
for i in range(sdna_structs_len):
struct_type, struct_tot = struct.unpack("hh", blend_sdna[ofs:ofs + 4])
ofs += 4
struct_type_name_pairs = struct.unpack("%dh" % struct_tot * 2, blend_sdna[ofs:ofs + (struct_tot * 4)])
# convert into pairs, easier to understand (type, name)
struct_type_name_pairs = [(struct_type_name_pairs[j], struct_type_name_pairs[j + 1]) for j in range(0, struct_tot * 2, 2)]
blend_sdna_structs.append((struct_type, struct_type_name_pairs))
ofs += struct_tot * 4
# ofs = (ofs + 1) & ~1
return blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs
def create_dna_structs(blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs):
# create all subclasses of ctypes.Structure
ctypes_structs = {name: type(name.decode(), (ctypes.Structure, MixIn), {}) for name in blend_sdna_types}
ctypes_basic = {b"float": ctypes.c_float, b"double": ctypes.c_double, b"int": ctypes.c_int, b"short": ctypes.c_short, b"char": ctypes.c_char, b"void": ctypes.c_void_p}
ctypes_fields = {}
# collect fields
for struct_id, struct_type_name_pairs in blend_sdna_structs:
struct_name = blend_sdna_types[struct_id]
ctype_struct = ctypes_structs[struct_name]
fields = []
for stype, sname in struct_type_name_pairs:
name_string = blend_sdna_names[sname]
type_string = blend_sdna_types[stype]
type_py = ctypes_basic.get(type_string)
if type_py is None:
type_py = ctypes_structs.get(type_string)
# todo, these might need to be changed
name_string = name_string.replace(b"(", b"")
name_string = name_string.replace(b")", b"")
# * First parse the pointer *
pointer_count = 0
while name_string[0] == 42: # '*'
pointer_count += 1
name_string = name_string[1:]
# alredy a pointer
if type_py is ctypes.c_void_p:
pointer_count -= 1
elif type_py is ctypes.c_char and pointer_count == 1:
type_py = ctypes.c_char_p
pointer_count = 0
if pointer_count < 0:
Exception("error parsing pointer")
for i in range(pointer_count):
type_py = ctypes.POINTER(type_py)
# * Now parse the array [] *
if b'[' in name_string:
name_string = name_string.replace(b'[', b' ')
name_string = name_string.replace(b']', b' ')
name_split = name_string.split()
name_string = name_split[0]
for array_dim in reversed(name_split[1:]):
type_py = type_py * int(array_dim)
fields.append((name_string.decode(), type_py))
ctypes_fields[struct_name] = fields
# apply fields all in one go!
for struct_id, struct_type_name_pairs in blend_sdna_structs:
struct_name = blend_sdna_types[struct_id]
ctype_struct = ctypes_structs[struct_name]
try:
ctype_struct._fields_ = ctypes_fields[struct_name]
except:
'''
print("Error:", struct_name)
import traceback
traceback.print_exc()
'''
pass
# print(fields)
# test fields
#for struct_id, struct_type_name_pairs in blend_sdna_structs:
# ctype_struct = ctypes_structs[blend_sdna_types[struct_id]]
# if blend_sdna_typelens[struct_id] != ctypes.sizeof(ctype_struct):
# print("Size Mismatch for %r blender:%d vs python:%d" % (blend_sdna_types[struct_id], blend_sdna_typelens[struct_id], ctypes.sizeof(ctype_struct)))
return ctypes_structs
def decorate_api(struct_dict):
# * Decotate the api *
# listbase iter
type_cast_lb = struct_dict[b'ListBase']
type_cast_link = struct_dict[b'Link']
def list_base_iter(self, type_name):
if type(type_name) == str:
type_cast = struct_dict[type_name.encode('ASCII')]
else:
# allow passing types direcly
type_cast = type_name
# empty listbase
if self.first is None:
ret = None
else:
try:
ret = type_cast_link.from_address(ctypes.addressof(self.first))
except TypeError:
ret = type_cast_link.from_address(self.first)
while ret is not None:
return_value = type_cast.from_address(ctypes.addressof(ret))
try:
next_pointer = getattr(ret.next, "contents")
except:
next_pointer = None
if next_pointer:
ret = type_cast_link.from_address(ctypes.addressof(next_pointer))
else:
ret = None
yield return_value
struct_dict[b'ListBase'].ITER = list_base_iter
def CAST(self, to):
if type(type_name) == str:
type_cast = struct_dict[to.encode('ASCII')]
else:
type_cast = to
return type_cast.from_address(ctypes.addressof(self))
MixIn.CAST = CAST
blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs = blend_parse_dna()
struct_dict = create_dna_structs(blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs)
def printStructs(showStructs=None):
for struct_id, struct_type_name_pairs in blend_sdna_structs:
struct_name = blend_sdna_types[struct_id].decode()
if not showStructs or struct_name in showStructs:
print("typedef struct %s {" % struct_name)
for stype, sname in struct_type_name_pairs:
print(" %s %s;" % (blend_sdna_types[stype].decode(), blend_sdna_names[sname].decode()))
print("} %s;" % struct_name)
decorate_api(struct_dict) # not essential but useful
# manually wrap Main
Main = type("Main", (ctypes.Structure, ), {})
_lb = struct_dict[b"ListBase"]
Main._fields_ = [("next", ctypes.POINTER(Main)),
("prev", ctypes.POINTER(Main)),
("name", ctypes.c_char * 240),
("versionfile", ctypes.c_short),
("subversionfile", ctypes.c_short),
("minversionfile", ctypes.c_short),
("minsubversionfile", ctypes.c_short),
("revision", ctypes.c_int),
("curlib", ctypes.POINTER(struct_dict[b"Library"])),
("scene", _lb),
("library", _lb),
("object", _lb),
("mesh", _lb),
("curve", _lb),
("mball", _lb),
("mat", _lb),
("tex", _lb),
("image", _lb),
("latt", _lb),
("lamp", _lb),
("camera", _lb),
("ipo", _lb),
("key", _lb),
("world", _lb),
("screen", _lb),
("script", _lb),
("vfont", _lb),
("text", _lb),
("sound", _lb),
("group", _lb),
("armature", _lb),
("action", _lb),
("nodetree", _lb),
("brush", _lb),
("particle", _lb),
("wm", _lb),
("gpencil", _lb),
("movieclip", _lb),
("mask",_lb),
("linestyle",_lb)
]
del _lb
# import bpy
# main = Main.from_address(bpy.data.as_pointer())
# main is the first pointer in Global.
main_address = ctypes.POINTER(ctypes.c_void_p).from_address(ctypes.addressof(blend_cdll.G)).contents.value
main = Main.from_address(main_address)
return main, struct_dict, printStructs
main, _struct_dict, printStructs = _api()
# types dict
types = type(ctypes)("pydna.types")
types.__dict__.update({s.__name__: s for s in _struct_dict.values()})
# --- Image extension component monkey-class into RNA, pydna.py above from r34522.
def setup():
# only wrap enough to read image buffer
ImBuf = type("ImBuf", (ctypes.Structure, ), {})
ImBuf._fields_ = [("next", ctypes.POINTER(ImBuf)),
("prev", ctypes.POINTER(ImBuf)),
("x", ctypes.c_int),
("y", ctypes.c_int),
("depth", ctypes.c_ubyte),
("channels", ctypes.c_int),
("flags", ctypes.c_int),
("mall", ctypes.c_int),
("rect", ctypes.POINTER(ctypes.c_ubyte)),
("crect", ctypes.POINTER(ctypes.c_ubyte)),
("rect_float", ctypes.POINTER(ctypes.c_float)),
]
# pretend we're external
pydna = type(ctypes)("pydna")
pydna.__dict__.update(globals())
def _image_get_buffer(image):
image_dna = pydna.types.Image.from_address(image.as_pointer())
for ibuf in image_dna.ibufs.ITER(ImBuf):
return ibuf.x, ibuf.y, ibuf.rect, ibuf.rect_float
return 0, 0, (), ()
def _get_dna(obj):
_dna = pydna.types.Object.from_address(obj.as_pointer())
return _dna
import bpy
bpy.types.Image.buffer_raw = property(_image_get_buffer)
bpy.types.Action._dna = property(_get_dna)
bpy.types.Armature._dna = property(_get_dna)
bpy.types.Brush._dna = property(_get_dna)
bpy.types.Camera._dna = property(_get_dna)
bpy.types.Curve._dna = property(_get_dna)
#bpy.types.Group._dna = property(_get_dna)
bpy.types.Image._dna = property(_get_dna)
#bpy.types.Lamp._dna = property(_get_dna)
bpy.types.Lattice._dna = property(_get_dna)
bpy.types.Library._dna = property(_get_dna)
bpy.types.Mask._dna = property(_get_dna)
bpy.types.MovieClip._dna = property(_get_dna)
bpy.types.NodeTree._dna = property(_get_dna)
bpy.types.Palette._dna = property(_get_dna)
bpy.types.Particle._dna = property(_get_dna)
bpy.types.Screen._dna = property(_get_dna)
bpy.types.Speaker._dna = property(_get_dna)
bpy.types.World._dna = property(_get_dna)
bpy.types.Object._dna = property(_get_dna)
bpy.types.NodeTree._dna = property(_get_dna)
bpy.types.Scene._dna = property(_get_dna)
bpy.types.Sound._dna = property(_get_dna)
bpy.types.Texture._dna = property(_get_dna)
bpy.types.Text._dna = property(_get_dna)
bpy.types.Material._dna = property(_get_dna)
bpy.types.Mesh._dna = property(_get_dna)
@darvon123
Copy link

when I run your script, it produces this error:

Python: Traceback (most recent call last):
  File "C:\Users\beast\3D Objects\all node.blend\pydna.py", line 342, in <module>
  File "C:\Users\beast\3D Objects\all node.blend\pydna.py", line 76, in _api
  File "C:\Program Files\Blender Foundation\Blender 3.4\3.4\python\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 87] The parameter is incorrect

@dertom95
Copy link
Author

This gist is quite old and the structure of blender changed quite a bit. I tested it as a script in the Editor and only had to remove bpy.types.Group and Lamp and it still worked. Was actually quite surprised it did :D

# ##### 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 LICENSE BLOCK #####

# <pep8 compliant>

"""
Experimental module (for developers only), exposes DNA data via python
uses no blender/python modules, pure python + autogenerated ctypes api.
code by ideasman42
(EDIT dertom: added bpy and setup to put _dna property to some types for instant dna-access)

Exposes:
 * pydna.main: main database access.
 * pydna.types: a module of all DNA ctypes structures.

 * Utility method: CAST(dna_type)
 * Utility method for ListBase: ITER(dna_type)
 * Show structs: printStruct() or printStructs(["Image","Object"])
 
Example:
 import pydna
 
 pydna.setup() # adds to _dna property to some types (see setup-def)
 
 ob = bpy.data.objects[0]
 print ("Name:" + ob._dna.id.name)


"""

import ctypes
import struct
import bpy

def _api():

    def is_ctypes_subclass(other, main_class):
        while other:
            if other is main_class:
                return True
            other = getattr(other, "_type_", None)
        return False

    def is_ctypes_initialized(other):
        _other = other
        while other:
            if hasattr(other, "_fields_"):
                return True
            other = getattr(other, "_type_", None)
        print(_other, "NOT INIT")
        return False

    def is_ctypes_base(other):
        while type(getattr(other, "_type_", "")) != str:
            other = other._type_
        return other

    class MixIn:
        pass

    blend_cdll = ctypes.CDLL("")
    blend_lib = ctypes.LibraryLoader("")

    def blend_parse_dna():
        # from dna.c
        sdna_str_pt = blend_cdll.DNAstr
        sdna_len_pt = blend_cdll.DNAlen

        # cast
        sdna_len_pt = ctypes.c_void_p.from_address(ctypes.addressof(sdna_len_pt))
        sdna_len = ctypes.c_int.from_address(sdna_len_pt.value)

        blend_sdna = ctypes.string_at(sdna_str_pt, sdna_len)

        ofs = 0
        assert(blend_sdna[ofs:ofs + 8] == b'SDNANAME')
        ofs += 8

        sdna_names_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
        ofs += 4

        blend_sdna_names = blend_sdna[ofs:].split(b'\0', sdna_names_len)
        blend_sdna_remainder = blend_sdna_names.pop(-1)  # last item is not a name.
        ofs = len(blend_sdna) - len(blend_sdna_remainder)
        ofs = (ofs + 3) & ~3

        assert(blend_sdna[ofs:ofs + 4] == b'TYPE')
        ofs += 4

        sdna_types_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
        ofs += 4

        blend_sdna_types = blend_sdna[ofs:].split(b'\0', sdna_types_len)
        blend_sdna_remainder = blend_sdna_types.pop(-1)
        ofs = len(blend_sdna) - len(blend_sdna_remainder)
        ofs = (ofs + 3) & ~3

        assert(blend_sdna[ofs:ofs + 4] == b'TLEN')
        ofs += 4

        blend_sdna_typelens = struct.unpack("%dh" % sdna_types_len, blend_sdna[ofs:ofs + (sdna_types_len * 2)])
        ofs += sdna_types_len * 2
        ofs = (ofs + 3) & ~3

        # array of pointers to short arrays
        assert(blend_sdna[ofs:ofs + 4] == b'STRC')
        ofs += 4

        sdna_structs_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
        ofs += 4

        blend_sdna_structs = []

        for i in range(sdna_structs_len):
            struct_type, struct_tot = struct.unpack("hh", blend_sdna[ofs:ofs + 4])
            ofs += 4
            struct_type_name_pairs = struct.unpack("%dh" % struct_tot * 2, blend_sdna[ofs:ofs + (struct_tot * 4)])

            # convert into pairs, easier to understand (type, name)
            struct_type_name_pairs = [(struct_type_name_pairs[j], struct_type_name_pairs[j + 1]) for j in range(0, struct_tot * 2, 2)]

            blend_sdna_structs.append((struct_type, struct_type_name_pairs))
            ofs += struct_tot * 4
            # ofs = (ofs + 1) & ~1

        return blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs

    def create_dna_structs(blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs):

        # create all subclasses of ctypes.Structure
        ctypes_structs = {name: type(name.decode(), (ctypes.Structure, MixIn), {}) for name in blend_sdna_types}
        ctypes_basic = {b"float": ctypes.c_float, b"double": ctypes.c_double, b"int": ctypes.c_int, b"short": ctypes.c_short, b"char": ctypes.c_char, b"void": ctypes.c_void_p}
        ctypes_fields = {}

        # collect fields
        for struct_id, struct_type_name_pairs in blend_sdna_structs:
            struct_name = blend_sdna_types[struct_id]
            ctype_struct = ctypes_structs[struct_name]
            fields = []

            for stype, sname in struct_type_name_pairs:
                name_string = blend_sdna_names[sname]
                type_string = blend_sdna_types[stype]
                type_py = ctypes_basic.get(type_string)
                if type_py is None:
                    type_py = ctypes_structs.get(type_string)

                # todo, these might need to be changed
                name_string = name_string.replace(b"(", b"")
                name_string = name_string.replace(b")", b"")

                # * First parse the pointer *
                pointer_count = 0
                while name_string[0] == 42:  # '*'
                    pointer_count += 1
                    name_string = name_string[1:]

                # alredy a pointer
                if type_py is ctypes.c_void_p:
                    pointer_count -= 1
                elif type_py is ctypes.c_char and pointer_count == 1:
                    type_py = ctypes.c_char_p
                    pointer_count = 0

                if pointer_count < 0:
                    Exception("error parsing pointer")

                for i in range(pointer_count):
                    type_py = ctypes.POINTER(type_py)

                # * Now parse the array [] *
                if b'[' in name_string:
                    name_string = name_string.replace(b'[', b' ')
                    name_string = name_string.replace(b']', b' ')
                    name_split = name_string.split()
                    name_string = name_split[0]
                    for array_dim in reversed(name_split[1:]):
                        type_py = type_py * int(array_dim)

                fields.append((name_string.decode(), type_py))

            ctypes_fields[struct_name] = fields

        # apply fields all in one go!
        for struct_id, struct_type_name_pairs in blend_sdna_structs:
            struct_name = blend_sdna_types[struct_id]
            ctype_struct = ctypes_structs[struct_name]
            try:
                ctype_struct._fields_ = ctypes_fields[struct_name]
            except:
                '''
                print("Error:", struct_name)
                import traceback
                traceback.print_exc()
                '''
                pass
                # print(fields)

        # test fields
        #for struct_id, struct_type_name_pairs in blend_sdna_structs:
        #    ctype_struct = ctypes_structs[blend_sdna_types[struct_id]]
        #    if blend_sdna_typelens[struct_id] != ctypes.sizeof(ctype_struct):
        #        print("Size Mismatch for %r blender:%d vs python:%d" % (blend_sdna_types[struct_id], blend_sdna_typelens[struct_id], ctypes.sizeof(ctype_struct)))

        return ctypes_structs

    def decorate_api(struct_dict):

        # * Decotate the api *

        # listbase iter

        type_cast_lb = struct_dict[b'ListBase']
        type_cast_link = struct_dict[b'Link']

        def list_base_iter(self, type_name):
            if type(type_name) == str:
                type_cast = struct_dict[type_name.encode('ASCII')]
            else:
                # allow passing types direcly
                type_cast = type_name

            # empty listbase
            if self.first is None:
                ret = None
            else:
                try:
                    ret = type_cast_link.from_address(ctypes.addressof(self.first))
                except TypeError:
                    ret = type_cast_link.from_address(self.first)

            while ret is not None:
                return_value = type_cast.from_address(ctypes.addressof(ret))
                try:
                    next_pointer = getattr(ret.next, "contents")
                except:
                    next_pointer = None

                if next_pointer:
                    ret = type_cast_link.from_address(ctypes.addressof(next_pointer))
                else:
                    ret = None

                yield return_value

        struct_dict[b'ListBase'].ITER = list_base_iter

        def CAST(self, to):
            if type(type_name) == str:
                type_cast = struct_dict[to.encode('ASCII')]
            else:
                type_cast = to

            return type_cast.from_address(ctypes.addressof(self))

        MixIn.CAST = CAST

    blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs = blend_parse_dna()
    struct_dict = create_dna_structs(blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs)

    def printStructs(showStructs=None):
        for struct_id, struct_type_name_pairs in blend_sdna_structs:
            struct_name = blend_sdna_types[struct_id].decode()
            if not showStructs or struct_name in showStructs:
                print("typedef struct %s {" % struct_name)
                for stype, sname in struct_type_name_pairs:
                    print("    %s %s;" % (blend_sdna_types[stype].decode(), blend_sdna_names[sname].decode()))
                print("} %s;" % struct_name)        
        
    
    decorate_api(struct_dict)  # not essential but useful

    # manually wrap Main
    Main = type("Main", (ctypes.Structure, ), {})
    _lb = struct_dict[b"ListBase"]
    Main._fields_ = [("next", ctypes.POINTER(Main)),
                     ("prev", ctypes.POINTER(Main)),
                     ("name", ctypes.c_char * 240),
                     ("versionfile", ctypes.c_short),
                     ("subversionfile", ctypes.c_short),
                     ("minversionfile", ctypes.c_short),
                     ("minsubversionfile", ctypes.c_short),
                     ("revision", ctypes.c_int),
                     ("curlib", ctypes.POINTER(struct_dict[b"Library"])),
                     ("scene", _lb),
                     ("library", _lb),
                     ("object", _lb),
                     ("mesh", _lb),
                     ("curve", _lb),
                     ("mball", _lb),
                     ("mat", _lb),
                     ("tex", _lb),
                     ("image", _lb),
                     ("latt", _lb),
                     ("lamp", _lb),
                     ("camera", _lb),
                     ("ipo", _lb),
                     ("key", _lb),
                     ("world", _lb),
                     ("screen", _lb),
                     ("script", _lb),
                     ("vfont", _lb),
                     ("text", _lb),
                     ("sound", _lb),
                     ("group", _lb),
                     ("armature", _lb),
                     ("action", _lb),
                     ("nodetree", _lb),
                     ("brush", _lb),
                     ("particle", _lb),
                     ("wm", _lb),
                     ("gpencil", _lb),
                     ("movieclip", _lb),
                     ("mask",_lb),
                     ("linestyle",_lb)
                     ]
    del _lb

    # import bpy
    # main = Main.from_address(bpy.data.as_pointer())
    # main is the first pointer in Global.
    main_address = ctypes.POINTER(ctypes.c_void_p).from_address(ctypes.addressof(blend_cdll.G)).contents.value
    main = Main.from_address(main_address)

    return main, struct_dict, printStructs

main, _struct_dict, printStructs = _api()

# types dict
types = type(ctypes)("pydna.types")
types.__dict__.update({s.__name__: s for s in _struct_dict.values()})



# --- Image extension component monkey-class into RNA, pydna.py above from r34522.


def setup():
    # only wrap enough to read image buffer
    
    ImBuf = type("ImBuf", (ctypes.Structure, ), {})
    ImBuf._fields_ = [("next", ctypes.POINTER(ImBuf)),
                     ("prev", ctypes.POINTER(ImBuf)),
                     ("x", ctypes.c_int),
                     ("y", ctypes.c_int),
                     ("depth", ctypes.c_ubyte),
                     ("channels", ctypes.c_int),
                     ("flags", ctypes.c_int),
                     ("mall", ctypes.c_int),
                     ("rect", ctypes.POINTER(ctypes.c_ubyte)),
                     ("crect", ctypes.POINTER(ctypes.c_ubyte)),
                     ("rect_float", ctypes.POINTER(ctypes.c_float)),
                     ]

    # pretend we're external
    pydna = type(ctypes)("pydna")
    pydna.__dict__.update(globals())

    def _image_get_buffer(image):
        image_dna = pydna.types.Image.from_address(image.as_pointer())
        for ibuf in image_dna.ibufs.ITER(ImBuf):
            return ibuf.x, ibuf.y, ibuf.rect, ibuf.rect_float
        return 0, 0, (), ()


    def _get_dna(obj):
       _dna = pydna.types.Object.from_address(obj.as_pointer())
       return _dna

    import bpy
    bpy.types.Image.buffer_raw = property(_image_get_buffer)
    
        
    bpy.types.Action._dna = property(_get_dna)
    bpy.types.Armature._dna = property(_get_dna)
    bpy.types.Brush._dna = property(_get_dna)
    bpy.types.Camera._dna = property(_get_dna)
    bpy.types.Curve._dna = property(_get_dna)
    bpy.types.Image._dna = property(_get_dna)
    #bpy.types.Lamp._dna = property(_get_dna)
    bpy.types.Lattice._dna = property(_get_dna)
    bpy.types.Library._dna = property(_get_dna)
    bpy.types.Mask._dna = property(_get_dna)
    bpy.types.MovieClip._dna = property(_get_dna)
    bpy.types.NodeTree._dna = property(_get_dna)
    bpy.types.Palette._dna = property(_get_dna)
    bpy.types.Particle._dna = property(_get_dna)
    bpy.types.Screen._dna = property(_get_dna)
    bpy.types.Speaker._dna = property(_get_dna)
    bpy.types.World._dna = property(_get_dna)
    bpy.types.Object._dna = property(_get_dna)
    bpy.types.NodeTree._dna = property(_get_dna)
    bpy.types.Scene._dna = property(_get_dna)
    bpy.types.Sound._dna = property(_get_dna)
    bpy.types.Texture._dna = property(_get_dna)
    bpy.types.Text._dna = property(_get_dna)
    bpy.types.Material._dna = property(_get_dna)
    bpy.types.Mesh._dna = property(_get_dna)

setup()
ob = bpy.data.objects[0]
print("name:"+str(ob._dna.id.name))

@Andrej730
Copy link

Can confirm that have the same error as above (Blender 3.6).

Error: Python: Traceback (most recent call last):
  File "\Text", line 342, in <module>
  File "\Text", line 76, in _api
  File "\Blender\3.6\python\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 87] The parameter is incorrect

@dertom95
Copy link
Author

dertom95 commented Oct 7, 2023

Can confirm that have the same error as above (Blender 3.6).

Error: Python: Traceback (most recent call last):
  File "\Text", line 342, in <module>
  File "\Text", line 76, in _api
  File "\Blender\3.6\python\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 87] The parameter is incorrect

Yeah, seems like that this don't work anymore as the internal structure seems to be changed. Those were very fragile calls from the get go as we call manually c-calls. Didn't do lots of blender-scripting lately but maybe there is an official way for this already? Maybe @ideasman42 knows more as he wrote the original script....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment