You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
772 lines
23 KiB
Python
772 lines
23 KiB
Python
# Copyright 2017 Martin Olejar
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from struct import pack, Struct
|
|
from string import printable
|
|
|
|
from .header import Header, DTB_PROP, DTB_BEGIN_NODE, DTB_END_NODE
|
|
from .misc import is_string, line_offset
|
|
|
|
BIGENDIAN_WORD = Struct(">I")
|
|
|
|
########################################################################################################################
|
|
# Helper methods
|
|
########################################################################################################################
|
|
|
|
def new_property(name: str, raw_value: bytes) -> object:
|
|
"""
|
|
Instantiate property with raw value type
|
|
|
|
:param name: Property name
|
|
:param raw_value: Property raw data
|
|
"""
|
|
if is_string(raw_value):
|
|
obj = PropStrings(name)
|
|
# Extract strings from raw value
|
|
for st in raw_value.decode('ascii').split('\0'):
|
|
if st:
|
|
obj.append(st)
|
|
return obj
|
|
|
|
elif len(raw_value) > 0 and len(raw_value) <= 256*1024 and len(raw_value) % 4 == 0:
|
|
obj = PropWords(name)
|
|
# Extract words from raw value
|
|
obj.data = [BIGENDIAN_WORD.unpack(raw_value[i:i + 4])[0] for i in range(0, len(raw_value), 4)]
|
|
obj.raw_value = raw_value
|
|
return obj
|
|
|
|
elif len(raw_value):
|
|
return PropBytes(name, data=raw_value)
|
|
|
|
return Property(name)
|
|
|
|
|
|
########################################################################################################################
|
|
# Base Class
|
|
########################################################################################################################
|
|
|
|
class BaseItem:
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
@property
|
|
def label(self):
|
|
return self._label
|
|
|
|
@property
|
|
def parent(self):
|
|
return self._parent
|
|
|
|
@property
|
|
def path(self):
|
|
node = self._parent
|
|
path = ""
|
|
while node:
|
|
if node.name == '/': break
|
|
path = '/' + node.name + path
|
|
node = node.parent
|
|
return path if path else '/'
|
|
|
|
def __init__(self, name: str, label=None):
|
|
"""
|
|
BaseItem constructor
|
|
|
|
:param name: Item name
|
|
"""
|
|
assert isinstance(name, str)
|
|
assert all(c in printable for c in name), "The value must contain just printable chars !"
|
|
self._name = name
|
|
self._label = label
|
|
self._parent = None
|
|
|
|
def __str__(self):
|
|
""" String representation """
|
|
return "{}".format(self.name)
|
|
|
|
def set_name(self, value: str):
|
|
"""
|
|
Set item name
|
|
|
|
:param value: The name in string format
|
|
"""
|
|
assert isinstance(value, str)
|
|
assert all(c in printable for c in value), "The value must contain just printable chars !"
|
|
self._name = value
|
|
|
|
def set_label(self, value: str):
|
|
"""
|
|
Set item label
|
|
|
|
:param value: The label in string format
|
|
"""
|
|
assert isinstance(value, str)
|
|
assert all(c in printable for c in value), "The value must contain just printable chars !"
|
|
self._label = value
|
|
|
|
|
|
def set_parent(self, value):
|
|
"""
|
|
Set item parent
|
|
|
|
:param value: The parent node
|
|
"""
|
|
assert isinstance(value, Node)
|
|
self._parent = value
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0):
|
|
raise NotImplementedError()
|
|
|
|
def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION):
|
|
raise NotImplementedError()
|
|
|
|
|
|
########################################################################################################################
|
|
# Property Classes
|
|
########################################################################################################################
|
|
|
|
class Property(BaseItem):
|
|
|
|
def __getitem__(self, value):
|
|
""" Returns No Items """
|
|
return None
|
|
|
|
def __eq__(self, obj):
|
|
""" Check Property object equality """
|
|
return isinstance(obj, Property) and self.name == obj.name
|
|
|
|
def copy(self):
|
|
""" Get object copy """
|
|
return Property(self.name)
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0):
|
|
"""
|
|
Get string representation
|
|
|
|
:param tabsize: Tabulator size in count of spaces
|
|
:param depth: Start depth for line
|
|
"""
|
|
return line_offset(tabsize, depth, '{};\n'.format(self.name))
|
|
|
|
def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION):
|
|
"""
|
|
Get binary blob representation
|
|
|
|
:param strings:
|
|
:param pos:
|
|
:param version:
|
|
"""
|
|
strpos = strings.find(self.name + '\0')
|
|
if strpos < 0:
|
|
strpos = len(strings)
|
|
strings += self.name + '\0'
|
|
pos += 12
|
|
return pack('>III', DTB_PROP, 0, strpos), strings, pos
|
|
|
|
|
|
class PropStrings(Property):
|
|
"""Property with strings as value"""
|
|
|
|
@property
|
|
def value(self):
|
|
return self.data[0] if self.data else None
|
|
|
|
def __init__(self, name: str, *args):
|
|
"""
|
|
PropStrings constructor
|
|
|
|
:param name: Property name
|
|
:param args: str1, str2, ...
|
|
"""
|
|
super().__init__(name)
|
|
self.data = []
|
|
for arg in args:
|
|
self.append(arg)
|
|
|
|
def __str__(self):
|
|
""" String representation """
|
|
return "{} = {}".format(self.name, self.data)
|
|
|
|
def __len__(self):
|
|
""" Get strings count """
|
|
return len(self.data)
|
|
|
|
def __getitem__(self, index):
|
|
""" Get string by index """
|
|
return self.data[index]
|
|
|
|
def __eq__(self, obj):
|
|
""" Check PropStrings object equality """
|
|
if not isinstance(obj, PropStrings) or self.name != obj.name or len(self) != len(obj):
|
|
return False
|
|
for index in range(len(self)):
|
|
if self.data[index] != obj[index]:
|
|
return False
|
|
return True
|
|
|
|
def copy(self):
|
|
""" Get object copy """
|
|
return PropStrings(self.name, *self.data)
|
|
|
|
def append(self, value: str):
|
|
assert isinstance(value, str)
|
|
assert len(value) > 0, "Invalid strings value"
|
|
assert all(c in printable or c in ('\r', '\n') for c in value), "Invalid chars in strings value"
|
|
self.data.append(value)
|
|
|
|
def pop(self, index: int):
|
|
assert 0 <= index < len(self.data), "Index out of range"
|
|
return self.data.pop(index)
|
|
|
|
def clear(self):
|
|
self.data.clear()
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0):
|
|
"""
|
|
Get string representation
|
|
|
|
:param tabsize: Tabulator size in count of spaces
|
|
:param depth: Start depth for line
|
|
"""
|
|
result = line_offset(tabsize, depth, self.name)
|
|
result += ' = "'
|
|
result += '", "'.join([item.replace('"', '\\"') for item in self.data])
|
|
result += '";\n'
|
|
return result
|
|
|
|
def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION):
|
|
"""
|
|
Get blob representation
|
|
|
|
:param strings:
|
|
:param pos:
|
|
:param version:
|
|
"""
|
|
blob = pack('')
|
|
for chars in self.data:
|
|
blob += chars.encode('ascii') + pack('b', 0)
|
|
blob_len = len(blob)
|
|
if version < 16 and (pos + 12) % 8 != 0:
|
|
blob = pack('b', 0) * (8 - ((pos + 12) % 8)) + blob
|
|
if blob_len % 4:
|
|
blob += pack('b', 0) * (4 - (blob_len % 4))
|
|
strpos = strings.find(self.name + '\0')
|
|
if strpos < 0:
|
|
strpos = len(strings)
|
|
strings += self.name + '\0'
|
|
blob = pack('>III', DTB_PROP, blob_len, strpos) + blob
|
|
pos += len(blob)
|
|
return blob, strings, pos
|
|
|
|
|
|
class PropWords(Property):
|
|
"""Property with words as value"""
|
|
|
|
@property
|
|
def value(self):
|
|
return self.data[0] if self.data else None
|
|
|
|
def __init__(self, name, *args):
|
|
"""
|
|
PropWords constructor
|
|
|
|
:param name: Property name
|
|
:param args: word1, word2, ...
|
|
"""
|
|
super().__init__(name)
|
|
self.data = []
|
|
self.word_size = 32
|
|
for val in args:
|
|
self.append(val)
|
|
|
|
def __str__(self):
|
|
""" String representation """
|
|
return "{} = {}".format(self.name, self.data)
|
|
|
|
def __getitem__(self, index):
|
|
""" Get word by index """
|
|
return self.data[index]
|
|
|
|
def __len__(self):
|
|
""" Get words count """
|
|
return len(self.data)
|
|
|
|
def __eq__(self, prop):
|
|
""" Check PropWords object equality """
|
|
if not isinstance(prop, PropWords):
|
|
return False
|
|
if self.name != prop.name:
|
|
return False
|
|
if len(self) != len(prop):
|
|
return False
|
|
for index in range(len(self)):
|
|
if self.data[index] != prop[index]:
|
|
return False
|
|
return True
|
|
|
|
def copy(self):
|
|
return PropWords(self.name, *self.data)
|
|
|
|
def append(self, value):
|
|
assert isinstance(value, int), "Invalid object type"
|
|
assert 0 <= value < 2**self.word_size, "Invalid word value {}, use <0x0 - 0x{:X}>".format(
|
|
value, 2**self.word_size - 1)
|
|
self.data.append(value)
|
|
|
|
def pop(self, index):
|
|
assert 0 <= index < len(self.data), "Index out of range"
|
|
return self.data.pop(index)
|
|
|
|
def clear(self):
|
|
self.data.clear()
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0):
|
|
"""
|
|
Get string representation
|
|
|
|
:param tabsize: Tabulator size in count of spaces
|
|
:param depth: Start depth for line
|
|
"""
|
|
result = line_offset(tabsize, depth, self.name)
|
|
result += ' = <'
|
|
result += ' '.join(["0x{:X}".format(word) for word in self.data])
|
|
result += ">;\n"
|
|
return result
|
|
|
|
def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION):
|
|
"""
|
|
Get blob representation
|
|
|
|
:param strings:
|
|
:param pos:
|
|
:param version:
|
|
"""
|
|
strpos = strings.find(self.name + '\0')
|
|
if strpos < 0:
|
|
strpos = len(strings)
|
|
strings += self.name + '\0'
|
|
blob = pack('>III', DTB_PROP, len(self.data) * 4, strpos)
|
|
blob += bytes().join([BIGENDIAN_WORD.pack(word) for word in self.data])
|
|
pos += len(blob)
|
|
return blob, strings, pos
|
|
|
|
|
|
class PropBytes(Property):
|
|
"""Property with bytes as value"""
|
|
|
|
def __init__(self, name, *args, data=None):
|
|
"""
|
|
PropBytes constructor
|
|
|
|
:param name: Property name
|
|
:param args: byte0, byte1, ...
|
|
:param data: Data as list, bytes or bytearray
|
|
"""
|
|
super().__init__(name)
|
|
self.data = bytearray(args)
|
|
if data:
|
|
assert isinstance(data, (list, bytes, bytearray))
|
|
self.data += bytearray(data)
|
|
|
|
def __str__(self):
|
|
""" String representation """
|
|
return "{} = {}".format(self.name, self.data)
|
|
|
|
def __getitem__(self, index):
|
|
"""Get byte by index """
|
|
return self.data[index]
|
|
|
|
def __len__(self):
|
|
""" Get bytes count """
|
|
return len(self.data)
|
|
|
|
def __eq__(self, prop):
|
|
""" Check PropBytes object equality """
|
|
if not isinstance(prop, PropBytes):
|
|
return False
|
|
if self.name != prop.name:
|
|
return False
|
|
if len(self) != len(prop):
|
|
return False
|
|
for index in range(len(self)):
|
|
if self.data[index] != prop[index]:
|
|
return False
|
|
return True
|
|
|
|
def copy(self):
|
|
""" Create a copy of object """
|
|
return PropBytes(self.name, data=self.data)
|
|
|
|
def append(self, value):
|
|
assert isinstance(value, int), "Invalid object type"
|
|
assert 0 <= value <= 0xFF, "Invalid byte value {}, use <0 - 255>".format(value)
|
|
self.data.append(value)
|
|
|
|
def pop(self, index):
|
|
assert 0 <= index < len(self.data), "Index out of range"
|
|
return self.data.pop(index)
|
|
|
|
def clear(self):
|
|
self.data = bytearray()
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0):
|
|
"""
|
|
Get string representation
|
|
|
|
:param tabsize: Tabulator size in count of spaces
|
|
:param depth: Start depth for line
|
|
"""
|
|
result = line_offset(tabsize, depth, self.name)
|
|
result += ' = ['
|
|
result += ' '.join(["{:02X}".format(byte) for byte in self.data])
|
|
result += '];\n'
|
|
return result
|
|
|
|
def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION):
|
|
"""
|
|
Get blob representation
|
|
|
|
:param strings:
|
|
:param pos:
|
|
:param version:
|
|
"""
|
|
strpos = strings.find(self.name + '\0')
|
|
if strpos < 0:
|
|
strpos = len(strings)
|
|
strings += self.name + '\0'
|
|
blob = pack('>III', DTB_PROP, len(self.data), strpos)
|
|
blob += bytes(self.data)
|
|
if len(blob) % 4:
|
|
blob += bytes([0] * (4 - (len(blob) % 4)))
|
|
pos += len(blob)
|
|
return blob, strings, pos
|
|
|
|
|
|
class PropIncBin(PropBytes):
|
|
"""Property with bytes as value"""
|
|
|
|
def __init__(self, name, data=None, file_name=None, rpath=None):
|
|
"""
|
|
PropIncBin constructor
|
|
|
|
:param name: Property name
|
|
:param data: Data as list, bytes or bytearray
|
|
:param file_name: File name
|
|
:param rpath: Relative path
|
|
"""
|
|
super().__init__(name, data)
|
|
self.file_name = file_name
|
|
self.relative_path = rpath
|
|
|
|
def __eq__(self, prop):
|
|
""" Check PropIncBin object equality """
|
|
if not isinstance(prop, PropIncBin):
|
|
return False
|
|
if self.name != prop.name:
|
|
return False
|
|
if self.file_name != prop.file_name:
|
|
return False
|
|
if self.relative_path != prop.relative_path:
|
|
return False
|
|
if self.data != prop.data:
|
|
return False
|
|
return True
|
|
|
|
def copy(self):
|
|
""" Create a copy of object """
|
|
return PropIncBin(self.name, self.data, self.file_name, self.relative_path)
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0):
|
|
"""
|
|
Get string representation
|
|
|
|
:param tabsize: Tabulator size in count of spaces
|
|
:param depth: Start depth for line
|
|
"""
|
|
file_path = self.file_name
|
|
if self.relative_path is not None:
|
|
file_path = "{}/{}".format(self.relative_path, self.file_name)
|
|
result = line_offset(tabsize, depth, self.name)
|
|
result += " = /incbin/(\"{}\");\n".format(file_path)
|
|
return result
|
|
|
|
|
|
########################################################################################################################
|
|
# Node Class
|
|
########################################################################################################################
|
|
|
|
class Node(BaseItem):
|
|
"""Node representation"""
|
|
|
|
@property
|
|
def path(self):
|
|
return super().path + '/' + self.name
|
|
|
|
@property
|
|
def props(self):
|
|
return self._props
|
|
|
|
@property
|
|
def nodes(self):
|
|
return self._nodes
|
|
|
|
@property
|
|
def empty(self):
|
|
return False if self.nodes or self.props else True
|
|
|
|
def __init__(self, name, *args, label=None):
|
|
"""
|
|
Node constructor
|
|
|
|
:param name: Node name
|
|
:param args: List of properties and subnodes
|
|
"""
|
|
super().__init__(name, label=label)
|
|
self._props = []
|
|
self._nodes = []
|
|
for item in args:
|
|
self.append(item)
|
|
|
|
def __str__(self):
|
|
""" String representation """
|
|
return "< {}: {} props, {} nodes >".format(self.name, len(self.props), len(self.nodes))
|
|
|
|
def __eq__(self, node):
|
|
""" Check node equality """
|
|
if not isinstance(node, Node):
|
|
return False
|
|
if self.name != node.name or \
|
|
len(self.props) != len(node.props) or \
|
|
len(self.nodes) != len(node.nodes):
|
|
return False
|
|
for p in self.props:
|
|
if p not in node.props:
|
|
return False
|
|
for n in self.nodes:
|
|
if n not in node.nodes:
|
|
return False
|
|
return True
|
|
|
|
def copy(self):
|
|
""" Create a copy of Node object """
|
|
node = Node(self.name, label=self.label)
|
|
for p in self.props:
|
|
node.append(p.copy())
|
|
for n in self.nodes:
|
|
node.append(n.copy())
|
|
return node
|
|
|
|
def get_property(self, name):
|
|
"""
|
|
Get property object by its name
|
|
|
|
:param name: Property name
|
|
"""
|
|
for p in self.props:
|
|
if p.name == name:
|
|
return p
|
|
return None
|
|
|
|
def set_property(self, name, value):
|
|
"""
|
|
Set property
|
|
|
|
:param name: Property name
|
|
:param value: Property value
|
|
"""
|
|
if value is None:
|
|
new_prop = Property(name)
|
|
elif isinstance(value, int):
|
|
new_prop = PropWords(name, value)
|
|
elif isinstance(value, str):
|
|
new_prop = PropStrings(name, value)
|
|
elif isinstance(value, list) and isinstance(value[0], int):
|
|
new_prop = PropWords(name, *value)
|
|
elif isinstance(value, list) and isinstance(value[0], str):
|
|
new_prop = PropStrings(name, *value)
|
|
elif isinstance(value, (bytes, bytearray)):
|
|
new_prop = PropBytes(name, data=value)
|
|
else:
|
|
raise TypeError('Value type not supported')
|
|
new_prop.set_parent(self)
|
|
old_prop = self.get_property(name)
|
|
if old_prop is None:
|
|
self.props.append(new_prop)
|
|
else:
|
|
index = self.props.index(old_prop)
|
|
self.props[index] = new_prop
|
|
|
|
def get_subnode(self, name: str):
|
|
"""
|
|
Get subnode object by name
|
|
|
|
:param name: Subnode name
|
|
"""
|
|
for n in self.nodes:
|
|
if n.name == name:
|
|
return n
|
|
return None
|
|
|
|
def exist_property(self, name: str) -> bool:
|
|
"""
|
|
Check if property exist and return True if exist else False
|
|
|
|
:param name: Property name
|
|
"""
|
|
return False if self.get_property(name) is None else True
|
|
|
|
def exist_subnode(self, name: str) -> bool:
|
|
"""
|
|
Check if subnode exist and return True if exist else False
|
|
|
|
:param name: Subnode name
|
|
"""
|
|
return False if self.get_subnode(name) is None else True
|
|
|
|
def remove_property(self, name: str):
|
|
"""
|
|
Remove property object by its name.
|
|
|
|
:param name: Property name
|
|
"""
|
|
item = self.get_property(name)
|
|
if item is not None:
|
|
self.props.remove(item)
|
|
|
|
def remove_subnode(self, name: str):
|
|
"""
|
|
Remove subnode object by its name.
|
|
|
|
:param name: Subnode name
|
|
"""
|
|
item = self.get_subnode(name)
|
|
if item is not None:
|
|
self.nodes.remove(item)
|
|
|
|
def append(self, item):
|
|
"""
|
|
Append node or property
|
|
|
|
:param item: The node or property object
|
|
"""
|
|
assert isinstance(item, (Node, Property)), "Invalid object type, use \"Node\" or \"Property\""
|
|
|
|
if isinstance(item, Property):
|
|
if self.get_property(item.name) is not None:
|
|
raise Exception("{}: \"{}\" property already exists".format(self, item.name))
|
|
item.set_parent(self)
|
|
self.props.append(item)
|
|
|
|
else:
|
|
if self.get_subnode(item.name) is not None:
|
|
raise Exception("{}: \"{}\" node already exists".format(self, item.name))
|
|
if item is self:
|
|
raise Exception("{}: append the same node {}".format(self, item.name))
|
|
item.set_parent(self)
|
|
self.nodes.append(item)
|
|
|
|
def merge(self, node_obj, replace: bool = True):
|
|
"""
|
|
Merge two nodes
|
|
|
|
:param node_obj: Node object
|
|
:param replace: If True, replace current properties with the given properties
|
|
"""
|
|
assert isinstance(node_obj, Node), "Invalid object type"
|
|
|
|
def get_property_index(name):
|
|
for i, p in enumerate(self.props):
|
|
if p.name == name:
|
|
return i
|
|
return None
|
|
|
|
def get_subnode_index(name):
|
|
for i, n in enumerate(self.nodes):
|
|
if n.name == name:
|
|
return i
|
|
return None
|
|
|
|
for prop in node_obj.props:
|
|
index = get_property_index(prop.name)
|
|
if index is None:
|
|
self.append(prop.copy())
|
|
elif prop in self._props:
|
|
continue
|
|
elif replace:
|
|
new_prop = prop.copy()
|
|
new_prop.set_parent(self)
|
|
self._props[index] = new_prop
|
|
else:
|
|
pass
|
|
|
|
for sub_node in node_obj.nodes:
|
|
index = get_subnode_index(sub_node.name)
|
|
if index is None:
|
|
self.append(sub_node.copy())
|
|
elif sub_node in self._nodes:
|
|
continue
|
|
else:
|
|
self._nodes[index].merge(sub_node, replace)
|
|
|
|
def to_dts(self, tabsize: int = 4, depth: int = 0) -> str:
|
|
"""
|
|
Get string representation of NODE object
|
|
|
|
:param tabsize: Tabulator size in count of spaces
|
|
:param depth: Start depth for line
|
|
"""
|
|
if self._label is not None:
|
|
dts = line_offset(tabsize, depth, self._label + ': ' + self.name + ' {\n')
|
|
else:
|
|
dts = line_offset(tabsize, depth, self.name + ' {\n')
|
|
# phantom properties which maintain reference state info
|
|
# have names ending with _with_references
|
|
# don't write those out to dts file
|
|
dts += ''.join(
|
|
prop.to_dts(tabsize, depth + 1)
|
|
for prop in self._props if prop.name.endswith('_with_references') is False)
|
|
dts += ''.join(node.to_dts(tabsize, depth + 1) for node in self._nodes)
|
|
dts += line_offset(tabsize, depth, "};\n")
|
|
return dts
|
|
|
|
def to_dtb(self, strings: str, pos: int = 0, version: int = Header.MAX_VERSION) -> tuple:
|
|
"""
|
|
Get NODE in binary blob representation
|
|
|
|
:param strings:
|
|
:param pos:
|
|
:param version:
|
|
"""
|
|
if self.name == '/':
|
|
blob = pack('>II', DTB_BEGIN_NODE, 0)
|
|
else:
|
|
blob = pack('>I', DTB_BEGIN_NODE)
|
|
blob += self.name.encode('ascii') + b'\0'
|
|
if len(blob) % 4:
|
|
blob += pack('b', 0) * (4 - (len(blob) % 4))
|
|
pos += len(blob)
|
|
for prop in self._props:
|
|
# phantom property too maintain reference state should
|
|
# not write out to dtb file
|
|
if prop.name.endswith('_with_references') is False:
|
|
(data, strings, pos) = prop.to_dtb(strings, pos, version)
|
|
blob += data
|
|
for node in self._nodes:
|
|
(data, strings, pos) = node.to_dtb(strings, pos, version)
|
|
blob += data
|
|
pos += 4
|
|
blob += pack('>I', DTB_END_NODE)
|
|
return blob, strings, pos
|