16a23d212SGreg Clayton#!/usr/bin/env python
26a23d212SGreg Clayton
36a23d212SGreg Claytonimport string
46a23d212SGreg Claytonimport struct
56a23d212SGreg Claytonimport sys
66a23d212SGreg Clayton
7b9c1b51eSKate Stone
86a23d212SGreg Claytonclass FileExtract:
96a23d212SGreg Clayton    '''Decode binary data from a file'''
106a23d212SGreg Clayton
116a23d212SGreg Clayton    def __init__(self, f, b='='):
126a23d212SGreg Clayton        '''Initialize with an open binary file and optional byte order'''
136a23d212SGreg Clayton
146a23d212SGreg Clayton        self.file = f
156a23d212SGreg Clayton        self.byte_order = b
166a23d212SGreg Clayton        self.offsets = list()
176a23d212SGreg Clayton
186a23d212SGreg Clayton    def set_byte_order(self, b):
196a23d212SGreg Clayton        '''Set the byte order, valid values are "big", "little", "swap", "native", "<", ">", "@", "="'''
206a23d212SGreg Clayton        if b == 'big':
216a23d212SGreg Clayton            self.byte_order = '>'
226a23d212SGreg Clayton        elif b == 'little':
236a23d212SGreg Clayton            self.byte_order = '<'
246a23d212SGreg Clayton        elif b == 'swap':
256a23d212SGreg Clayton            # swap what ever the current byte order is
266a23d212SGreg Clayton            self.byte_order = swap_unpack_char()
276a23d212SGreg Clayton        elif b == 'native':
286a23d212SGreg Clayton            self.byte_order = '='
296a23d212SGreg Clayton        elif b == '<' or b == '>' or b == '@' or b == '=':
306a23d212SGreg Clayton            self.byte_order = b
316a23d212SGreg Clayton        else:
32*525cd59fSSerge Guelton            print("error: invalid byte order specified: '%s'" % b)
336a23d212SGreg Clayton
346a23d212SGreg Clayton    def is_in_memory(self):
356a23d212SGreg Clayton        return False
366a23d212SGreg Clayton
376a23d212SGreg Clayton    def seek(self, offset, whence=0):
386a23d212SGreg Clayton        if self.file:
396a23d212SGreg Clayton            return self.file.seek(offset, whence)
406a23d212SGreg Clayton        raise ValueError
416a23d212SGreg Clayton
426a23d212SGreg Clayton    def tell(self):
436a23d212SGreg Clayton        if self.file:
446a23d212SGreg Clayton            return self.file.tell()
456a23d212SGreg Clayton        raise ValueError
466a23d212SGreg Clayton
476a23d212SGreg Clayton    def read_size(self, byte_size):
486a23d212SGreg Clayton        s = self.file.read(byte_size)
496a23d212SGreg Clayton        if len(s) != byte_size:
506a23d212SGreg Clayton            return None
516a23d212SGreg Clayton        return s
526a23d212SGreg Clayton
536a23d212SGreg Clayton    def push_offset_and_seek(self, offset):
546a23d212SGreg Clayton        '''Push the current file offset and seek to "offset"'''
556a23d212SGreg Clayton        self.offsets.append(self.file.tell())
566a23d212SGreg Clayton        self.file.seek(offset, 0)
576a23d212SGreg Clayton
586a23d212SGreg Clayton    def pop_offset_and_seek(self):
596a23d212SGreg Clayton        '''Pop a previously pushed file offset, or do nothing if there were no previously pushed offsets'''
606a23d212SGreg Clayton        if len(self.offsets) > 0:
616a23d212SGreg Clayton            self.file.seek(self.offsets.pop())
626a23d212SGreg Clayton
636a23d212SGreg Clayton    def get_sint8(self, fail_value=0):
646a23d212SGreg Clayton        '''Extract a single int8_t from the binary file at the current file position, returns a single integer'''
656a23d212SGreg Clayton        s = self.read_size(1)
666a23d212SGreg Clayton        if s:
676a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'b', s)
686a23d212SGreg Clayton            return v
696a23d212SGreg Clayton        else:
706a23d212SGreg Clayton            return fail_value
716a23d212SGreg Clayton
726a23d212SGreg Clayton    def get_uint8(self, fail_value=0):
736a23d212SGreg Clayton        '''Extract a single uint8_t from the binary file at the current file position, returns a single integer'''
746a23d212SGreg Clayton        s = self.read_size(1)
756a23d212SGreg Clayton        if s:
766a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'B', s)
776a23d212SGreg Clayton            return v
786a23d212SGreg Clayton        else:
796a23d212SGreg Clayton            return fail_value
806a23d212SGreg Clayton
816a23d212SGreg Clayton    def get_sint16(self, fail_value=0):
826a23d212SGreg Clayton        '''Extract a single int16_t from the binary file at the current file position, returns a single integer'''
836a23d212SGreg Clayton        s = self.read_size(2)
846a23d212SGreg Clayton        if s:
856a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'h', s)
866a23d212SGreg Clayton            return v
876a23d212SGreg Clayton        else:
886a23d212SGreg Clayton            return fail_value
896a23d212SGreg Clayton
906a23d212SGreg Clayton    def get_uint16(self, fail_value=0):
916a23d212SGreg Clayton        '''Extract a single uint16_t from the binary file at the current file position, returns a single integer'''
926a23d212SGreg Clayton        s = self.read_size(2)
936a23d212SGreg Clayton        if s:
946a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'H', s)
956a23d212SGreg Clayton            return v
966a23d212SGreg Clayton        else:
976a23d212SGreg Clayton            return fail_value
986a23d212SGreg Clayton
996a23d212SGreg Clayton    def get_sint32(self, fail_value=0):
1006a23d212SGreg Clayton        '''Extract a single int32_t from the binary file at the current file position, returns a single integer'''
1016a23d212SGreg Clayton        s = self.read_size(4)
1026a23d212SGreg Clayton        if s:
1036a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'i', s)
1046a23d212SGreg Clayton            return v
1056a23d212SGreg Clayton        else:
1066a23d212SGreg Clayton            return fail_value
1076a23d212SGreg Clayton
1086a23d212SGreg Clayton    def get_uint32(self, fail_value=0):
1096a23d212SGreg Clayton        '''Extract a single uint32_t from the binary file at the current file position, returns a single integer'''
1106a23d212SGreg Clayton        s = self.read_size(4)
1116a23d212SGreg Clayton        if s:
1126a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'I', s)
1136a23d212SGreg Clayton            return v
1146a23d212SGreg Clayton        else:
1156a23d212SGreg Clayton            return fail_value
1166a23d212SGreg Clayton
1176a23d212SGreg Clayton    def get_sint64(self, fail_value=0):
1186a23d212SGreg Clayton        '''Extract a single int64_t from the binary file at the current file position, returns a single integer'''
1196a23d212SGreg Clayton        s = self.read_size(8)
1206a23d212SGreg Clayton        if s:
1216a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'q', s)
1226a23d212SGreg Clayton            return v
1236a23d212SGreg Clayton        else:
1246a23d212SGreg Clayton            return fail_value
1256a23d212SGreg Clayton
1266a23d212SGreg Clayton    def get_uint64(self, fail_value=0):
1276a23d212SGreg Clayton        '''Extract a single uint64_t from the binary file at the current file position, returns a single integer'''
1286a23d212SGreg Clayton        s = self.read_size(8)
1296a23d212SGreg Clayton        if s:
1306a23d212SGreg Clayton            v, = struct.unpack(self.byte_order + 'Q', s)
1316a23d212SGreg Clayton            return v
1326a23d212SGreg Clayton        else:
1336a23d212SGreg Clayton            return fail_value
1346a23d212SGreg Clayton
135b9c1b51eSKate Stone    def get_fixed_length_c_string(
136b9c1b51eSKate Stone            self,
137b9c1b51eSKate Stone            n,
138b9c1b51eSKate Stone            fail_value='',
139b9c1b51eSKate Stone            isprint_only_with_space_padding=False):
1406a23d212SGreg Clayton        '''Extract a single fixed length C string from the binary file at the current file position, returns a single C string'''
1416a23d212SGreg Clayton        s = self.read_size(n)
1426a23d212SGreg Clayton        if s:
1436a23d212SGreg Clayton            cstr, = struct.unpack(self.byte_order + ("%i" % n) + 's', s)
1446a23d212SGreg Clayton            # Strip trialing NULLs
1456a23d212SGreg Clayton            cstr = string.strip(cstr, "\0")
1466a23d212SGreg Clayton            if isprint_only_with_space_padding:
1476a23d212SGreg Clayton                for c in cstr:
1486a23d212SGreg Clayton                    if c in string.printable or ord(c) == 0:
1496a23d212SGreg Clayton                        continue
1506a23d212SGreg Clayton                    return fail_value
1516a23d212SGreg Clayton            return cstr
1526a23d212SGreg Clayton        else:
1536a23d212SGreg Clayton            return fail_value
1546a23d212SGreg Clayton
1556a23d212SGreg Clayton    def get_c_string(self):
1566a23d212SGreg Clayton        '''Extract a single NULL terminated C string from the binary file at the current file position, returns a single C string'''
1576a23d212SGreg Clayton        cstr = ''
1586a23d212SGreg Clayton        byte = self.get_uint8()
1596a23d212SGreg Clayton        while byte != 0:
1606a23d212SGreg Clayton            cstr += "%c" % byte
1616a23d212SGreg Clayton            byte = self.get_uint8()
1626a23d212SGreg Clayton        return cstr
1636a23d212SGreg Clayton
1646a23d212SGreg Clayton    def get_n_sint8(self, n, fail_value=0):
1656a23d212SGreg Clayton        '''Extract "n" int8_t integers from the binary file at the current file position, returns a list of integers'''
1666a23d212SGreg Clayton        s = self.read_size(n)
1676a23d212SGreg Clayton        if s:
1686a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'b', s)
1696a23d212SGreg Clayton        else:
1706a23d212SGreg Clayton            return (fail_value,) * n
1716a23d212SGreg Clayton
1726a23d212SGreg Clayton    def get_n_uint8(self, n, fail_value=0):
1736a23d212SGreg Clayton        '''Extract "n" uint8_t integers from the binary file at the current file position, returns a list of integers'''
1746a23d212SGreg Clayton        s = self.read_size(n)
1756a23d212SGreg Clayton        if s:
1766a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'B', s)
1776a23d212SGreg Clayton        else:
1786a23d212SGreg Clayton            return (fail_value,) * n
1796a23d212SGreg Clayton
1806a23d212SGreg Clayton    def get_n_sint16(self, n, fail_value=0):
1816a23d212SGreg Clayton        '''Extract "n" int16_t integers from the binary file at the current file position, returns a list of integers'''
1826a23d212SGreg Clayton        s = self.read_size(2 * n)
1836a23d212SGreg Clayton        if s:
1846a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'h', s)
1856a23d212SGreg Clayton        else:
1866a23d212SGreg Clayton            return (fail_value,) * n
1876a23d212SGreg Clayton
1886a23d212SGreg Clayton    def get_n_uint16(self, n, fail_value=0):
1896a23d212SGreg Clayton        '''Extract "n" uint16_t integers from the binary file at the current file position, returns a list of integers'''
1906a23d212SGreg Clayton        s = self.read_size(2 * n)
1916a23d212SGreg Clayton        if s:
1926a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'H', s)
1936a23d212SGreg Clayton        else:
1946a23d212SGreg Clayton            return (fail_value,) * n
1956a23d212SGreg Clayton
1966a23d212SGreg Clayton    def get_n_sint32(self, n, fail_value=0):
1976a23d212SGreg Clayton        '''Extract "n" int32_t integers from the binary file at the current file position, returns a list of integers'''
1986a23d212SGreg Clayton        s = self.read_size(4 * n)
1996a23d212SGreg Clayton        if s:
2006a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'i', s)
2016a23d212SGreg Clayton        else:
2026a23d212SGreg Clayton            return (fail_value,) * n
2036a23d212SGreg Clayton
2046a23d212SGreg Clayton    def get_n_uint32(self, n, fail_value=0):
2056a23d212SGreg Clayton        '''Extract "n" uint32_t integers from the binary file at the current file position, returns a list of integers'''
2066a23d212SGreg Clayton        s = self.read_size(4 * n)
2076a23d212SGreg Clayton        if s:
2086a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'I', s)
2096a23d212SGreg Clayton        else:
2106a23d212SGreg Clayton            return (fail_value,) * n
2116a23d212SGreg Clayton
2126a23d212SGreg Clayton    def get_n_sint64(self, n, fail_value=0):
2136a23d212SGreg Clayton        '''Extract "n" int64_t integers from the binary file at the current file position, returns a list of integers'''
2146a23d212SGreg Clayton        s = self.read_size(8 * n)
2156a23d212SGreg Clayton        if s:
2166a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'q', s)
2176a23d212SGreg Clayton        else:
2186a23d212SGreg Clayton            return (fail_value,) * n
2196a23d212SGreg Clayton
2206a23d212SGreg Clayton    def get_n_uint64(self, n, fail_value=0):
2216a23d212SGreg Clayton        '''Extract "n" uint64_t integers from the binary file at the current file position, returns a list of integers'''
2226a23d212SGreg Clayton        s = self.read_size(8 * n)
2236a23d212SGreg Clayton        if s:
2246a23d212SGreg Clayton            return struct.unpack(self.byte_order + ("%u" % n) + 'Q', s)
2256a23d212SGreg Clayton        else:
2266a23d212SGreg Clayton            return (fail_value,) * n
227