Blender V2.61 - r43446

pydna.py

Go to the documentation of this file.
00001 # ##### BEGIN GPL LICENSE BLOCK #####
00002 #
00003 #  This program is free software; you can redistribute it and/or
00004 #  modify it under the terms of the GNU General Public License
00005 #  as published by the Free Software Foundation; either version 2
00006 #  of the License, or (at your option) any later version.
00007 #
00008 #  This program is distributed in the hope that it will be useful,
00009 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 #  GNU General Public License for more details.
00012 #
00013 #  You should have received a copy of the GNU General Public License
00014 #  along with this program; if not, write to the Free Software Foundation,
00015 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00016 #
00017 # ##### END GPL LICENSE BLOCK #####
00018 
00019 # <pep8 compliant>
00020 
00021 """
00022 Experemental module (for developers only), exposes DNA data via python
00023 uses no blender/python modules, pure python + autogenerated ctypes api.
00024 
00025 Exposes:
00026  * pydna.main: main database access.
00027  * pydna.types: a module of all DNA ctypes structures.
00028 
00029  * Utility method: CAST(dna_type)
00030  * Utility method for ListBase: ITER(dna_type)
00031 
00032 Example:
00033  import sys
00034  sys.path.append("/b/intern/tools")
00035 
00036  import pydna
00037 
00038  for obj in pydna.main.object.ITER("Object"):
00039      print("Object:", obj.id.name)
00040      if obj.data:
00041          data = pydna.types.ID.from_address(obj.data)
00042          print("  ObData:", data.name)
00043 """
00044 
00045 import ctypes
00046 import struct
00047 
00048 
00049 def _api():
00050 
00051     def is_ctypes_subclass(other, main_class):
00052         while other:
00053             if other is main_class:
00054                 return True
00055             other = getattr(other, "_type_", None)
00056         return False
00057 
00058     def is_ctypes_initialized(other):
00059         _other = other
00060         while other:
00061             if hasattr(other, "_fields_"):
00062                 return True
00063             other = getattr(other, "_type_", None)
00064         print(_other, "NOT INIT")
00065         return False
00066 
00067     def is_ctypes_base(other):
00068         while type(getattr(other, "_type_", "")) != str:
00069             other = other._type_
00070         return other
00071 
00072     class MixIn:
00073         pass
00074 
00075     blend_cdll = ctypes.CDLL("")
00076     blend_lib = ctypes.LibraryLoader("")
00077 
00078     def blend_parse_dna():
00079         # from dna.c
00080         sdna_str_pt = blend_cdll.DNAstr
00081         sdna_len_pt = blend_cdll.DNAlen
00082 
00083         # cast
00084         sdna_len_pt = ctypes.c_void_p.from_address(ctypes.addressof(sdna_len_pt))
00085         sdna_len = ctypes.c_int.from_address(sdna_len_pt.value)
00086 
00087         blend_sdna = ctypes.string_at(sdna_str_pt, sdna_len)
00088 
00089         ofs = 0
00090         assert(blend_sdna[ofs:ofs + 8] == b'SDNANAME')
00091         ofs += 8
00092 
00093         sdna_names_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
00094         ofs += 4
00095 
00096         blend_sdna_names = blend_sdna[ofs:].split(b'\0', sdna_names_len)
00097         blend_sdna_remainder = blend_sdna_names.pop(-1)  # last item is not a name.
00098         ofs = len(blend_sdna) - len(blend_sdna_remainder)
00099         ofs = (ofs + 3) & ~3
00100 
00101         assert(blend_sdna[ofs:ofs + 4] == b'TYPE')
00102         ofs += 4
00103 
00104         sdna_types_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
00105         ofs += 4
00106 
00107         blend_sdna_types = blend_sdna[ofs:].split(b'\0', sdna_types_len)
00108         blend_sdna_remainder = blend_sdna_types.pop(-1)
00109         ofs = len(blend_sdna) - len(blend_sdna_remainder)
00110         ofs = (ofs + 3) & ~3
00111 
00112         assert(blend_sdna[ofs:ofs + 4] == b'TLEN')
00113         ofs += 4
00114 
00115         blend_sdna_typelens = struct.unpack("%dh" % sdna_types_len, blend_sdna[ofs:ofs + (sdna_types_len * 2)])
00116         ofs += sdna_types_len * 2
00117         ofs = (ofs + 3) & ~3
00118 
00119         # array of pointers to short arrays
00120         assert(blend_sdna[ofs:ofs + 4] == b'STRC')
00121         ofs += 4
00122 
00123         sdna_structs_len = struct.unpack("i", blend_sdna[ofs:ofs + 4])[0]
00124         ofs += 4
00125 
00126         blend_sdna_structs = []
00127 
00128         for i in range(sdna_structs_len):
00129             struct_type, struct_tot = struct.unpack("hh", blend_sdna[ofs:ofs + 4])
00130             ofs += 4
00131             struct_type_name_pairs = struct.unpack("%dh" % struct_tot * 2, blend_sdna[ofs:ofs + (struct_tot * 4)])
00132 
00133             # convert into pairs, easier to understand (type, name)
00134             struct_type_name_pairs = [(struct_type_name_pairs[j], struct_type_name_pairs[j + 1]) for j in range(0, struct_tot * 2, 2)]
00135 
00136             blend_sdna_structs.append((struct_type, struct_type_name_pairs))
00137             ofs += struct_tot * 4
00138             # ofs = (ofs + 1) & ~1
00139 
00140         return blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs
00141 
00142     def create_dna_structs(blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs):
00143 
00144         # create all subclasses of ctypes.Structure
00145         ctypes_structs = {name: type(name.decode(), (ctypes.Structure, MixIn), {}) for name in blend_sdna_types}
00146         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}
00147         ctypes_fields = {}
00148 
00149         # collect fields
00150         for struct_id, struct_type_name_pairs in blend_sdna_structs:
00151             struct_name = blend_sdna_types[struct_id]
00152             ctype_struct = ctypes_structs[struct_name]
00153             fields = []
00154 
00155             for stype, sname in struct_type_name_pairs:
00156                 name_string = blend_sdna_names[sname]
00157                 type_string = blend_sdna_types[stype]
00158                 type_py = ctypes_basic.get(type_string)
00159                 if type_py is None:
00160                     type_py = ctypes_structs.get(type_string)
00161 
00162                 # todo, these might need to be changed
00163                 name_string = name_string.replace(b"(", b"")
00164                 name_string = name_string.replace(b")", b"")
00165 
00166                 # * First parse the pointer *
00167                 pointer_count = 0
00168                 while name_string[0] == 42:  # '*'
00169                     pointer_count += 1
00170                     name_string = name_string[1:]
00171 
00172                 # alredy a pointer
00173                 if type_py is ctypes.c_void_p:
00174                     pointer_count -= 1
00175                 elif type_py is ctypes.c_char and pointer_count == 1:
00176                     type_py = ctypes.c_char_p
00177                     pointer_count = 0
00178 
00179                 if pointer_count < 0:
00180                     Exception("error parsing pointer")
00181 
00182                 for i in range(pointer_count):
00183                     type_py = ctypes.POINTER(type_py)
00184 
00185                 # * Now parse the array [] *
00186                 if b'[' in name_string:
00187                     name_string = name_string.replace(b'[', b' ')
00188                     name_string = name_string.replace(b']', b' ')
00189                     name_split = name_string.split()
00190                     name_string = name_split[0]
00191                     for array_dim in reversed(name_split[1:]):
00192                         type_py = type_py * int(array_dim)
00193 
00194                 fields.append((name_string.decode(), type_py))
00195 
00196             ctypes_fields[struct_name] = fields
00197 
00198         # apply fields all in one go!
00199         for struct_id, struct_type_name_pairs in blend_sdna_structs:
00200             struct_name = blend_sdna_types[struct_id]
00201             ctype_struct = ctypes_structs[struct_name]
00202             try:
00203                 ctype_struct._fields_ = ctypes_fields[struct_name]
00204             except:
00205                 print("Error:", struct_name)
00206                 import traceback
00207                 traceback.print_exc()
00208                 # print(fields)
00209 
00210         # test fields
00211         for struct_id, struct_type_name_pairs in blend_sdna_structs:
00212             ctype_struct = ctypes_structs[blend_sdna_types[struct_id]]
00213             if blend_sdna_typelens[struct_id] != ctypes.sizeof(ctype_struct):
00214                 print("Size Mismatch for %r blender:%d vs python:%d" % (blend_sdna_types[struct_id], blend_sdna_typelens[struct_id], ctypes.sizeof(ctype_struct)))
00215 
00216         return ctypes_structs
00217 
00218     def decorate_api(struct_dict):
00219 
00220         # * Decotate the api *
00221 
00222         # listbase iter
00223 
00224         type_cast_lb = struct_dict[b'ListBase']
00225         type_cast_link = struct_dict[b'Link']
00226 
00227         def list_base_iter(self, type_name):
00228             if type(type_name) == str:
00229                 type_cast = struct_dict[type_name.encode('ASCII')]
00230             else:
00231                 # allow passing types direcly
00232                 type_cast = type_name
00233 
00234             # empty listbase
00235             if self.first is None:
00236                 ret = None
00237             else:
00238                 try:
00239                     ret = type_cast_link.from_address(ctypes.addressof(self.first))
00240                 except TypeError:
00241                     ret = type_cast_link.from_address(self.first)
00242 
00243             while ret is not None:
00244                 return_value = type_cast.from_address(ctypes.addressof(ret))
00245                 try:
00246                     next_pointer = getattr(ret.next, "contents")
00247                 except:
00248                     next_pointer = None
00249 
00250                 if next_pointer:
00251                     ret = type_cast_link.from_address(ctypes.addressof(next_pointer))
00252                 else:
00253                     ret = None
00254 
00255                 yield return_value
00256 
00257         struct_dict[b'ListBase'].ITER = list_base_iter
00258 
00259         def CAST(self, to):
00260             if type(to) == str:
00261                 type_cast = struct_dict[to.encode('ASCII')]
00262             else:
00263                 type_cast = to
00264 
00265             return type_cast.from_address(ctypes.addressof(self))
00266 
00267         MixIn.CAST = CAST
00268 
00269     blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs = blend_parse_dna()
00270     struct_dict = create_dna_structs(blend_sdna_names, blend_sdna_types, blend_sdna_typelens, blend_sdna_structs)
00271 
00272     # print out all structs
00273     '''
00274     for struct_id, struct_type_name_pairs in blend_sdna_structs:
00275         print("")
00276         sruct_name = blend_sdna_types[struct_id].decode()
00277         print("typedef struct %s {" % sruct_name)
00278         for stype, sname in struct_type_name_pairs:
00279             print("    %s %s;" % (blend_sdna_types[stype].decode(), blend_sdna_names[sname].decode()))
00280         print("} %s;" % sruct_name)
00281     '''
00282 
00283     decorate_api(struct_dict)  # not essential but useful
00284 
00285     # manually wrap Main
00286     Main = type("Main", (ctypes.Structure, ), {})
00287     _lb = struct_dict[b"ListBase"]
00288     Main._fields_ = [("next", ctypes.POINTER(Main)),
00289                      ("prev", ctypes.POINTER(Main)),
00290                      ("name", ctypes.c_char * 240),
00291                      ("versionfile", ctypes.c_short),
00292                      ("subversionfile", ctypes.c_short),
00293                      ("minversionfile", ctypes.c_short),
00294                      ("minsubversionfile", ctypes.c_short),
00295                      ("revision", ctypes.c_int),
00296                      ("curlib", ctypes.POINTER(struct_dict[b"Library"])),
00297                      ("scene", _lb),
00298                      ("library", _lb),
00299                      ("object", _lb),
00300                      ("mesh", _lb),
00301                      ("curve", _lb),
00302                      ("mball", _lb),
00303                      ("mat", _lb),
00304                      ("tex", _lb),
00305                      ("image", _lb),
00306                      ("latt", _lb),
00307                      ("lamp", _lb),
00308                      ("camera", _lb),
00309                      ("ipo", _lb),
00310                      ("key", _lb),
00311                      ("world", _lb),
00312                      ("screen", _lb),
00313                      ("script", _lb),
00314                      ("vfont", _lb),
00315                      ("text", _lb),
00316                      ("sound", _lb),
00317                      ("group", _lb),
00318                      ("armature", _lb),
00319                      ("action", _lb),
00320                      ("nodetree", _lb),
00321                      ("brush", _lb),
00322                      ("particle", _lb),
00323                      ("wm", _lb),
00324                      ("gpencil", _lb),
00325                      ]
00326     del _lb
00327 
00328     # import bpy
00329     # main = Main.from_address(bpy.data.as_pointer())
00330     # main is the first pointer in Global.
00331     main_address = ctypes.POINTER(ctypes.c_void_p).from_address(ctypes.addressof(blend_cdll.G)).contents.value
00332     main = Main.from_address(main_address)
00333 
00334     return main, struct_dict
00335 
00336 main, _struct_dict = _api()
00337 
00338 # types dict
00339 types = type(ctypes)("pydna.types")
00340 types.__dict__.update({s.__name__: s for s in _struct_dict.values()})