199 lines
6.0 KiB
Python
199 lines
6.0 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (C) 2016 Elliott Mitchell <ehem+android@m5p.com>
|
|
Copyright (C) 2013 IOMonster (thecubed on XDA)
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import print_function
|
|
import sys
|
|
from struct import Struct
|
|
from collections import OrderedDict
|
|
|
|
|
|
class DZStruct(object):
|
|
"""
|
|
Common class for DZ file structures
|
|
"""
|
|
|
|
# Length of the headers in DZ files
|
|
_dz_length = 512
|
|
|
|
def __init__(self, classy):
|
|
"""
|
|
Common initializations for all DZ structures
|
|
"""
|
|
|
|
|
|
# Generate the struct for .unpack()
|
|
try:
|
|
classy._dzstruct
|
|
|
|
except AttributeError:
|
|
classy._dz_struct = Struct("<" + "".join([x[0] for x in classy._dz_format_dict.values()]))
|
|
|
|
# Sanity check
|
|
if self._dz_struct.size != self._dz_length:
|
|
print("[!] Internal error! Chunk format wrong! (computed={:d}, specified={:d})".format(self._dz_struct.size, self._dz_length), file=sys.stderr)
|
|
sys.exit(-1)
|
|
|
|
# Generate list of items that can be collapsed (truncated)
|
|
try:
|
|
classy._dz_collapsibles
|
|
|
|
except AttributeError:
|
|
classy._dz_collapsibles = [n for n, (y, p) in classy._dz_format_dict.items() if p]
|
|
|
|
|
|
def packdict(self, din):
|
|
"""
|
|
Pack all the fields from the dict into a returned buffer
|
|
"""
|
|
|
|
dout = dict()
|
|
|
|
# pad any string keys that need padding
|
|
for k in self._dz_format_dict.keys():
|
|
if self._dz_format_dict[k][0][-1] == 's':
|
|
l = int(self._dz_format_dict[k][0][:-1])
|
|
dout[k] = (din[k] if k in din else b"").ljust(l, b'\x00')
|
|
elif not k in din and k in self._dz_collapsibles:
|
|
dout[k] = 0
|
|
else:
|
|
dout[k] = din[k]
|
|
|
|
dout['header'] = self._dz_header
|
|
|
|
values = [dout[k] for k in self._dz_format_dict.keys()]
|
|
buffer = self._dz_struct.pack(*values)
|
|
|
|
return buffer
|
|
|
|
|
|
def unpackdict(self, buffer):
|
|
"""
|
|
Unpack data in buffer into a returned dictionary, return None
|
|
if magic number/header is absent
|
|
"""
|
|
|
|
d = dict(zip(
|
|
self._dz_format_dict.keys(),
|
|
self._dz_struct.unpack(buffer)
|
|
))
|
|
|
|
if d['header'] != self._dz_header:
|
|
return None
|
|
|
|
return d
|
|
|
|
|
|
|
|
class DZChunk(DZStruct):
|
|
"""
|
|
Representation of an individual file chunk from a LGE DZ file
|
|
"""
|
|
|
|
_dz_area = "chunk"
|
|
_dz_header = b"\x30\x12\x95\x78"
|
|
|
|
# Format string dict
|
|
# itemName is the new dict key for the data to be stored under
|
|
# formatString is the Python formatstring for struct.unpack()
|
|
# collapse: boolean that controls whether extra \x00 's should be stripped
|
|
# for integer types collapse set to True means that the value should always be zero
|
|
# Example:
|
|
# ('itemName', ('formatString', collapse))
|
|
_dz_format_dict = OrderedDict([
|
|
('header', ('4s', False)), # magic number
|
|
('sliceName', ('32s', True)), # name of our slice
|
|
('chunkName', ('64s', True)), # name of our chunk
|
|
('targetSize', ('I', False)), # bytes of target area
|
|
('dataSize', ('I', False)), # amount of compressed
|
|
('md5', ('16s', False)), # MD5 of target image
|
|
('targetAddr', ('I', False)), # first block to write
|
|
('trimCount', ('I', False)), # blocks to TRIM before
|
|
('dev', ('I', False)), # flash device Id
|
|
('crc32', ('I', False)), # CRC32 of target image
|
|
('pad', ('372s', True)), # currently always zero
|
|
])
|
|
|
|
def __init__(self):
|
|
"""
|
|
Initializer for DZChunk, gets DZStruct to fill remaining values
|
|
"""
|
|
super(DZChunk, self).__init__(DZChunk)
|
|
|
|
|
|
|
|
class DZFile(DZStruct):
|
|
"""
|
|
Representation of the data parsed from a LGE DZ file
|
|
"""
|
|
|
|
_dz_area = "file"
|
|
_dz_header = b"\x32\x96\x18\x74"
|
|
|
|
# Format string dict
|
|
# itemName is the new dict key for the data to be stored under
|
|
# formatString is the Python formatstring for struct.unpack()
|
|
# collapse: boolean that controls whether extra \x00 's should be stripped
|
|
# for integer types collapse set to True means that the value should always be zero
|
|
# Example:
|
|
# ('itemName', ('formatString', collapse))
|
|
_dz_format_dict = OrderedDict([
|
|
('header', ('4s', False)), # magic number
|
|
('formatMajor', ('I', False)), # always 2 in LE
|
|
('formatMinor', ('I', False)), # always 1 in LE
|
|
('reserved0', ('I', True)), # format patchlevel?
|
|
('device', ('32s', True)),
|
|
('version', ('121s', True)), # "factoryversion"
|
|
('unknown9', ('23s', False)), # md5?
|
|
('chunkCount', ('I', False)),
|
|
('md5', ('16s', False)), # MD5 of chunk headers
|
|
('unknown0', ('I', False)), # 256?
|
|
('reserved1', ('I', True)), # currently always zero
|
|
('reserved4', ('H', True)), # currently always zero
|
|
('unknown1', ('16s', False)), # unknown, MD5 of thing?
|
|
('unknown2', ('50s', True)), # A##-M##-C##-U##-0 ?
|
|
('buildType', ('20s', True)), # "user"???
|
|
('unknown3', ('4s', False)), # version code? CRC?
|
|
('androidVer', ('10s', True)), # Android ver, optional
|
|
#anti-rollback minimum date? absent from Lollipop, "122142720" on all other V10
|
|
('oldDateCode', ('10s', True)), # prior firmware date?
|
|
('reserved5', ('I', False)), # currently always zero
|
|
('unknown4', ('I', False)), # sometimes 256?
|
|
('unknown5', ('I', False)), # ???
|
|
('unknown6', ('64s', False)), # ???
|
|
('unknown7', ('32s', False)), # ???
|
|
('unknown8', ('8s', False)), # ???
|
|
('pad', ('64s', True)), # currently always zero
|
|
])
|
|
|
|
def __init__(self):
|
|
"""
|
|
Initializer for DZFile, gets DZStruct to fill remaining values
|
|
"""
|
|
super(DZFile, self).__init__(DZFile)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("Sorry, this file is an internal library and doesn't do anything interesting by", file=sys.stderr)
|
|
print("itself.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|