예제 #1
0
 def __processBlockData(self, f):
     """
     Iterates over the list of block headers and parses the data for each block.
     """
     dna = self.__dna
     types = dna.getTypes() if dna else None
     structs = dna.getStructs() if dna else None
     for block in self.__blockHeaders:
         if block['processed']:
             continue  # already processed
         code = block['blockCode']
         dataLength = block['blockLength']
         f.seek(block['filePos'])
         if code == 'REND':
             # process the abbreviated render data
             for idx in range(0, block['numberOfStructs']):
                 sframe = getInt(f.read(4))  # start frame number
                 eframe = getInt(f.read(4))  # end frame number
                 scene = (f.read(64)).decode().rstrip('\0')
                 rd = RenderData()
                 rd.startFrame = sframe
                 rd.endFrame = eframe
                 rd.sceneName = scene
                 self.__renderData.append(rd)
             block['processed'] = True
         elif code == 'TEST':
             # The block data is a thumbnail image in RGBA format
             # Data starts with two integers, the width and height of the image
             width = getInt(f.read(4))
             height = getInt(f.read(4))
             daimg = Image.frombytes('RGBA', (width, height),
                                     f.read(dataLength - 8))
             self.__thumbnailImage = daimg.transpose(
                 method=Image.FLIP_TOP_BOTTOM)
             block['processed'] = True
         else:
             scode = block['structCode']
             daspecs = block['memberSpecs']
             if (scode == 0 and len(daspecs) == 0) or not dna:
                 continue  # can't process
             else:
                 dastruct = structs[scode]
                 stype = types[dastruct[0]]
                 slist = []
                 for idx in range(0, block['numberOfStructs']):
                     slist.append(
                         self.__buildStruct(stype, block, mspecs=daspecs))
                 block['structData'] = slist
                 block['processed'] = True
예제 #2
0
 def __getBlockHeader(self, f):
     """
     Parses block headers and saves data to a dictionary. Dictionary keys:
         blockCode       a string of 2 or 4 characters indicating the block type
         blockLength     length in bytes of the data following the block header
         oldPointer      memory address of block when it was saved
         structCode      index into the array of structure definitions read from the
                         structure DNA. The data in the block conforms to this structure.
         numberOfStructs the data consists of this number of consecutive structs
         filePos         file offset to block's data
         structData      list of Struct objects that comprise the block's data
         references      pointers that point to this block
         memberSpecs     for ad-hoc structures (i.e. not in the structure DNA), a list
                         of (type, name) tuples for the structure members
     """
     pointerSize = self.__fileHeader['pointerSize']
     # first 4 bytes are a block type code
     raw = f.read(4)
     if len(raw) != 4: return None
     code = raw.decode().rstrip('\0')
     # next 4 bytes are the length of data after the block
     raw = f.read(4)
     if len(raw) != 4: return None
     length = getInt(raw)
     # next 4 or 8 bytes are a pointer
     raw = f.read(pointerSize)
     if len(raw) != pointerSize: return None
     oldPointer = getUint(raw)
     # next 4 bytes are a structure code - need structure DNA to interpret
     raw = f.read(4)
     if len(raw) != 4: return None
     structCode = getInt(raw)
     # next 4 bytes are the number of structures in this block
     raw = f.read(4)
     if len(raw) != 4: return None
     numStructs = getInt(raw)
     # quickrefs are strings consisting of the struct name, a "|" char, and the
     # member type and member name separated by a space.
     # referringMembers is a list of StructureMember objects.
     emptyrefs = {'quickRefs': set(), 'referringMembers': []}
     return {
         'blockCode': code,
         'blockLength': length,
         'oldPointer': oldPointer,
         'structCode': structCode,
         'numberOfStructs': numStructs,
         'references': emptyrefs,
         'memberSpecs': []
     }
예제 #3
0
    def __getStringBlock(self, f, type, postProc=None):
        """
        Loads a block of null-terminated strings into an array
        The block consists of a 4-character type string, e.g. NAME,
        followed by a 4-byte integer representing the count of strings,
        followed by the strings themselves.
        
        Arguments are the file stream and the type string. The function
        returns the strings in a list, or None if there was a problem.
        There is also an optional postProc function argument. This is a
        function which takes a string and returns an altered string.
        """
        # first 4 bytes must be the type
        if check4(f, type) == None:
            return None

        # next 4 bytes are an integer representing the total number of strings
        # of the given type
        typesLen = getInt(f.read(4))

        # fetch all the name strings and save
        allTypes = []
        for runningLen in range(0, typesLen):
            theString = getString(f)
            if postProc != None:
                theString = postProc(theString)
            allTypes.append(theString)

        return allTypes
예제 #4
0
    def processDNA(self):
        # first 4 bytes must be string "SDNA"
        if check4(self.__f, 'SDNA') == None:
            return

        # get the NAME strings
        self.__allNames = self.__getStringBlock(self.__f, 'NAME')
        if self.__allNames == None or len(self.__allNames) == 0: return

        # seek to next 4-byte aligned position
        padUp4(self.__f)

        # get the TYPE strings
        self.__allTypes = self.__getStringBlock(
            self.__f, 'TYPE',
            self.__DNA_struct_rename_legacy_hack_static_from_alias)
        if self.__allTypes == None or len(self.__allTypes) == 0: return
        padUp4(self.__f)

        # next 4 bytes must be "TLEN"
        if check4(self.__f, 'TLEN') == None:
            return

        # following data is a sequence of 2-byte integers representing the
        # length (in bytes) of each type in __allTypes, in the same order.
        for idx in range(0, len(self.__allTypes)):
            theLen = getInt(self.__f.read(2))
            self.__allTypeLengths.append(theLen)
        padUp4(self.__f)

        # get the structure definitions
        self.__allStructs = self.__getStructures(self.__f)
예제 #5
0
    def __getStructures(self, f):
        """
        getStructures fetches all the structure arrays and returns them as
        a list of lists, where each element list is an encoded structure
        definition.
        
        Input structure definition format:
        Each structure definition is a variable length array of 2-byte
        integers.
        index  meaning
        -----  -------
           0    struct type number, i.e. index into __allTypes array
           1    number of members in the structure
           2    type number of first member
           3    name number of first member, i.e. index into allnames array
        ... repeats for each member
        
        We will save these as a list where the first element is the structure
        type code, and the second element is a list of tuples, where each tuple
        is a type/name code pair.
        """
        # first 4 bytes of structs section must be "STRC"
        if check4(f, 'STRC') == None:
            return None
        # read the number of structure definitions
        numStructs = getInt(f.read(4))
        theStructs = []
        typeNames = self.getTypes()
        for structIdx in range(0, numStructs):
            curStruct = []
            structType = getInt(f.read(2))
            curStruct.append(structType)
            numMembers = getInt(f.read(2))
            curMembers = []
            for memberIdx in range(0, numMembers):
                typeCode = getInt(f.read(2))
                nameCode = getInt(f.read(2))
                curMembers.append((typeCode, nameCode))
            curStruct.append(curMembers)
            if len(curStruct) > 0:
                theStructs.append(curStruct)
                self.__structCodesByType[typeNames[structType]] = structIdx

        if len(theStructs) > 0:
            return theStructs
        else:
            return None
예제 #6
0
    def __getSingleValue(self, mtype, name, isSimpleType, dimensions,
                         isPointer, smember):
        """
        Creates a simple value from raw data
        """
        f = self.__f  # file object
        if isPointer:
            # make a pointer
            hdr = self.getFileHeader()
            psize = hdr['pointerSize']
            ptr = Pointer(getUint(f.read(psize)))
            # check for blocks referred to by this pointer
            hdrs = self.getHeadersByAddress()
            b = hdrs.get(ptr)
            if b:
                stype = smember.parentStruct.stype
                ptr.pointsTo(b)
                # add a reference to this pointer to the destination block
                b['references']['quickRefs'].add(stype + '|' + mtype + ' ' +
                                                 name)
                b['references']['referringMembers'].append(smember)
            return ptr

        if not isSimpleType:
            # find enclosing block
            parent = smember.parentStruct
            while parent:
                if parent.block:
                    break
                parent = parent.parentStruct
            return self.__buildStruct(
                mtype, parent.block, name,
                parentMember=smember)  # value is another struct
        if mtype == 'char':
            numDim = len(dimensions)
            if numDim == 0:
                return getInt(f.read(1))
            else:
                maxlen = dimensions[0]
                raw = f.read(maxlen)
                rs = raw
                nulidx = raw.find(b'\x00')
                if nulidx >= 0:
                    rs = raw[0:nulidx]
                try:
                    tstring = rs.decode()
                except UnicodeDecodeError:
                    return raw
                return tstring
        elif mtype == 'uchar':
            return getUint(f.read(1))
        elif mtype == 'short':
            return getInt(f.read(2))
        elif mtype == 'ushort':
            return getUint(f.read(2))
        elif mtype == 'int' or type == 'long':
            return getInt(f.read(4))
        elif mtype == 'ulong':
            return getUint(f.read(4))
        elif mtype == 'int64_t':
            return getInt(f.read(8))
        elif mtype == 'uint64_t':
            return getInt(f.read(8))
        elif mtype == 'float':
            return getFloat(f.read(4))
        elif mtype == 'double':
            return getDouble(f.read(8))
        else:
            return None