ooo-build r14877 - in trunk: . scratch/sd-pptutil scratch/sd-pptutil/src



Author: thorstenb
Date: Tue Dec 16 23:02:29 2008
New Revision: 14877
URL: http://svn.gnome.org/viewvc/ooo-build?rev=14877&view=rev

Log:
    * scratch/sd-pptutil/*: blatant rip-off from Kohei's binary xls
    dumper; this here is a very bare-bones PPT one (whose src/*
    subdir I better consolidate with Kohei's ...)



Added:
   trunk/scratch/sd-pptutil/
   trunk/scratch/sd-pptutil/ppt-dump.py   (contents, props changed)
   trunk/scratch/sd-pptutil/src/
   trunk/scratch/sd-pptutil/src/globals.py
   trunk/scratch/sd-pptutil/src/ole.py
   trunk/scratch/sd-pptutil/src/record.py
   trunk/scratch/sd-pptutil/src/stream.py
Modified:
   trunk/ChangeLog

Added: trunk/scratch/sd-pptutil/ppt-dump.py
==============================================================================
--- (empty file)
+++ trunk/scratch/sd-pptutil/ppt-dump.py	Tue Dec 16 23:02:29 2008
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+import sys, os.path, getopt
+sys.path.append(sys.path[0]+"/src")
+import ole, stream, globals
+
+from globals import error
+
+def usage (exname):
+    exname = os.path.basename(exname)
+    msg = """Usage: %s [options] [ppt file]
+
+Options:
+  --help        displays this help message.
+"""%exname
+    print msg
+
+
+class PPTDumper(object):
+
+    def __init__ (self, filepath, params):
+        self.filepath = filepath
+        self.params = params
+
+    def __printDirHeader (self, dirname, byteLen):
+        dirname = globals.decodeName(dirname)
+        print("")
+        print("="*68)
+        print("%s (size: %d bytes)"%(dirname, byteLen))
+        print("-"*68)
+
+    def dump (self):
+        file = open(self.filepath, 'rb')
+        strm = stream.PPTStream(file.read(), self.params)
+        file.close()
+        strm.printStreamInfo()
+        strm.printHeader()
+        strm.printDirectory()
+        dirnames = strm.getDirectoryNames()
+        for dirname in dirnames:
+            if len(dirname) == 0 or dirname == 'Root Entry':
+                continue
+
+            dirstrm = strm.getDirectoryStreamByName(dirname)
+            self.__printDirHeader(dirname, len(dirstrm.bytes))
+            if  dirname == "Revision Log":
+                self.__readSubStream(dirstrm)
+            else:
+                globals.dumpBytes(dirstrm.bytes, 512)
+
+    def __readSubStream (self, strm):
+        try:
+            # read bytes from BOF to EOF.
+            header = 0x0000
+            while header != 0x000A:
+                header = strm.readRecord()
+            return True
+        except stream.EndOfStream:
+            return False
+
+
+def main (args):
+    exname, args = args[0], args[1:]
+    if len(args) < 1:
+        print("takes at least one argument")
+        usage(exname)
+        return
+
+    params = globals.Params()
+    try:
+        opts, args = getopt.getopt(args, "h", ["help", "debug", "show-sector-chain"])
+        for opt, arg in opts:
+            if opt in ['-h', '--help']:
+                usage(exname)
+                return
+            elif opt in ['--debug']:
+                params.debug = True
+            elif opt in ['--show-sector-chain']:
+                params.showSectorChain = True
+            else:
+                error("unknown option %s\n"%opt)
+                usage()
+
+    except getopt.GetoptError:
+        error("error parsing input options\n")
+        usage(exname)
+        return
+
+    dumper = PPTDumper(args[0], params)
+    dumper.dump()
+
+if __name__ == '__main__':
+    main(sys.argv)

Added: trunk/scratch/sd-pptutil/src/globals.py
==============================================================================
--- (empty file)
+++ trunk/scratch/sd-pptutil/src/globals.py	Tue Dec 16 23:02:29 2008
@@ -0,0 +1,220 @@
+
+import sys, struct, math
+
+class ByteConvertError(Exception): pass
+
+
+class Params(object):
+    """command-line parameters."""
+    def __init__ (self):
+        self.debug = False
+        self.showSectorChain = False
+
+
+def output (msg):
+    sys.stdout.write(msg)
+
+def error (msg):
+    sys.stderr.write("Error: " + msg)
+
+
+def decodeName (name):
+    """decode name that contains unprintable characters."""
+
+    n = len(name)
+    if n == 0:
+        return name
+
+    newname = ''
+    for i in xrange(0, n):
+        if ord(name[i]) <= 20:
+            newname += "<%2.2Xh>"%ord(name[i])
+        else:
+            newname += name[i]
+
+    return newname
+
+
+def getRichText (bytes, textLen=None):
+    """parse a string of the rich-text format that Excel uses."""
+
+    flags = bytes[0]
+    if type(flags) == type('c'):
+        flags = ord(flags)
+    is16Bit   = (flags & 0x01)
+    isFarEast = (flags & 0x04)
+    isRich    = (flags & 0x08)
+
+    i = 1
+    formatRuns = 0
+    if isRich:
+        formatRuns = getSignedInt(bytes[i:i+2])
+        i += 2
+
+    extInfo = 0
+    if isFarEast:
+        extInfo = getSignedInt(bytes[i:i+4])
+        i += 4
+
+    extraBytes = 0
+    if textLen == None:
+        extraBytes = formatRuns*4 + extInfo
+        textLen = len(bytes) - extraBytes - i
+
+    totalByteLen = i + textLen + extraBytes
+    if is16Bit:
+        return ("<16-bit strings not supported yet>", totalByteLen)
+
+    text = toTextBytes(bytes[i:i+textLen])
+    return (text, totalByteLen)
+
+
+def dumpBytes (chars, subDivide=None):
+    line = 0
+    subDivideLine = None
+    if subDivide != None:
+        subDivideLine = subDivide/16
+
+    charLen = len(chars)
+    if charLen == 0:
+        # no bytes to dump.
+        return
+
+    labelWidth = int(math.ceil(math.log(charLen, 10)))
+    flushBytes = False
+    for i in xrange(0, charLen):
+        if (i+1)%16 == 1:
+            # print line header with seek position
+            fmt = "%%%d.%dd: "%(labelWidth, labelWidth)
+            output(fmt%i)
+
+        byte = ord(chars[i])
+        output("%2.2X "%byte)
+        flushBytes = True
+
+        if (i+1)%4 == 0:
+            # put extra space at every 4 bytes.
+            output(" ")
+
+        if (i+1)%16 == 0:
+            output("\n")
+            flushBytes = False
+            if subDivideLine != None and (line+1)%subDivideLine == 0:
+                output("\n")
+            line += 1
+
+    if flushBytes:
+        output("\n")
+
+
+def getSectorPos (secID, secSize):
+    return 512 + secID*secSize
+
+
+def getRawBytes (bytes, spaced=True, reverse=True):
+    text = ''
+    for b in bytes:
+        if type(b) == type(''):
+            b = ord(b)
+        if len(text) == 0:
+            text = "%2.2X"%b
+        elif spaced:
+            if reverse:
+                text = "%2.2X "%b + text
+            else:
+                text += " %2.2X"%b
+        else:
+            if reverse:
+                text = "%2.2X"%b + text
+            else:
+                text += "%2.2X"%b
+    return text
+
+
+def getTextBytes (bytes):
+    return toTextBytes(bytes)
+
+
+def toTextBytes (bytes):
+    n = len(bytes)
+    text = ''
+    for i in xrange(0, n):
+        b = bytes[i]
+        if type(b) == type(0x00):
+            b = struct.pack('B', b)
+        text += b
+    return text
+
+
+def getSignedInt (bytes):
+    # little endian
+    n = len(bytes)
+    if n == 0:
+        return 0
+
+    text = toTextBytes(bytes)
+    if n == 1:
+        # byte - 1 byte
+        return struct.unpack('b', text)[0]
+    elif n == 2:
+        # short - 2 bytes
+        return struct.unpack('<h', text)[0]
+    elif n == 4:
+        # int, long - 4 bytes
+        return struct.unpack('<l', text)[0]
+
+    raise ByteConvertError
+
+
+def getUnsignedInt (bytes):
+    # little endian
+    n = len(bytes)
+    if n == 0:
+        return 0
+
+    text = toTextBytes(bytes)
+    if n == 1:
+        # byte - 1 byte
+        return struct.unpack('B', text)[0]
+    elif n == 2:
+        # short - 2 bytes
+        return struct.unpack('<H', text)[0]
+    elif n == 4:
+        # int, long - 4 bytes
+        return struct.unpack('<L', text)[0]
+
+    raise ByteConvertError
+
+
+def getFloat (bytes):
+    n = len(bytes)
+    if n == 0:
+        return 0.0
+
+    text = toTextBytes(bytes)
+    return struct.unpack('<f', text)[0]
+
+
+def getDouble (bytes):
+    n = len(bytes)
+    if n == 0:
+        return 0.0
+
+    text = toTextBytes(bytes)
+    return struct.unpack('<d', text)[0]
+
+
+def getUTF8FromUTF16 (bytes):
+    # little endian utf-16 strings
+    byteCount = len(bytes)
+    loopCount = int(byteCount/2)
+    text = ''
+    for i in xrange(0, loopCount):
+        code = ''
+        if bytes[i*2+1] != '\x00':
+            code += bytes[i*2+1]
+        if bytes[i*2] != '\x00':
+            code += bytes[i*2]
+        text += unicode(code, 'utf-8')
+    return text
+

Added: trunk/scratch/sd-pptutil/src/ole.py
==============================================================================
--- (empty file)
+++ trunk/scratch/sd-pptutil/src/ole.py	Tue Dec 16 23:02:29 2008
@@ -0,0 +1,732 @@
+
+import sys
+import stream, globals
+from globals import getSignedInt
+# ----------------------------------------------------------------------------
+# Reference: The Microsoft Compound Document File Format by Daniel Rentz
+# http://sc.openoffice.org/compdocfileformat.pdf
+# ----------------------------------------------------------------------------
+
+from globals import output
+
+
+class NoRootStorage(Exception): pass
+
+class ByteOrder:
+    LittleEndian = 0
+    BigEndian    = 1
+    Unknown      = 2
+
+class BlockType:
+    MSAT      = 0
+    SAT       = 1
+    SSAT      = 2
+    Directory = 3
+
+class StreamLocation:
+    SAT  = 0
+    SSAT = 1
+
+class Header(object):
+
+    @staticmethod
+    def byteOrder (chars):
+        b1, b2 = ord(chars[0]), ord(chars[1])
+        if b1 == 0xFE and b2 == 0xFF:
+            return ByteOrder.LittleEndian
+        elif b1 == 0xFF and b2 == 0xFE:
+            return ByteOrder.BigEndian
+        else:
+            return ByteOrder.Unknown
+
+
+    def __init__ (self, bytes, params):
+        self.bytes = bytes
+        self.MSAT = None
+
+        self.docId = None
+        self.uId = None
+        self.revision = 0
+        self.version = 0
+        self.byteOrder = ByteOrder.Unknown
+        self.minStreamSize = 0
+
+        self.numSecMSAT = 0
+        self.numSecSSAT = 0
+        self.numSecSAT = 0
+
+        self.__secIDFirstMSAT = -2
+        self.__secIDFirstDirStrm = -2
+        self.__secIDFirstSSAT = -2
+
+        self.secSize = 512
+        self.secSizeShort = 64
+
+        self.params = params
+
+    def getSectorSize (self):
+        return 2**self.secSize
+
+
+    def getShortSectorSize (self):
+        return 2**self.secSizeShort
+
+
+    def getFirstSectorID (self, blockType):
+        if blockType == BlockType.MSAT:
+            return self.__secIDFirstMSAT
+        elif blockType == BlockType.SSAT:
+            return self.__secIDFirstSSAT
+        elif blockType == BlockType.Directory:
+            return self.__secIDFirstDirStrm
+        return -2
+
+
+    def output (self):
+
+        def printRawBytes (bytes):
+            for b in bytes:
+                output("%2.2X "%ord(b))
+            output("\n")
+
+        def printSep (c='-', w=68, prefix=''):
+            print(prefix + c*w)
+
+        printSep('=', 68)
+        print("Compound Document Header")
+        printSep('-', 68)
+
+        if self.params.debug:
+            globals.dumpBytes(self.bytes[0:512])
+            printSep('-', 68)
+
+        # document ID and unique ID
+        output("Document ID: ")
+        printRawBytes(self.docId)
+        output("Unique ID: ")
+        printRawBytes(self.uId)
+
+        # revision and version
+        print("Revision: %d  Version: %d"%(self.revision, self.version))
+
+        # byte order
+        output("Byte order: ")
+        if self.byteOrder == ByteOrder.LittleEndian:
+            print("little endian")
+        elif self.byteOrder == ByteOrder.BigEndian:
+            print("big endian")
+        else:
+            print("unknown")
+
+        # sector size (usually 512 bytes)
+        print("Sector size: %d (%d)"%(2**self.secSize, self.secSize))
+
+        # short sector size (usually 64 bytes)
+        print("Short sector size: %d (%d)"%(2**self.secSizeShort, self.secSizeShort))
+
+        # total number of sectors in SAT (equals the number of sector IDs 
+        # stored in the MSAT).
+        print("Total number of sectors used in SAT: %d"%self.numSecSAT)
+
+        print("Sector ID of the first sector of the directory stream: %d"%
+              self.__secIDFirstDirStrm)
+
+        print("Minimum stream size: %d"%self.minStreamSize)
+
+        if self.__secIDFirstSSAT == -2:
+            print("Sector ID of the first SSAT sector: [none]")
+        else:
+            print("Sector ID of the first SSAT sector: %d"%self.__secIDFirstSSAT)
+
+        print("Total number of sectors used in SSAT: %d"%self.numSecSSAT)
+
+        if self.__secIDFirstMSAT == -2:
+            # There is no more sector ID stored outside the header.
+            print("Sector ID of the first MSAT sector: [end of chain]")
+        else:
+            # There is more sector IDs than 109 IDs stored in the header.
+            print("Sector ID of the first MSAT sector: %d"%(self.__secIDFirstMSAT))
+
+        print("Total number of sectors used to store additional MSAT: %d"%self.numSecMSAT)
+
+
+    def parse (self):
+
+        # document ID and unique ID
+        self.docId = self.bytes[0:8]
+        self.uId = self.bytes[8:24]
+
+        # revision and version
+        self.revision = getSignedInt(self.bytes[24:26])
+        self.version = getSignedInt(self.bytes[26:28])
+
+        # byte order
+        self.byteOrder = Header.byteOrder(self.bytes[28:30])
+
+        # sector size (usually 512 bytes)
+        self.secSize = getSignedInt(self.bytes[30:32])
+
+        # short sector size (usually 64 bytes)
+        self.secSizeShort = getSignedInt(self.bytes[32:34])
+
+        # total number of sectors in SAT (equals the number of sector IDs 
+        # stored in the MSAT).
+        self.numSecSAT = getSignedInt(self.bytes[44:48])
+
+        self.__secIDFirstDirStrm = getSignedInt(self.bytes[48:52])
+        self.minStreamSize = getSignedInt(self.bytes[56:60])
+        self.__secIDFirstSSAT = getSignedInt(self.bytes[60:64])
+        self.numSecSSAT = getSignedInt(self.bytes[64:68])
+        self.__secIDFirstMSAT = getSignedInt(self.bytes[68:72])
+        self.numSecMSAT = getSignedInt(self.bytes[72:76])
+
+        # master sector allocation table
+        self.MSAT = MSAT(2**self.secSize, self.bytes, self.params)
+
+        # First part of MSAT consisting of an array of up to 109 sector IDs.
+        # Each sector ID is 4 bytes in length.
+        for i in xrange(0, 109):
+            pos = 76 + i*4
+            id = getSignedInt(self.bytes[pos:pos+4])
+            if id == -1:
+                break
+
+            self.MSAT.appendSectorID(id)
+
+        if self.__secIDFirstMSAT != -2:
+            # additional sectors are used to store more SAT sector IDs.
+            secID = self.__secIDFirstMSAT
+            size = self.getSectorSize()
+            inLoop = True
+            while inLoop:
+                pos = 512 + secID*size
+                bytes = self.bytes[pos:pos+size]
+                n = int(size/4)
+                for i in xrange(0, n):
+                    pos = i*4
+                    id = getSignedInt(bytes[pos:pos+4])
+                    if id < 0:
+                        inLoop = False
+                        break
+                    elif i == n-1:
+                        # last sector ID - points to the next MSAT sector.
+                        secID = id
+                        break
+                    else:
+                        self.MSAT.appendSectorID(id)
+
+        return 512 
+
+
+    def getMSAT (self):
+        return self.MSAT
+
+
+    def getSAT (self):
+        return self.MSAT.getSAT()
+
+
+    def getSSAT (self):
+        ssatID = self.getFirstSectorID(BlockType.SSAT)
+        if ssatID < 0:
+            return None
+        chain = self.getSAT().getSectorIDChain(ssatID)
+        if len(chain) == 0:
+            return None
+        obj = SSAT(2**self.secSize, self.bytes, self.params)
+        for secID in chain:
+            obj.addSector(secID)
+        obj.buildArray()
+        return obj
+
+
+    def getDirectory (self):
+        dirID = self.getFirstSectorID(BlockType.Directory)
+        if dirID < 0:
+            return None
+        chain = self.getSAT().getSectorIDChain(dirID)
+        if len(chain) == 0:
+            return None
+        obj = Directory(self, self.params)
+        for secID in chain:
+            obj.addSector(secID)
+        return obj
+
+
+    def dummy ():
+        pass
+
+
+
+
+class MSAT(object):
+    """Master Sector Allocation Table (MSAT)
+
+This class represents the master sector allocation table (MSAT) that stores 
+sector IDs that point to all the sectors that are used by the sector 
+allocation table (SAT).  The actual SAT are to be constructed by combining 
+all the sectors pointed by the sector IDs in order of occurrence.
+"""
+    def __init__ (self, sectorSize, bytes, params):
+        self.sectorSize = sectorSize
+        self.secIDs = []
+        self.bytes = bytes
+        self.__SAT = None
+
+        self.params = params
+
+    def appendSectorID (self, id):
+        self.secIDs.append(id)
+
+    def output (self):
+        print('')
+        print("="*68)
+        print("Master Sector Allocation Table (MSAT)")
+        print("-"*68)
+
+        for id in self.secIDs:
+            print("sector ID: %5d   (pos: %7d)"%(id, 512+id*self.sectorSize))
+
+    def getSATSectorPosList (self):
+        list = []
+        for id in self.secIDs:
+            pos = 512 + id*self.sectorSize
+            list.append([id, pos])
+        return list
+
+    def getSAT (self):
+        if self.__SAT != None:
+            return self.__SAT
+
+        obj = SAT(self.sectorSize, self.bytes, self.params)
+        for id in self.secIDs:
+            obj.addSector(id)
+        obj.buildArray()
+        self.__SAT = obj
+        return self.__SAT
+
+
+class SAT(object):
+    """Sector Allocation Table (SAT)
+"""
+    def __init__ (self, sectorSize, bytes, params):
+        self.sectorSize = sectorSize
+        self.sectorIDs = []
+        self.bytes = bytes
+        self.array = []
+
+        self.params = params
+
+
+    def getSectorSize (self):
+        return self.sectorSize
+
+
+    def addSector (self, id):
+        self.sectorIDs.append(id)
+
+
+    def buildArray (self):
+        if len(self.array) > 0:
+            # array already built.
+            return
+
+        numItems = int(self.sectorSize/4)
+        self.array = []
+        for secID in self.sectorIDs:
+            pos = 512 + secID*self.sectorSize
+            for i in xrange(0, numItems):
+                beginPos = pos + i*4
+                id = getSignedInt(self.bytes[beginPos:beginPos+4])
+                self.array.append(id)
+
+
+    def outputRawBytes (self):
+        bytes = []
+        for secID in self.sectorIDs:
+            pos = 512 + secID*self.sectorSize
+            bytes.extend(self.bytes[pos:pos+self.sectorSize])
+        globals.dumpBytes(bytes, 512)
+
+
+    def outputArrayStats (self):
+        sectorTotal = len(self.array)
+        sectorP  = 0       # >= 0
+        sectorM1 = 0       # -1
+        sectorM2 = 0       # -2
+        sectorM3 = 0       # -3
+        sectorM4 = 0       # -4
+        sectorMElse = 0    # < -4
+        sectorLiveTotal = 0
+        for i in xrange(0, len(self.array)):
+            item = self.array[i]
+            if item >= 0:
+                sectorP += 1
+            elif item == -1:
+                sectorM1 += 1
+            elif item == -2:
+                sectorM2 += 1
+            elif item == -3:
+                sectorM3 += 1
+            elif item == -4:
+                sectorM4 += 1
+            elif item < -4:
+                sectorMElse += 1
+            else:
+                sectorLiveTotal += 1
+        print("total sector count:          %4d"%sectorTotal)
+        print("* live sector count:         %4d"%sectorP)
+        print("* end-of-chain sector count: %4d"%sectorM2)  # end-of-chain is also live
+
+        print("* free sector count:         %4d"%sectorM1)
+        print("* SAT sector count:          %4d"%sectorM3)
+        print("* MSAT sector count:         %4d"%sectorM4)
+        print("* other sector count:        %4d"%sectorMElse)
+
+
+    def output (self):
+        print('')
+        print("="*68)
+        print("Sector Allocation Table (SAT)")
+        print("-"*68)
+        if self.params.debug:
+            self.outputRawBytes()
+            print("-"*68)
+            for i in xrange(0, len(self.array)):
+                print("%5d: %5d"%(i, self.array[i]))
+            print("-"*68)
+
+        self.outputArrayStats()
+
+
+    def getSectorIDChain (self, initID):
+        if initID < 0:
+            return []
+        chain = [initID]
+        nextID = self.array[initID]
+        while nextID != -2:
+            chain.append(nextID)
+            nextID = self.array[nextID]
+        return chain
+
+
+class SSAT(SAT):
+    """Short Sector Allocation Table (SSAT)
+
+SSAT contains an array of sector ID chains of all short streams, as oppposed 
+to SAT which contains an array of sector ID chains of all standard streams.
+The sector IDs included in the SSAT point to the short sectors in the short
+stream container stream.
+
+The first sector ID of SSAT is in the header, and the IDs of the remaining 
+sectors are contained in the SAT as a sector ID chain.
+"""
+
+    def output (self):
+        print('')
+        print("="*68)
+        print("Short Sector Allocation Table (SSAT)")
+        print("-"*68)
+        if self.params.debug:
+            self.outputRawBytes()
+            print("-"*68)
+            for i in xrange(0, len(self.array)):
+                item = self.array[i]
+                output("%3d : %3d\n"%(i, item))
+
+        self.outputArrayStats()
+
+
+class Directory(object):
+    """Directory Entries
+
+This stream contains a list of directory entries that are stored within the
+entire file stream.
+"""
+
+    class Type:
+        Empty = 0
+        UserStorage = 1
+        UserStream = 2
+        LockBytes = 3
+        Property = 4
+        RootStorage = 5
+
+    class NodeColor:
+        Red = 0
+        Black = 1
+        Unknown = 99
+        
+    class Entry:
+        def __init__ (self):
+            self.Name = ''
+            self.CharBufferSize = 0
+            self.Type = Directory.Type.Empty
+            self.NodeColor = Directory.NodeColor.Unknown
+            self.DirIDLeft = -1
+            self.DirIDRight = -1
+            self.DirIDRoot = -1
+            self.UniqueID = None
+            self.UserFlags = None
+            self.TimeCreated = None
+            self.TimeModified = None
+            self.StreamSectorID = -2
+            self.StreamSize = 0
+            self.bytes = []
+
+
+    def __init__ (self, header, params):
+        self.sectorSize = header.getSectorSize()
+        self.bytes = header.bytes
+        self.minStreamSize = header.minStreamSize
+        self.sectorIDs = []
+        self.entries = []
+        self.SAT = header.getSAT()
+        self.SSAT = header.getSSAT()
+        self.header = header
+        self.RootStorage = None
+        self.RootStorageBytes = []
+        self.params = params
+
+
+    def __buildRootStorageBytes (self):
+        if self.RootStorage == None:
+            # no root storage exists.
+            return
+
+        firstSecID = self.RootStorage.StreamSectorID
+        chain = self.header.getSAT().getSectorIDChain(firstSecID)
+        for secID in chain:
+            pos = 512 + secID*self.sectorSize
+            self.RootStorageBytes.extend(self.header.bytes[pos:pos+self.sectorSize])
+
+
+    def __getRawStream (self, entry):
+        chain = []
+        if entry.StreamLocation == StreamLocation.SAT:
+            chain = self.header.getSAT().getSectorIDChain(entry.StreamSectorID)
+        elif entry.StreamLocation == StreamLocation.SSAT:
+            chain = self.header.getSSAT().getSectorIDChain(entry.StreamSectorID)
+
+
+        if entry.StreamLocation == StreamLocation.SSAT:
+            # Get the root storage stream.
+            if self.RootStorage == None:
+                raise NoRootStorage
+
+            bytes = []
+            self.__buildRootStorageBytes()
+            size = self.header.getShortSectorSize()
+            for id in chain:
+                pos = id*size
+                bytes.extend(self.RootStorageBytes[pos:pos+size])
+            return bytes
+
+        offset = 512
+        size = self.header.getSectorSize()
+        bytes = []
+        for id in chain:
+            pos = offset + id*size
+            bytes.extend(self.header.bytes[pos:pos+size])
+
+        return bytes
+
+
+    def getRawStreamByName (self, name):
+        bytes = []
+        for entry in self.entries:
+            if entry.Name == name:
+                bytes = self.__getRawStream(entry)
+                break
+        return bytes
+
+
+    def addSector (self, id):
+        self.sectorIDs.append(id)
+
+
+    def output (self, debug=False):
+        print('')
+        print("="*68)
+        print("Directory")
+
+        if debug:
+            print("-"*68)
+            print("sector(s) used:")
+            for secID in self.sectorIDs:
+                print("  sector %d"%secID)
+
+            print("")
+            for secID in self.sectorIDs:
+                print("-"*68)
+                print("  Raw Hex Dump (sector %d)"%secID)
+                print("-"*68)
+                pos = globals.getSectorPos(secID, self.sectorSize)
+                globals.dumpBytes(self.bytes[pos:pos+self.sectorSize], 128)
+
+        for entry in self.entries:
+            self.__outputEntry(entry, debug)
+
+    def __outputEntry (self, entry, debug):
+        print("-"*68)
+        if len(entry.Name) > 0:
+            name = entry.Name
+            if ord(name[0]) <= 5:
+                name = "<%2.2Xh>%s"%(ord(name[0]), name[1:])
+            print("name: %s   (name buffer size: %d bytes)"%(name, entry.CharBufferSize))
+        else:
+            print("name: [empty]   (name buffer size: %d bytes)"%entry.CharBufferSize)
+
+        if self.params.debug:
+            print("-"*68)
+            globals.dumpBytes(entry.bytes)
+            print("-"*68)
+
+        output("type: ")
+        if entry.Type == Directory.Type.Empty:
+            print("empty")
+        elif entry.Type == Directory.Type.LockBytes:
+            print("lock bytes")
+        elif entry.Type == Directory.Type.Property:
+            print("property")
+        elif entry.Type == Directory.Type.RootStorage:
+            print("root storage")
+        elif entry.Type == Directory.Type.UserStorage:
+            print("user storage")
+        elif entry.Type == Directory.Type.UserStream:
+            print("user stream")
+        else:
+            print("[unknown type]")
+
+        output("node color: ")
+        if entry.NodeColor == Directory.NodeColor.Red:
+            print("red")
+        elif entry.NodeColor == Directory.NodeColor.Black:
+            print("black")
+        elif entry.NodeColor == Directory.NodeColor.Unknown:
+            print("[unknown color]")
+
+        print("linked dir entries: left: %d; right: %d; root: %d"%
+              (entry.DirIDLeft, entry.DirIDRight, entry.DirIDRoot))
+
+        self.__outputRaw("unique ID",  entry.UniqueID)
+        self.__outputRaw("user flags", entry.UserFlags)
+        self.__outputRaw("time created", entry.TimeCreated)
+        self.__outputRaw("time last modified", entry.TimeModified)
+
+        output("stream info: ")
+        if entry.StreamSectorID < 0:
+            print("[empty stream]")
+        else:
+            strmLoc = "SAT"
+            if entry.StreamLocation == StreamLocation.SSAT:
+                strmLoc = "SSAT"
+            print("(first sector ID: %d; size: %d; location: %s)"%
+                  (entry.StreamSectorID, entry.StreamSize, strmLoc))
+    
+            satObj = None
+            secSize = 0
+            if entry.StreamLocation == StreamLocation.SAT:
+                satObj = self.SAT
+                secSize = self.header.getSectorSize()
+            elif entry.StreamLocation == StreamLocation.SSAT:
+                satObj = self.SSAT
+                secSize = self.header.getShortSectorSize()
+            if satObj != None:
+                chain = satObj.getSectorIDChain(entry.StreamSectorID)
+                print("sector count: %d"%len(chain))
+                print("total sector size: %d"%(len(chain)*secSize))
+                if self.params.showSectorChain:
+                    self.__outputSectorChain(chain)
+
+
+    def __outputSectorChain (self, chain):
+        line = "sector chain: "
+        lineLen = len(line)
+        for id in chain:
+            frag = "%d, "%id
+            fragLen = len(frag)
+            if lineLen + fragLen > 68:
+                print(line)
+                line = frag
+                lineLen = fragLen
+            else:
+                line += frag
+                lineLen += fragLen
+        if line[-2:] == ", ":
+            line = line[:-2]
+            lineLen -= 2
+        if lineLen > 0:
+            print(line)
+
+
+    def __outputRaw (self, name, bytes):
+        if bytes == None:
+            return
+
+        output("%s: "%name)
+        for byte in bytes:
+            output("%2.2X "%ord(byte))
+        print("")
+
+
+    def getDirectoryNames (self):
+        names = []
+        for entry in self.entries:
+            names.append(entry.Name)
+        return names
+
+
+    def parseDirEntries (self):
+        if len(self.entries):
+            # directory entries already built
+            return
+
+        # combine all sectors first.
+        bytes = []
+        for secID in self.sectorIDs:
+            pos = globals.getSectorPos(secID, self.sectorSize)
+            bytes.extend(self.bytes[pos:pos+self.sectorSize])
+
+        self.entries = []
+
+        # each directory entry is exactly 128 bytes.
+        numEntries = int(len(bytes)/128)
+        if numEntries == 0:
+            return
+        for i in xrange(0, numEntries):
+            pos = i*128
+            self.entries.append(self.parseDirEntry(bytes[pos:pos+128]))
+
+
+    def parseDirEntry (self, bytes):
+        entry = Directory.Entry()
+        entry.bytes = bytes
+        name = globals.getUTF8FromUTF16(bytes[0:64])
+        entry.Name = name
+        entry.CharBufferSize = getSignedInt(bytes[64:66])
+        entry.Type = getSignedInt(bytes[66:67])
+        entry.NodeColor = getSignedInt(bytes[67:68])
+
+        entry.DirIDLeft  = getSignedInt(bytes[68:72])
+        entry.DirIDRight = getSignedInt(bytes[72:76])
+        entry.DirIDRoot  = getSignedInt(bytes[76:80])
+
+        entry.UniqueID     = bytes[80:96]
+        entry.UserFlags    = bytes[96:100]
+        entry.TimeCreated  = bytes[100:108]
+        entry.TimeModified = bytes[108:116]
+
+        entry.StreamSectorID = getSignedInt(bytes[116:120])
+        entry.StreamSize     = getSignedInt(bytes[120:124])
+        entry.StreamLocation = StreamLocation.SAT
+        if entry.Type != Directory.Type.RootStorage and \
+            entry.StreamSize < self.header.minStreamSize:
+            entry.StreamLocation = StreamLocation.SSAT
+
+        if entry.Type == Directory.Type.RootStorage and entry.StreamSectorID >= 0:
+            # This is an existing root storage.
+            self.RootStorage = entry
+
+        return entry
+

Added: trunk/scratch/sd-pptutil/src/record.py
==============================================================================
--- (empty file)
+++ trunk/scratch/sd-pptutil/src/record.py	Tue Dec 16 23:02:29 2008
@@ -0,0 +1,81 @@
+
+import struct
+import globals
+
+# -------------------------------------------------------------------
+# record handler classes
+
+class BaseRecordHandler(object):
+
+    def __init__ (self, header, size, bytes, strmData):
+        self.header = header
+        self.size = size
+        self.bytes = bytes
+        self.lines = []
+        self.pos = 0       # current byte position
+
+        self.strmData = strmData
+
+    def parseBytes (self):
+        """Parse the original bytes and generate human readable output.
+
+The derived class should only worry about overwriting this function.  The
+bytes are given as self.bytes, and call self.appendLine([new line]) to
+append a line to be displayed.
+"""
+        pass
+
+    def output (self):
+        self.parseBytes()
+        print("%4.4Xh: %s"%(self.header, "-"*61))
+        for line in self.lines:
+            print("%4.4Xh: %s"%(self.header, line))
+
+    def appendLine (self, line):
+        self.lines.append(line)
+
+    def appendLineBoolean (self, name, value):
+        text = "%s: %s"%(name, self.getYesNo(value))
+        self.appendLine(text)
+
+    def readBytes (self, length):
+        r = self.bytes[self.pos:self.pos+length]
+        self.pos += length
+        return r
+
+    def readRemainingBytes (self):
+        r = self.bytes[self.pos:]
+        self.pos = self.size
+        return r
+
+    def getCurrentPos (self):
+        return self.pos
+
+    def setCurrentPos (self, pos):
+        self.pos = pos
+
+    def getYesNo (self, boolVal):
+        if boolVal:
+            return 'yes'
+        else:
+            return 'no'
+
+    def getTrueFalse (self, boolVal):
+        if boolVal:
+            return 'true'
+        else:
+            return 'false'
+
+    def readUnsignedInt (self, length):
+        bytes = self.readBytes(length)
+        return globals.getUnsignedInt(bytes)
+
+    def readSignedInt (self, length):
+        bytes = self.readBytes(length)
+        return globals.getSignedInt(bytes)
+
+    def readDouble (self):
+        # double is always 8 bytes.
+        bytes = self.readBytes(8)
+        return globals.getDouble(bytes)
+

Added: trunk/scratch/sd-pptutil/src/stream.py
==============================================================================
--- (empty file)
+++ trunk/scratch/sd-pptutil/src/stream.py	Tue Dec 16 23:02:29 2008
@@ -0,0 +1,347 @@
+
+import sys
+import ole, globals, record
+from globals import output
+
+class EndOfStream(Exception): pass
+
+    # opcode: [canonical name, handler (optional)]
+
+recData = {
+
+	1:  ["DFF_PST_SubContainerCompleted"],      
+	2:	["DFF_PST_IRRAtom"],                    
+	3:	["DFF_PST_PSS"],                        
+	4:	["DFF_PST_SubContainerException"],      
+	6:	["DFF_PST_ClientSignal1"],              
+	7:	["DFF_PST_ClientSignal2"],              
+   10:	["DFF_PST_PowerPointStateInfoAtom"],    
+ 1000:	["DFF_PST_Document"],                   
+ 1001:	["DFF_PST_DocumentAtom"],               
+ 1002:	["DFF_PST_EndDocument"],                
+ 1003:	["DFF_PST_SlidePersist"],               
+ 1004:	["DFF_PST_SlideBase"],                  
+ 1005:	["DFF_PST_SlideBaseAtom"],              
+ 1006:	["DFF_PST_Slide"],                      
+ 1007:	["DFF_PST_SlideAtom"],                  
+ 1008:	["DFF_PST_Notes"],                      
+ 1009:	["DFF_PST_NotesAtom"],                  
+ 1010:	["DFF_PST_Environment"],                
+ 1011:	["DFF_PST_SlidePersistAtom"],           
+ 1012:	["DFF_PST_Scheme"],                     
+ 1013:	["DFF_PST_SchemeAtom"],                 
+ 1014:	["DFF_PST_DocViewInfo"],                
+ 1015:	["DFF_PST_SslideLayoutAtom"],           
+ 1016:	["DFF_PST_MainMaster"],                 
+ 1017:	["DFF_PST_SSSlideInfoAtom"],            
+ 1018:	["DFF_PST_SlideViewInfo"],              
+ 1019:	["DFF_PST_GuideAtom"],                  
+ 1020:	["DFF_PST_ViewInfo"],                   
+ 1021:	["DFF_PST_ViewInfoAtom"],               
+ 1022:	["DFF_PST_SlideViewInfoAtom"],          
+ 1023:	["DFF_PST_VBAInfo"],                    
+ 1024:	["DFF_PST_VBAInfoAtom"],                
+ 1025:	["DFF_PST_SSDocInfoAtom"],              
+ 1026:	["DFF_PST_Summary"],                    
+ 1027:	["DFF_PST_Texture"],                    
+ 1028:	["DFF_PST_VBASlideInfo"],               
+ 1029:	["DFF_PST_VBASlideInfoAtom"],           
+ 1030:	["DFF_PST_DocRoutingSlip"],             
+ 1031:	["DFF_PST_OutlineViewInfo"],            
+ 1032:	["DFF_PST_SorterViewInfo"],             
+ 1033:	["DFF_PST_ExObjList"],                  
+ 1034:	["DFF_PST_ExObjListAtom"],              
+ 1035:	["DFF_PST_PPDrawingGroup"],             
+ 1036:	["DFF_PST_PPDrawing"],                  
+ 1040:	["DFF_PST_NamedShows"],                 
+ 1041:	["DFF_PST_NamedShow"],                  
+ 1042:	["DFF_PST_NamedShowSlides"],            
+ 1055:	["DFF_PST_RoundTripShapeId"],           
+ 2000:	["DFF_PST_List"],                       
+ 2005:	["DFF_PST_FontCollection"],             
+ 2017:	["DFF_PST_ListPlaceholder"],            
+ 2019:	["DFF_PST_BookmarkCollection"],         
+ 2020:	["DFF_PST_SoundCollection"],            
+ 2021:	["DFF_PST_SoundCollAtom"],              
+ 2022:	["DFF_PST_Sound"],                      
+ 2023:	["DFF_PST_SoundData"],                  
+ 2025:	["DFF_PST_BookmarkSeedAtom"],           
+ 2026:	["DFF_PST_GuideList"],                  
+ 2028:	["DFF_PST_RunArray"],                   
+ 2029:	["DFF_PST_RunArrayAtom"],               
+ 2030:	["DFF_PST_ArrayElementAtom"],           
+ 2031:	["DFF_PST_Int4ArrayAtom"],              
+ 2032:	["DFF_PST_ColorSchemeAtom"],            
+ 3008:	["DFF_PST_OEShape"],                    
+ 3009:	["DFF_PST_ExObjRefAtom"],               
+ 3011:	["DFF_PST_OEPlaceholderAtom"],          
+ 3020:	["DFF_PST_GrColor"],                    
+ 3025:	["DFF_PST_GrectAtom"],                  
+ 3031:	["DFF_PST_GratioAtom"],                 
+ 3032:	["DFF_PST_Gscaling"],                   
+ 3034:	["DFF_PST_GpointAtom"],                 
+ 3035:	["DFF_PST_OEShapeAtom"],                
+ 3998:	["DFF_PST_OutlineTextRefAtom"],         
+ 3999:	["DFF_PST_TextHeaderAtom"],             
+ 4000:	["DFF_PST_TextCharsAtom"],              
+ 4001:	["DFF_PST_StyleTextPropAtom"],          
+ 4002:	["DFF_PST_BaseTextPropAtom"],           
+ 4003:	["DFF_PST_TxMasterStyleAtom"],          
+ 4004:	["DFF_PST_TxCFStyleAtom"],              
+ 4005:	["DFF_PST_TxPFStyleAtom"],              
+ 4006:	["DFF_PST_TextRulerAtom"],              
+ 4007:	["DFF_PST_TextBookmarkAtom"],           
+ 4008:	["DFF_PST_TextBytesAtom"],              
+ 4009:	["DFF_PST_TxSIStyleAtom"],              
+ 4010:	["DFF_PST_TextSpecInfoAtom"],           
+ 4011:	["DFF_PST_DefaultRulerAtom"],           
+ 4023:	["DFF_PST_FontEntityAtom"],             
+ 4024:	["DFF_PST_FontEmbedData"],              
+ 4025:	["DFF_PST_TypeFace"],                   
+ 4026:	["DFF_PST_CString"],                    
+ 4027:	["DFF_PST_ExternalObject"],             
+ 4033:	["DFF_PST_MetaFile"],                   
+ 4034:	["DFF_PST_ExOleObj"],                   
+ 4035:	["DFF_PST_ExOleObjAtom"],               
+ 4036:	["DFF_PST_ExPlainLinkAtom"],            
+ 4037:	["DFF_PST_CorePict"],                   
+ 4038:	["DFF_PST_CorePictAtom"],               
+ 4039:	["DFF_PST_ExPlainAtom"],                
+ 4040:	["DFF_PST_SrKinsoku"],                  
+ 4041:	["DFF_PST_Handout"],                    
+ 4044:	["DFF_PST_ExEmbed"],                    
+ 4045:	["DFF_PST_ExEmbedAtom"],                
+ 4046:	["DFF_PST_ExLink"],                     
+ 4047:	["DFF_PST_ExLinkAtom_old"],             
+ 4048:	["DFF_PST_BookmarkEntityAtom"],         
+ 4049:	["DFF_PST_ExLinkAtom"],                 
+ 4050:	["DFF_PST_SrKinsokuAtom"],              
+ 4051:	["DFF_PST_ExHyperlinkAtom"],            
+ 4053:	["DFF_PST_ExPlain"],                    
+ 4054:	["DFF_PST_ExPlainLink"],                
+ 4055:	["DFF_PST_ExHyperlink"],                
+ 4056:	["DFF_PST_SlideNumberMCAtom"],          
+ 4057:	["DFF_PST_HeadersFooters"],             
+ 4058:	["DFF_PST_HeadersFootersAtom"],         
+ 4062:	["DFF_PST_RecolorEntryAtom"],           
+ 4063:	["DFF_PST_TxInteractiveInfoAtom"],      
+ 4065:	["DFF_PST_EmFormatAtom"],               
+ 4066:	["DFF_PST_CharFormatAtom"],             
+ 4067:	["DFF_PST_ParaFormatAtom"],             
+ 4068:	["DFF_PST_MasterText"],                 
+ 4071:	["DFF_PST_RecolorInfoAtom"],            
+ 4073:	["DFF_PST_ExQuickTime"],                
+ 4074:	["DFF_PST_ExQuickTimeMovie"],           
+ 4075:	["DFF_PST_ExQuickTimeMovieData"],       
+ 4076:	["DFF_PST_ExSubscription"],             
+ 4077:	["DFF_PST_ExSubscriptionSection"],      
+ 4078:	["DFF_PST_ExControl"],                  
+ 4091:	["DFF_PST_ExControlAtom"],              
+ 4080:	["DFF_PST_SlideListWithText"],          
+ 4081:	["DFF_PST_AnimationInfoAtom"],          
+ 4082:	["DFF_PST_InteractiveInfo"],            
+ 4083:	["DFF_PST_InteractiveInfoAtom"],        
+ 4084:	["DFF_PST_SlideList"],                  
+ 4085:	["DFF_PST_UserEditAtom"],               
+ 4086:	["DFF_PST_CurrentUserAtom"],            
+ 4087:	["DFF_PST_DateTimeMCAtom"],             
+ 4088:	["DFF_PST_GenericDateMCAtom"],          
+ 4089:	["DFF_PST_HeaderMCAtom"],               
+ 4090:	["DFF_PST_FooterMCAtom"],               
+ 4100:	["DFF_PST_ExMediaAtom"],                
+ 4101:	["DFF_PST_ExVideo"],                    
+ 4102:	["DFF_PST_ExAviMovie"],                 
+ 4103:	["DFF_PST_ExMCIMovie"],                 
+ 4109:	["DFF_PST_ExMIDIAudio"],                
+ 4110:	["DFF_PST_ExCDAudio"],                  
+ 4111:	["DFF_PST_ExWAVAudioEmbedded"],         
+ 4112:	["DFF_PST_ExWAVAudioLink"],             
+ 4113:	["DFF_PST_ExOleObjStg"],                
+ 4114:	["DFF_PST_ExCDAudioAtom"],              
+ 4115:	["DFF_PST_ExWAVAudioEmbeddedAtom"],     
+ 4116:	["DFF_PST_AnimationInfo"],              
+ 4117:	["DFF_PST_RTFDateTimeMCAtom"],          
+ 5000:	["DFF_PST_ProgTags"],                   
+ 5001:	["DFF_PST_ProgStringTag"],              
+ 5002:	["DFF_PST_ProgBinaryTag"],              
+ 5003:	["DFF_PST_BinaryTagData"],              
+ 6000:	["DFF_PST_PrintOptions"],               
+ 6001:	["DFF_PST_PersistPtrFullBlock"],        
+ 6002:	["DFF_PST_PersistPtrIncrementalBlock"], 
+10000:	["DFF_PST_RulerIndentAtom"],            
+10001:	["DFF_PST_GscalingAtom"],               
+10002:	["DFF_PST_GrColorAtom"],                
+10003:	["DFF_PST_GLPointAtom"],                
+10004:	["DFF_PST_GlineAtom"],                  
+
+0xF000: ["DFF_msofbtDggContainer"],    
+0xF006:	["DFF_msofbtDgg"],             
+0xF016:	["DFF_msofbtCLSID"],           
+0xF00B:	["DFF_msofbtOPT"],             
+0xF11A:	["DFF_msofbtColorMRU"],        
+0xF11E:	["DFF_msofbtSplitMenuColors"], 
+0xF001:	["DFF_msofbtBstoreContainer"], 
+0xF007:	["DFF_msofbtBSE"],             
+0xF018:	["DFF_msofbtBlipFirst"],       
+0xF117:	["DFF_msofbtBlipLast"],        
+								  
+0xF002:	["DFF_msofbtDgContainer"],     
+0xF008:	["DFF_msofbtDg"],              
+0xF118:	["DFF_msofbtRegroupItems"],    
+0xF120:	["DFF_msofbtColorScheme"],     
+0xF003:	["DFF_msofbtSpgrContainer"],   
+0xF004:	["DFF_msofbtSpContainer"],     
+0xF009:	["DFF_msofbtSpgr"],            
+0xF00A:	["DFF_msofbtSp"],              
+0xF00C:	["DFF_msofbtTextbox"],         
+0xF00D:	["DFF_msofbtClientTextbox"],   
+0xF00E:	["DFF_msofbtAnchor"],          
+0xF00F:	["DFF_msofbtChildAnchor"],     
+0xF010:	["DFF_msofbtClientAnchor"],    
+0xF011:	["DFF_msofbtClientData"],      
+0xF11F:	["DFF_msofbtOleObject"],       
+0xF11D:	["DFF_msofbtDeletedPspl"],     
+0xF122:	["DFF_msofbtUDefProp"],        
+0xF005:	["DFF_msofbtSolverContainer"], 
+0xF012:	["DFF_msofbtConnectorRule"],   
+0xF013:	["DFF_msofbtAlignRule"],       
+0xF014:	["DFF_msofbtArcRule"],         
+0xF015:	["DFF_msofbtClientRule"],      
+0xF017:	["DFF_msofbtCalloutRule"],     
+								  
+0xF119:	["DFF_msofbtSelection"]       
+
+}
+
+class PPTStream(object):
+
+    def __init__ (self, chars, params):
+        self.chars = chars
+        self.size = len(self.chars)
+        self.pos = 0
+        self.version = None
+
+        self.header = None
+
+        self.params = params
+
+    def __printSep (self, c='-', w=68, prefix=''):
+        print(prefix + c*w)
+
+    def printStreamInfo (self):
+        self.__printSep('=', 68)
+        print("PPT File Format Dumper by Kohei Yoshida & Thorsten Behrens")
+        print("  total stream size: %d bytes"%self.size)
+        self.__printSep('=', 68)
+        print('')
+
+    def printHeader (self):
+        self.header = ole.Header(self.chars, self.params)
+        self.pos = self.header.parse()
+        self.header.output()
+
+    def __getDirectoryObj (self):
+        obj = self.header.getDirectory()
+        if obj == None:
+            return None
+        obj.parseDirEntries()
+        return obj
+
+    def printDirectory (self):
+        obj = self.__getDirectoryObj()
+        if obj == None:
+            return
+        obj.output()
+
+
+    def getDirectoryNames (self):
+        obj = self.__getDirectoryObj()
+        if obj == None:
+            return
+        return obj.getDirectoryNames()
+
+
+    def getDirectoryStreamByName (self, name):
+        obj = self.__getDirectoryObj()
+        bytes = []
+        if obj != None:
+            bytes = obj.getRawStreamByName(name)
+        strm = PPTDirStream(bytes, self.params)
+        return strm
+
+
+class PPTDirStream(object):
+
+    def __init__ (self, bytes, params):
+        self.bytes = bytes
+        self.size = len(self.bytes)
+        self.pos = 0
+
+        self.params = params
+
+
+    def readRaw (self, size=1):
+        # PPT stores little endian
+        bytes = 0
+        for i in xrange(0, size):
+            b = ord(self.bytes[self.pos])
+            if i == 0:
+                bytes = b
+            else:
+                bytes += b*(256**i)
+            self.pos += 1
+
+        return bytes
+
+    def readByteArray (self, size=1):
+        bytes = []
+        for i in xrange(0, size):
+            if self.pos >= self.size:
+                raise EndOfStream
+            bytes.append(ord(self.bytes[self.pos]))
+            self.pos += 1
+        return bytes
+
+    def __printSep (self, c='-', w=68, prefix=''):
+        print(prefix + c*w)
+
+    def readRecord (self):
+        if self.size - self.pos < 4:
+            raise EndOfStream
+
+        pos = self.pos
+        header = self.readRaw(2)
+        if header == 0x0000:
+            raise EndOfStream
+        size = self.readRaw(2)
+        bytes = self.readByteArray(size)
+
+        # record handler that parses the raw bytes and displays more 
+        # meaningful information.
+        handler = None 
+
+        print("")
+        self.__printSep('=', 61, "%4.4Xh: "%header)
+        if recData.has_key(header):
+            print("%4.4Xh: %s (%4.4Xh)"%
+                  (header, recData[header][0], header))
+            if len(recData[header]) >= 2:
+                handler = recData[header][1](header, size, bytes)
+        else:
+            print("%4.4Xh: [unknown record name] (%4.4Xh)"%(header, header))
+        print("%4.4Xh:   size = %d; pos = %d"%(header, size, pos))
+        self.__printSep('-', 61, "%4.4Xh: "%header)
+        for i in xrange(0, size):
+            if (i+1) % 16 == 1:
+                output("%4.4Xh: "%header)
+            output("%2.2X "%bytes[i])
+            if (i+1) % 16 == 0 and i != size-1:
+                print("")
+        if size > 0:
+            print("")
+
+        if handler != None:
+            # record handler exists.  Parse the record and display more info.
+            handler.output()
+
+        return header



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]