def __init__(self, name): construct.Struct.__init__( self, name, construct.Bytes('header', 8), construct.Value( 'total_bytes', lambda ctx: self.__total_utf8_bytes__(ctx['header'])), construct.MetaRepeater( lambda ctx: self.__total_utf8_bytes__(ctx['header']) - 1, construct.Bytes('sub_byte', 8)), construct.Value('value', lambda ctx: self.__calculate_utf8_value__(ctx)))
construct.Anchor('stream_end')) VAULT_ATTRIBUTE_EXTRA = construct.Struct('VAULT_ATTRIBUTE_EXTRA', construct.ULInt32('id'), construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'), construct.Embed(SIZED_DATA)) VAULT_ATTRIBUTE_MAP_ENTRY = construct.Struct( 'VAULT_ATTRIBUTE_MAP_ENTRY', construct.ULInt32('id'), construct.ULInt32('offset'), construct.ULInt32('attr_map_entry_unknown_1'), construct.Pointer(lambda ctx: ctx.offset, VAULT_ATTRIBUTE)) VAULT_VCRD = construct.Struct( 'VAULT_VCRD', GUID('schema_guid'), construct.ULInt32('vcrd_unknown_1'), FILETIME('last_update'), construct.ULInt32('vcrd_unknown_2'), construct.ULInt32('vcrd_unknown_3'), construct.Rename('description', UNICODE_STRING), construct.ULInt32('attributes_array_size'), construct.Value( 'attributes_num', lambda ctx: (ctx.attributes_array_size / VAULT_ATTRIBUTE_MAP_ENTRY.sizeof())), construct.Rename( 'attributes', construct.Array(lambda ctx: ctx.attributes_num, VAULT_ATTRIBUTE_MAP_ENTRY)), construct.Pointer( lambda ctx: (ctx.attributes[ctx.attributes_num - 1].VAULT_ATTRIBUTE.stream_end), construct.Rename('extra_entry', VAULT_ATTRIBUTE_EXTRA)))
# from comment string, build Containers encoder=lambda obj, ctx: construct.Container(blocks=[ construct.Container( block_size=len(chunk), data_values=chunk, ) for chunk in [obj[i:i + 255] for i in xrange(0, len(obj), 255)] ] + [construct.Container(block_size=0, data_values='')], ), # from Containers, build comment string decoder=lambda obj, ctx: ''.join(dsb.data_values for dsb in obj.blocks), ) _image_block = construct.Struct( 'image', construct.Value('block_type', lambda ctx: 'image'), construct.Struct( 'image_descriptor', construct.ULInt16('left'), construct.ULInt16('top'), construct.ULInt16('width'), construct.ULInt16('height'), construct.EmbeddedBitStruct( construct.Flag('lct_flag'), construct.Flag('interlace_flag'), construct.Flag('sort_flag'), construct.Padding(2), # reserved construct.macros.BitField('lct_size', 3), ), ), construct.If(
AXSString = construct.Struct("AXSString", construct.ULInt32("length"), construct.Bytes("data", lambda ctx: ctx.length)) AXSKeyEntry = construct.Struct("AXSKeyEntry", construct.Array(2, AXSString), construct.ULInt32("unk")) AXSKeyMan = construct.Struct( "AXSKeyMan", construct.ULInt32("nb_entry"), #construct.Array(lambda ctx: ctx.nb_entry, AXSKeyEntry)) construct.Array(1, AXSKeyEntry)) binInfo = construct.Struct( "binInfo", construct.ULInt16("a"), construct.ULInt8("b"), construct.Value("type", lambda ctx: (ctx["b"] << 16) | ctx["a"]), construct.ULInt32("size"), construct.ULInt32("unk_dword_00"), construct.ULInt8("unk_byte_00"), construct.ULInt32("type_compression"), construct.ULInt32("length"), construct.Bytes("data", lambda ctx: ctx.length)) binInfoNext = construct.Struct("binInfoNext", construct.ULInt16("type"), construct.ULInt32("size"), construct.ULInt32("length"), construct.Bytes("data", lambda ctx: ctx.length)) class Buffer: def __init__(self, buf): self.buf = buf self.length = len(self.buf)
class FlacReader: FRAME_HEADER = construct.Struct( 'frame_header', construct.Bits('sync', 14), construct.Bits('reserved', 2), construct.Bits('block_size', 4), construct.Bits('sample_rate', 4), construct.Bits('channel_assignment', 4), construct.Bits('bits_per_sample', 3), construct.Padding(1), construct.IfThenElse( 'total_channels', lambda ctx1: ctx1['channel_assignment'] <= 7, construct.Value('c', lambda ctx2: ctx2['channel_assignment'] + 1), construct.Value('c', lambda ctx3: 2)), UTF8('frame_number'), construct.IfThenElse( 'extended_block_size', lambda ctx1: ctx1['block_size'] == 6, construct.Bits('b', 8), construct.If(lambda ctx2: ctx2['block_size'] == 7, construct.Bits('b', 16))), construct.IfThenElse( 'extended_sample_rate', lambda ctx1: ctx1['sample_rate'] == 12, construct.Bits('s', 8), construct.If(lambda ctx2: ctx2['sample_rate'] in (13, 14), construct.Bits('s', 16))), construct.Bits('crc8', 8)) UNARY = construct.Struct( 'unary', construct.RepeatUntil(lambda obj, ctx: obj == '\x01', construct.Field('bytes', 1)), construct.Value('value', lambda ctx: len(ctx['bytes']) - 1)) SUBFRAME_HEADER = construct.Struct( 'subframe_header', construct.Padding(1), construct.Bits('subframe_type', 6), construct.Flag('has_wasted_bits_per_sample'), construct.IfThenElse('wasted_bits_per_sample', lambda ctx: ctx['has_wasted_bits_per_sample'], PlusOne(Unary('value')), construct.Value('value', lambda ctx2: 0))) GET_BLOCKSIZE_FROM_STREAMINFO = -1 GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER = -2 GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER = -3 BLOCK_SIZE = (GET_BLOCKSIZE_FROM_STREAMINFO, 192, 576, 1152, 2304, 4608, GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER, GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768) GET_SAMPLE_SIZE_FROM_STREAMINFO = -1 SAMPLE_SIZE = (GET_SAMPLE_SIZE_FROM_STREAMINFO, 8, 12, None, 16, 20, 24, None) def FIXED0(subframe, residual, i): subframe.insert(i, residual[i]) def FIXED1(subframe, residual, i): subframe.insert(i, subframe[i - 1] + residual[i]) def FIXED2(subframe, residual, i): subframe.insert(i, ((2 * subframe[i - 1]) - subframe[i - 2] + \ residual[i])) def FIXED3(subframe, residual, i): subframe.insert(i, ((3 * subframe[i - 1]) - (3 * subframe[i - 2]) + \ subframe[i - 3] + residual[i])) def FIXED4(subframe, residual, i): subframe.insert(i, ((4 * subframe[i - 1]) - (6 * subframe[i - 2]) + \ (4 * subframe[i - 3]) - subframe[i - 4] + residual[i])) #iterates over all of the channels, in order def MERGE_INDEPENDENT(channel_list): channel_data = [iter(c) for c in channel_list] while (True): for channel in channel_data: yield channel.next() def MERGE_LEFT(channel_list): channel_left = iter(channel_list[0]) channel_side = iter(channel_list[1]) while (True): left = channel_left.next() side = channel_side.next() yield left yield left - side def MERGE_RIGHT(channel_list): channel_side = iter(channel_list[0]) channel_right = iter(channel_list[1]) while (True): side = channel_side.next() right = channel_right.next() yield side + right yield right def MERGE_MID(channel_list): channel_mid = iter(channel_list[0]) channel_side = iter(channel_list[1]) while (True): mid = channel_mid.next() side = channel_side.next() mid = mid << 1 mid |= (side & 0x1) yield (mid + side) >> 1 yield (mid - side) >> 1 CHANNEL_FUNCTIONS = (MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_LEFT, MERGE_RIGHT, MERGE_MID) FIXED_FUNCTIONS = (FIXED0, FIXED1, FIXED2, FIXED3, FIXED4) def __init__(self, flac_stream): self.stream = BufferedStream(flac_stream) self.streaminfo = None self.bitstream = None #ensure the file starts with 'fLaC' self.read_stream_marker() #initialize self.bitstream self.begin_bitstream() #find self.streaminfo in case we need it self.read_metadata_blocks() def close(self): if (self.bitstream != None): self.bitstream.close() else: self.stream.close() def read_stream_marker(self): if (self.stream.read(4) != 'fLaC'): raise FlacStreamException('invalid stream marker') def read_metadata_blocks(self): block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream( self.stream) while (block.last_block == 0): if (block.block_type == 0): self.streaminfo = audiotools.FlacAudio.STREAMINFO.parse_stream( self.stream) else: self.stream.seek(block.block_length, 1) block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream( self.stream) self.stream.seek(block.block_length, 1) def begin_bitstream(self): import bitstream #self.bitstream = construct.BitStreamReader(self.stream) self.bitstream = bitstream.BitStreamReader(self.stream) def read_frame(self): self.stream.reset_buffer() try: header = FlacReader.FRAME_HEADER.parse_stream(self.bitstream) except construct.core.FieldError: return "" if (header.sync != 0x3FFE): raise FlacStreamException('invalid sync') if (crc8(self.stream.getvalue()[0:-1]) != header.crc8): raise FlacStreamException('crc8 checksum failed') #block_size tells us how many samples we need from each subframe block_size = FlacReader.BLOCK_SIZE[header.block_size] if (block_size == self.GET_BLOCKSIZE_FROM_STREAMINFO): block_size = self.streaminfo.maximum_blocksize elif ((block_size == self.GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER) or (block_size == self.GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER)): block_size = header.extended_block_size + 1 #grab subframe data as 32-bit array objects subframe_data = [] for channel_number in xrange(header.total_channels): subframe_data.append( self.read_subframe(header, block_size, channel_number)) crc16sum = crc16(self.stream.getvalue()) #try to byte-align the stream if (len(self.bitstream.buffer) > 0): self.bitstream.read(len(self.bitstream.buffer)) if (crc16sum != construct.Bits('crc16', 16).parse_stream( self.bitstream)): raise FlacStreamException('crc16 checksum failed') #convert our list of subframe data arrays into #a string of sample data if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 16): merged_frames = array.array( 'h', FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment]( subframe_data)) if (audiotools.BIG_ENDIAN): merged_frames.byteswap() return merged_frames.tostring() elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 8): merged_frames = array.array( 'b', FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment]( subframe_data)) return merged_frames.tostring() else: if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == \ self.GET_SAMPLE_SIZE_FROM_STREAMINFO): bits_per_sample = self.streaminfo.bits_per_sample + 1 elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == None): raise FlacStreamException('invalid bits per sample') else: bits_per_sample = FlacReader.SAMPLE_SIZE[ header.bits_per_sample] stream = construct.GreedyRepeater( construct.BitStruct( 'bits', construct.Bits('value', bits_per_sample, swapped=True, signed=True))) return stream.build([ construct.Container(value=v) for v in FlacReader.CHANNEL_FUNCTIONS[ header.channel_assignment](subframe_data) ]) def read_subframe(self, frame_header, block_size, channel_number): subframe_header = \ FlacReader.SUBFRAME_HEADER.parse_stream(self.bitstream) #figure out the bits-per-sample of this subframe if ((frame_header.channel_assignment == 8) and (channel_number == 1)): #if channel is stored as left+difference #and this is the difference, add 1 bit bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] + 1 elif ((frame_header.channel_assignment == 9) and (channel_number == 0)): #if channel is stored as difference+right #and this is the difference, add 1 bit bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] + 1 elif ((frame_header.channel_assignment == 10) and (channel_number == 1)): #if channel is stored as average+difference #and this is the difference, add 1 bit bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] + 1 else: #otherwise, use the number from the frame header bits_per_sample = FlacReader.SAMPLE_SIZE[ frame_header.bits_per_sample] if (subframe_header.has_wasted_bits_per_sample): bits_per_sample -= subframe_header.wasted_bits_per_sample if (subframe_header.subframe_type == 0): subframe = self.read_subframe_constant(block_size, bits_per_sample) elif (subframe_header.subframe_type == 1): subframe = self.read_subframe_verbatim(block_size, bits_per_sample) elif ((subframe_header.subframe_type & 0x38) == 0x08): subframe = self.read_subframe_fixed( subframe_header.subframe_type & 0x07, block_size, bits_per_sample) elif ((subframe_header.subframe_type & 0x20) == 0x20): subframe = self.read_subframe_lpc( (subframe_header.subframe_type & 0x1F) + 1, block_size, bits_per_sample) else: raise FlacStreamException('invalid subframe type') if (subframe_header.has_wasted_bits_per_sample): return array.array('i', [ i << subframe_header.wasted_bits_per_sample for i in subframe ]) else: return subframe def read_subframe_constant(self, block_size, bits_per_sample): sample = construct.Bits('b', bits_per_sample).parse_stream(self.bitstream) subframe = array.array('i', [sample] * block_size) return subframe def read_subframe_verbatim(self, block_size, bits_per_sample): return array.array( 'i', construct.StrictRepeater( block_size, construct.Bits("samples", bits_per_sample, signed=True)).parse_stream(self.bitstream)) def read_subframe_fixed(self, order, block_size, bits_per_sample): samples = construct.StrictRepeater( order, construct.Bits("warm_up_samples", bits_per_sample, signed=True)) subframe = array.array('i', samples.parse_stream(self.bitstream)) residual = self.read_residual(block_size, order) fixed_func = self.FIXED_FUNCTIONS[order] for i in xrange(len(subframe), block_size): fixed_func(subframe, residual, i) return subframe def read_subframe_lpc(self, order, block_size, bits_per_sample): samples = construct.StrictRepeater( order, construct.Bits("warm_up_samples", bits_per_sample, signed=True)) subframe = array.array('i', samples.parse_stream(self.bitstream)) lpc_precision = construct.Bits('lpc_precision', 4).parse_stream( self.bitstream) + 1 lpc_shift = construct.Bits('lpc_shift', 5).parse_stream(self.bitstream) coefficients = array.array( 'i', construct.StrictRepeater( order, construct.Bits('coefficients', lpc_precision, signed=True)).parse_stream(self.bitstream)) residual = self.read_residual(block_size, order) for i in xrange(len(subframe), block_size): subframe.insert(i, (sum( [coefficients[j] * subframe[i - j - 1] for j in xrange(0,len(coefficients))]) >> lpc_shift) + \ residual[i]) return subframe def read_residual(self, block_size, predictor_order): rice = array.array('i') #add some dummy rice so that the Rice index matches #that of the rest of the subframe for i in xrange(predictor_order): rice.append(0) coding_method = self.bitstream.read(2) if (coding_method == '\x00\x00'): rice2 = False elif (coding_method == '\x00\x01'): rice2 = True else: raise FlacStreamException('invalid residual coding method') partition_order = construct.Bits('partition_order', 4).parse_stream(self.bitstream) if (partition_order > 0): total_samples = ((block_size / 2**partition_order) - predictor_order) rice.extend(self.read_encoded_rice(total_samples, rice2)) for i in xrange(1, 2**partition_order): total_samples = (block_size / 2**partition_order) rice.extend(self.read_encoded_rice(total_samples, rice2)) else: rice.extend( self.read_encoded_rice(block_size - predictor_order, rice2)) return rice def read_encoded_rice(self, total_samples, rice2=False): bin_to_int = construct.lib.binary.bin_to_int samples = array.array('i') if (not rice2): rice_parameter = construct.Bits('rice_parameter', 4).parse_stream(self.bitstream) else: rice_parameter = construct.Bits('rice_parameter', 5).parse_stream(self.bitstream) if (rice_parameter != 0xF): #a Rice encoded residual for x in xrange(total_samples): #count the number of 0 bits before the next 1 bit #(unary encoding) #to find our most significant bits msb = 0 s = self.bitstream.read(1) while (s != '\x01'): msb += 1 s = self.bitstream.read(1) #grab the proper number of least significant bits lsb = bin_to_int(self.bitstream.read(rice_parameter)) #combine msb and lsb to get the Rice-encoded value value = (msb << rice_parameter) | lsb if ((value & 0x1) == 0x1): #negative samples.append(-(value >> 1) - 1) else: #positive samples.append(value >> 1) else: #unencoded residual bits_per_sample = construct.Bits('escape_code', 5).parse_stream(self.bitstream) sample = construct.Bits("sample", bits_per_sample, signed=True) for x in xrange(total_samples): samples.append(sample.parse_stream(self.bitstream)) return samples
def MakeRva(name): return construct.Embed(construct.Struct('EmbeddedRva', construct.ULInt32(name), construct.Value('VA', lambda ctx: idaapi.get_imagebase() + ctx[name]) ))