Package pype32 :: Module utils |
[hide private]
[frames] | no frames]

# Source Code for Module pype32.utils

      1  #!/usr/bin/python
      2  # -*- coding: utf-8 -*-
      3
      4  # Copyright (c) 2013, Nahuel Riva
      5  # All rights reserved.
      6  #
      7  # Redistribution and use in source and binary forms, with or without
      8  # modification, are permitted provided that the following conditions are met:
      9  #
     10  #     * Redistributions of source code must retain the above copyright notice,
     11  #       this list of conditions and the following disclaimer.
     12  #     * Redistributions in binary form must reproduce the above copyright
     13  #       notice,this list of conditions and the following disclaimer in the
     14  #       documentation and/or other materials provided with the distribution.
     15  #     * Neither the name of the copyright holder nor the names of its
     16  #       contributors may be used to endorse or promote products derived from
     17  #       this software without specific prior written permission.
     18  #
     19  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     20  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     23  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  # POSSIBILITY OF SUCH DAMAGE.
     30
     31  """
     32  Auxiliary classes and functions.
     33
     34  @group Read/Write data stream objects:
     35      ReadData, WriteData
     36  """
     37
     38  __revision__ = "$Id$"
     39
     40  __all__ = [
     41             "ReadData",
     42             "WriteData",
     43             ]
     44
     45  import excep
     46
     47  from cStringIO import StringIO as cstringio
     48  from StringIO import StringIO
     49  from struct import pack, unpack
     50
     51 -def powerOfTwo(value):
     52      """
     53      Tries to determine if a given value is a power of two.
     54
     55      @type value: int
     56      @param value: Value to test if it is power of two.
     57
     58      @rtype: bool
     59      @return: C{True} if the value is power of two,
     60          C{False} if it doesn't.
     61      """
     62      return value != 0 and (value & (value - 1)) == 0
     63
     64 -def allZero(buffer):
     65      """
     66      Tries to determine if a buffer is empty.
     67
     68      @type buffer: str
     69      @param buffer: Buffer to test if it is empty.
     70
     71      @rtype: bool
     72      @return: C{True} if the given buffer is empty, i.e. full of zeros,
     73          C{False} if it doesn't.
     74      """
     75      allZero = True
     76      for byte in buffer:
     77          if byte != "\x00":
     78              allZero = False
     79              break
     80      return allZero
     81
     82 -class WriteData(object):
     83      """Return a WriteData-like stream object for writing."""
     84 -    def __init__(self, data, endianness = "<", signed = False):
     85          """
     86          @type data: str
     87          @param data: Data to create the L{WriteData} object.
     88
     89          @type endianness: str
     90          @param endianness: (Optional) Indicates the endianness used to write the data. The C{<} indicates little-endian while C{>} indicates big-endian.
     91
     92          @type signed: bool
     93          @param signed: (Optional) If set to C{True} the data will be treated as signed. If set to C{False} it will be treated as unsigned.
     94          """
     95          self.data = StringIO(data)
     96          self.endianness = endianness
     97          self.signed = signed
     98
     99 -    def __len__(self):
    100          return len(self.data.buf[self.data.tell():])
    101
    102 -    def __str__(self):
    103          return self.data.getvalue()
    104
    105 -    def writeByte(self, byte):
    106          """
    107          Writes a byte into the L{WriteData} stream object.
    108
    109          @type byte: int
    110          @param byte: Byte value to write into the stream.
    111          """
    112          self.data.write(pack("B" if not self.signed else "b", byte))
    113
    114 -    def writeWord(self, word):
    115          """
    116          Writes a word value into the L{WriteData} stream object.
    117
    118          @type word: int
    119          @param word: Word value to write into the stream.
    120          """
    121          self.data.write(pack(self.endianness + ("H" if not self.signed else "h"), word))
    122
    123 -    def writeDword(self, dword):
    124          """
    125          Writes a dword value into the L{WriteData} stream object.
    126
    127          @type dword: int
    128          @param dword: Dword value to write into the stream.
    129          """
    130          self.data.write(pack(self.endianness + ("L" if not self.signed else "l"), dword))
    131
    132 -    def writeQword(self, qword):
    133          """
    134          Writes a qword value into the L{WriteData} stream object.
    135
    136          @type qword: int
    137          @param qword: Qword value to write into the stream.
    138          """
    139          self.data.write(pack(self.endianness + ("Q" if not self.signed else "q"),  qword))
    140
    141 -    def write(self, dataToWrite):
    142          """
    143          Writes data into the L{WriteData} stream object.
    144
    145          @type dataToWrite: str
    146          @param dataToWrite: Data to write into the stream.
    147          """
    148          self.data.write(dataToWrite)
    149
    150 -    def setOffset(self, value):
    151          """
    152          Sets the offset of the L{WriteData} stream object in wich the data is written.
    153
    154          @type value: int
    155          @param value: Integer value that represent the offset we want to start writing in the L{WriteData} stream.
    156
    157          @raise WrongOffsetValueException: The value is beyond the total length of the data.
    158          """
    159          if value >= len(self.data.getvalue()):
    160              raise excep.WrongOffsetValueException("Wrong offset value. Must be less than %d" % len(self.data))
    161          self.data.seek(value)
    162
    163 -    def skipBytes(self, nroBytes):
    164          """
    165          Skips the specified number as parameter to the current value of the L{WriteData} stream.
    166
    167          @type nroBytes: int
    168          @param nroBytes: The number of bytes to skip.
    169          """
    170          self.data.seek(nroBytes + self.data.tell())
    171
    172 -    def tell(self):
    173          """
    174          Returns the current position of the offset in the L{WriteData} sream object.
    175
    176          @rtype: int
    177          @return: The value of the current offset in the stream.
    178          """
    179          return self.data.tell()
    180
    181 -    def __del__(self):
    182          self.data.close()
    183          del self.data
    184
    185 -class ReadData(object):
    186      """Returns a ReadData-like stream object."""
    187 -    def __init__(self, data, endianness = "<",  signed = False):
    188          """
    189          @type data: str
    190          @param data: The data from which we want to read.
    191
    192          @type endianness: str
    193          @param endianness: (Optional) Indicates the endianness used to read the data. The C{<} indicates little-endian while C{>} indicates big-endian.
    194
    195          @type signed: bool
    196          @param signed: (Optional) If set to C{True} the data will be treated as signed. If set to C{False} it will be treated as unsigned.
    197          """
    198          self.data = data
    199          self.offset = 0
    200          self.endianness = endianness
    201          self.signed = signed
    202          self.log = False
    203
    204 -    def __len__(self):
    205          return len(self.data[self.offset:])
    206
    207 -    def readDword(self):
    208          """
    209          Reads a dword value from the L{ReadData} stream object.
    210
    211          @rtype: int
    212          @return: The dword value read from the L{ReadData} stream.
    213          """
    214          dword = unpack(self.endianness + ('L' if not self.signed else 'l'), self.readAt(self.offset,  4))[0]
    215          self.offset += 4
    216          return dword
    217
    218 -    def readWord(self):
    219          """
    220          Reads a word value from the L{ReadData} stream object.
    221
    222          @rtype: int
    223          @return: The word value read from the L{ReadData} stream.
    224          """
    225          word = unpack(self.endianness + ('H' if not self.signed else 'h'), self.readAt(self.offset, 2))[0]
    226          self.offset += 2
    227          return word
    228
    229 -    def readByte(self):
    230          """
    231          Reads a byte value from the L{ReadData} stream object.
    232
    233          @rtype: int
    234          @return: The byte value read from the L{ReadData} stream.
    235          """
    236          byte = unpack('B' if not self.signed else 'b', self.readAt(self.offset,  1))[0]
    237          self.offset += 1
    238          return byte
    239
    240 -    def readQword(self):
    241          """
    242          Reads a qword value from the L{ReadData} stream object.
    243
    244          @rtype: int
    245          @return: The qword value read from the L{ReadData} stream.
    246          """
    247          qword = unpack(self.endianness + ('Q' if not self.signed else 'b'),  self.readAt(self.offset, 8))[0]
    248          self.offset += 8
    249          return qword
    250
    251 -    def readString(self):
    252          """
    253          Reads an ASCII string from the L{ReadData} stream object.
    254
    255          @rtype: str
    256          @return: An ASCII string read form the stream.
    257          """
    258          resultStr = ""
    259          while self.data[self.offset] != "\x00":
    260              resultStr += self.data[self.offset]
    261              self.offset += 1
    262          return resultStr
    263
    264 -    def readAlignedString(self, align = 4):
    265          """
    266          Reads an ASCII string aligned to the next align-bytes boundary.
    267
    268          @type align: int
    269          @param align: (Optional) The value we want the ASCII string to be aligned.
    270
    271          @rtype: str
    272          @return: A 4-bytes aligned (default) ASCII string.
    273          """
    274          s = self.readString()
    275          r = align - len(s) % align
    276          while r:
    277              s += self.data[self.offset]
    278              self.offset += 1
    279              r -= 1
    280          return s
    281
    282 -    def read(self, nroBytes):
    283          """
    284          Reads data from the L{ReadData} stream object.
    285
    286          @type nroBytes: int
    287          @param nroBytes: The number of bytes to read.
    288
    289          @rtype: str
    290          @return: A string containing the read data from the L{ReadData} stream object.
    291
    292          @raise DataLengthException: The number of bytes tried to be read are more than the remaining in the L{ReadData} stream.
    293          """
    294          if nroBytes > len(self.data[self.offset:]):
    295              if self.log:
    296                  print "Warning: Trying to read: %d bytes - only %d bytes left" % (nroBytes,  len(self.data[self.offset:]))
    297              nroBytes = len(self.data[self.offset:])
    298
    299          resultStr = self.data[self.offset:self.offset + nroBytes]
    300          self.offset += nroBytes
    301          return resultStr
    302
    303 -    def skipBytes(self, nroBytes):
    304          """
    305          Skips the specified number as parameter to the current value of the L{ReadData} stream.
    306
    307          @type nroBytes: int
    308          @param nroBytes: The number of bytes to skip.
    309          """
    310          self.offset += nroBytes
    311
    312 -    def setOffset(self, value):
    313          """
    314          Sets the offset of the L{ReadData} stream object in wich the data is read.
    315
    316          @type value: int
    317          @param value: Integer value that represent the offset we want to start reading in the L{ReadData} stream.
    318
    319          @raise WrongOffsetValueException: The value is beyond the total length of the data.
    320          """
    321          #if value >= len(self.data):
    322          #    raise excep.WrongOffsetValueException("Wrong offset value. Must be less than %d" % len(self.data))
    323          self.offset = value
    324
    325 -    def readAt(self, offset, size):
    326          """
    327          Reads as many bytes indicated in the size parameter at the specific offset.
    328
    329          @type offset: int
    330          @param offset: Offset of the value to be read.
    331
    332          @type size: int
    333          @param size: This parameter indicates how many bytes are going to be read from a given offset.
    334
    335          @rtype: str
    336          @return: A packed string containing the read data.
    337          """
    338          if offset > len(self.data):
    339              if self.log:
    340                  print "Warning: Trying to read: %d bytes - only %d bytes left" % (nroBytes,  len(self.data[self.offset:]))
    341              offset = len(self.data[self.offset:])
    342          tmpOff = self.tell()
    343          self.setOffset(offset)
    344          r = self.read(size)
    345          self.setOffset(tmpOff)
    346          return r
    347
    348 -    def tell(self):
    349          """
    350          Returns the current position of the offset in the L{ReadData} sream object.
    351
    352          @rtype: int
    353          @return: The value of the current offset in the stream.
    354          """
    355          return self.offset
    356