Beispiel #1
0
 def save(self, filename=None, compress=False):
     if self.file_platform not in XNB_PLATFORMS:
         raise ReaderError("bad platform: '{!r}'".format(self.file_platform))
     if self.file_version not in XNB_VERSIONS:
         raise ReaderError("bad version: {}".format(self.file_version))
     attribs = 0
     if self.file_version >= VERSION_40:
         if self.graphics_profile not in XNB_PROFILES:
             raise ReaderError("bad profile: {}".format(self.graphics_profile))
         attribs |= self.graphics_profile & _PROFILE_MASK
     do_compress = False
     if self.file_version >= VERSION_30:
         if compress:
             do_compress = True
             attribs |= _COMPRESS_MASK
     stream = BinaryStream()
     if do_compress:
         raise ReaderError("Recompression not supported")
     else:
         data = self.getvalue()
         size = len(data) + stream.calc_size(_XNB_HEADER)
     stream.pack(_XNB_HEADER, XNB_SIGNATURE, self.file_platform, self.file_version, attribs, size)
     stream.write(data)
     if filename is not None:
         filename = os.path.normpath(filename)
         dirname = os.path.dirname(filename)
         if not os.path.isdir(dirname):
             os.makedirs(dirname)
         if not filename.endswith(XNB_EXTENSION):
             filename += XNB_EXTENSION
         stream.write_file(filename)
     else:
         return stream.getvalue()
Beispiel #2
0
 def find_assets(self):
     for pak_file in self.content_pak_files:
         filename = os.path.join(self.root_dir, pak_file)
         if not os.path.isfile(filename):
             raise ReaderError("Content pak not found in content root: '{}'".format(filename))
         stream = BinaryStream(filename=filename)
         capacity = stream.read_int32()
         for _ in range(capacity):
             asset_name = stream.read_string()
             asset_size = stream.read_int32()
             asset_data = stream.read(asset_size)
             asset_name = asset_name.replace('\\', '/')
             asset_name = asset_name.lower()
             yield asset_name, asset_data
Beispiel #3
0
    def __init__(self, data=None, filename=None):
        # open in little endian initially
        stream = BinaryStream(data=data, filename=filename)
        del data

        # check sig to find actual endianess
        h_sig = stream.peek(len(XGS_L_SIGNATURE))
        if h_sig == XGS_L_SIGNATURE:
            big_endian = False
        elif h_sig == XGS_B_SIGNATURE:
            big_endian = True
        else:
            raise ValueError("bad sig: {!r}".format(h_sig))

        # switch stream to correct endianess
        stream.set_endian(big_endian)
Beispiel #4
0
    def __init__(self, data=None, filename=None):
        # open in little endian initially
        stream = BinaryStream(data=data, filename=filename)
        del data

        # check sig to find actual endianess
        h_sig = stream.peek(len(XGS_L_SIGNATURE))
        if h_sig == XGS_L_SIGNATURE:
            big_endian = False
        elif h_sig == XGS_B_SIGNATURE:
            big_endian = True
        else:
            raise ValueError("bad sig: {!r}".format(h_sig))

        # switch stream to correct endianess
        stream.set_endian(big_endian)
Beispiel #5
0
 def __init__(self, data, file_platform=PLATFORM_WINDOWS, file_version=VERSION_40, graphics_profile=PROFILE_REACH,
              compressed=False, parse=True, expected_type=None):
     BinaryStream.__init__(self, data=data)
     del data
     if XNBReader._type_reader_manager is None:
         XNBReader._type_reader_manager = TypeReaderManager()
     self.type_reader_manager = XNBReader._type_reader_manager
     self.file_platform = file_platform
     self.file_version = file_version
     self.graphics_profile = graphics_profile
     self.compressed = compressed
     self.needs_swap = self.file_platform == PLATFORM_XBOX
     self.type_readers = []
     self.shared_objects = []
     self.content = None
     if parse:
         self.parse(expected_type=expected_type)
Beispiel #6
0
    def __init__(self, header, data, dpds=None, seek=None, needs_swap=False):
        self.header_raw = header
        self.data_raw = data
        self.dpds_raw = dpds
        self.seek_raw = seek
        self.needs_swap = needs_swap

        h_s = BinaryStream(data=self.header_raw, big_endian=needs_swap)
        waveformatex_size = h_s.calc_size(self._waveformatex)
        (self.h_format_tag, self.h_channels, self.h_samples_per_sec, self.h_avg_bytes_per_sec, self.h_block_align,
         self.h_bits_per_sample) = h_s.unpack(self._waveformatex)
        header_size = waveformatex_size

        # do we have a WAVEFORMATEX
        self.h_size = None
        if len(self.header_raw) >= waveformatex_size + 2:
            self.h_size = h_s.read_uint16()
            header_size += 2

            if self.h_format_tag == WAVE_FORMAT_XMA2:
                waveformat_xma2_size = h_s.calc_size(self._waveformat_xma2)
                if self.h_size != waveformat_xma2_size:
                    raise ReaderError("Unknown cbSize for XMA2WAVEFORMATEX: {}".format(self.h_size))
                (self.hx_num_streams, self.hx_channel_mask, self.hx_samples_encoded, self.hx_bytes_per_block,
                 self.hx_play_begin, self.hx_play_length, self.hx_loop_begin, self.hx_loop_length, self.hx_loop_count,
                 self.hx_encoder_version, self.hx_block_count) = h_s.unpack(self._waveformat_xma2)
                header_size += waveformat_xma2_size
            elif self.h_format_tag == WAVE_FORMAT_EXTENSIBLE:
                waveformat_extensible_size = h_s.calc_size(self._waveformat_extensible)
                if self.h_size < waveformat_extensible_size:
                    raise ReaderError("Invalid cbSize for WAVEFORMATEXTENSIBLE: {}".format(self.h_size))
                (self.he_valid_bits_per_sample, self.he_channel_mask,
                 he_subformat_bytes) = h_s.unpack(self._waveformat_extensible)
                self.he_subformat = UUID(bytes_le=he_subformat_bytes)
                header_size += waveformat_extensible_size
                self.he_remainder = None
                if self.h_size > waveformat_extensible_size:
                    self.he_remainder = h_s.read(self.h_size - waveformat_extensible_size)
                    header_size += self.h_size - waveformat_extensible_size
                    raise ReaderError("Extra bytes in WAVEFORMATEXTENSIBLE: {}".format(len(self.he_remainder)))
            self.h_remainder = h_s.read()
            if len(self.h_remainder):
                header_size += len(self.h_remainder)
        if header_size != len(self.header_raw):
            raise ReaderError("Header size mismatch: {} != {}".format(header_size, len(self.header_raw)))
Beispiel #7
0
 def load(cls, data=None, filename=None, parse=True, expected_type=None):
     if filename is not None:
         filename = os.path.normpath(filename)
     stream = BinaryStream(data=data, filename=filename)
     del data
     (sig, platform, version, attribs, size) = stream.unpack(_XNB_HEADER)
     if sig != XNB_SIGNATURE:
         raise ReaderError("bad sig: '{!r}'".format(sig))
     if platform not in XNB_PLATFORMS:
         raise ReaderError("bad platform: '{!r}'".format(platform))
     if version not in XNB_VERSIONS:
         raise ReaderError("bad version: {}".format(version))
     stream_length = stream.length()
     if stream_length != size:
         raise ReaderError("bad size: {} != {}".format(stream_length, size))
     compressed = False
     profile = 0
     if version >= VERSION_40:
         profile = attribs & _PROFILE_MASK
         if profile not in XNB_PROFILES:
             raise ReaderError("bad profile: {}".format(profile))
     if version >= VERSION_30:
         compressed = bool(attribs & _COMPRESS_MASK)
         size -= stream.calc_size(_XNB_HEADER)
     if compressed:
         uncomp = stream.read_int32()
         size -= 4
         content_comp = stream.read(size)
         content = decompress(content_comp, uncomp)
     else:
         content = stream.read(size)
     return cls(content, platform, version, profile, compressed, parse=parse, expected_type=expected_type)
Beispiel #8
0
    def write_bytearray(self, filename, rows):
        stream = BinaryStream(big_endian=True)
        # http://www.w3.org/TR/PNG/#5PNG-file-signature
        stream.write(PyPngWriter._SIGNATURE)

        # http://www.w3.org/TR/PNG/#11IHDR
        PyPngWriter._write_chunk(stream, b'IHDR', struct.pack('!II B B BBB', self.width, self.height, 8, 6, 0, 0, 0))

        # http://www.w3.org/TR/PNG/#11IDAT
        compressor = zlib.compressobj()

        data = bytearray()
        for row in rows:
            data.append(0)
            data.extend(row)
            if len(data) > self.chunk_limit:
                compressed = compressor.compress(bytes(data))
                if len(compressed):
                    PyPngWriter._write_chunk(stream, b'IDAT', compressed)
                data = bytearray()
        if len(data):
            compressed = compressor.compress(bytes(data))
        else:
            compressed = bytes()
        flushed = compressor.flush()
        if len(compressed) or len(flushed):
            PyPngWriter._write_chunk(stream, b'IDAT', compressed + flushed)

        # http://www.w3.org/TR/PNG/#11IEND
        PyPngWriter._write_chunk(stream, b'IEND')
        stream.write_file(filename)
Beispiel #9
0
 def __init__(self,
              data,
              file_platform=PLATFORM_WINDOWS,
              file_version=VERSION_40,
              graphics_profile=PROFILE_REACH,
              compressed=False,
              parse=True,
              expected_type=None):
     BinaryStream.__init__(self, data=data)
     del data
     if XNBReader._type_reader_manager is None:
         XNBReader._type_reader_manager = TypeReaderManager()
     self.type_reader_manager = XNBReader._type_reader_manager
     self.file_platform = file_platform
     self.file_version = file_version
     self.graphics_profile = graphics_profile
     self.compressed = compressed
     self.needs_swap = self.file_platform == PLATFORM_XBOX
     self.type_readers = []
     self.shared_objects = []
     self.content = None
     if parse:
         self.parse(expected_type=expected_type)
Beispiel #10
0
 def save(self, filename=None, compress=False):
     if self.file_platform not in XNB_PLATFORMS:
         raise ReaderError("bad platform: '{!r}'".format(
             self.file_platform))
     if self.file_version not in XNB_VERSIONS:
         raise ReaderError("bad version: {}".format(self.file_version))
     attribs = 0
     if self.file_version >= VERSION_40:
         if self.graphics_profile not in XNB_PROFILES:
             raise ReaderError("bad profile: {}".format(
                 self.graphics_profile))
         attribs |= self.graphics_profile & _PROFILE_MASK
     do_compress = False
     if self.file_version >= VERSION_30:
         if compress:
             do_compress = True
             attribs |= _COMPRESS_MASK
     stream = BinaryStream()
     if do_compress:
         raise ReaderError("Recompression not supported")
     else:
         data = self.getvalue()
         size = len(data) + stream.calc_size(_XNB_HEADER)
     stream.pack(_XNB_HEADER, XNB_SIGNATURE, self.file_platform,
                 self.file_version, attribs, size)
     stream.write(data)
     if filename is not None:
         filename = os.path.normpath(filename)
         dirname = os.path.dirname(filename)
         if not os.path.isdir(dirname):
             os.makedirs(dirname)
         if not filename.endswith(XNB_EXTENSION):
             filename += XNB_EXTENSION
         stream.write_file(filename)
     else:
         return stream.getvalue()
 def find_assets(self):
     for pak_file in self.content_pak_files:
         filename = os.path.join(self.root_dir, pak_file)
         if os.path.isfile(filename):
             stream = BinaryStream(filename=filename)
             capacity = stream.read_int32()
             for _ in range(capacity):
                 asset_name = stream.read_string()
                 asset_size = stream.read_int32()
                 asset_data = stream.read(asset_size)
                 asset_name = asset_name.replace('\\', '/')
                 asset_name = asset_name.lower()
                 yield asset_name, asset_data
Beispiel #12
0
 def load(cls, data=None, filename=None, parse=True, expected_type=None):
     if filename is not None:
         filename = os.path.normpath(filename)
     stream = BinaryStream(data=data, filename=filename)
     del data
     (sig, platform, version, attribs, size) = stream.unpack(_XNB_HEADER)
     if sig != XNB_SIGNATURE:
         raise ReaderError("bad sig: '{!r}'".format(sig))
     if platform not in XNB_PLATFORMS:
         raise ReaderError("bad platform: '{!r}'".format(platform))
     if version not in XNB_VERSIONS:
         raise ReaderError("bad version: {}".format(version))
     stream_length = stream.length()
     if stream_length != size:
         raise ReaderError("bad size: {} != {}".format(stream_length, size))
     compressed = False
     profile = 0
     if version >= VERSION_40:
         profile = attribs & _PROFILE_MASK
         if profile not in XNB_PROFILES:
             raise ReaderError("bad profile: {}".format(profile))
     if version >= VERSION_30:
         compressed = bool(attribs & _COMPRESS_MASK)
         size -= stream.calc_size(_XNB_HEADER)
     if compressed:
         uncomp = stream.read_int32()
         size -= 4
         content_comp = stream.read(size)
         content = decompress(content_comp, uncomp)
     else:
         content = stream.read(size)
     return cls(content,
                platform,
                version,
                profile,
                compressed,
                parse=parse,
                expected_type=expected_type)
Beispiel #13
0
 def find_assets(self):
     for pak_file in self.content_pak_files:
         filename = os.path.join(self.root_dir, pak_file)
         if not os.path.isfile(filename):
             raise ReaderError(
                 "Content pak not found in content root: '{}'".format(
                     filename))
         stream = BinaryStream(filename=filename)
         capacity = stream.read_int32()
         for _ in range(capacity):
             asset_name = stream.read_string()
             asset_size = stream.read_int32()
             asset_data = stream.read(asset_size)
             asset_name = asset_name.replace('\\', '/')
             asset_name = asset_name.lower()
             yield asset_name, asset_data
Beispiel #14
0
    def __init__(self, data=None, filename=None, audio_engine=None):
        self.audio_engine = audio_engine

        # open in little endian initially
        stream = BinaryStream(data=data, filename=filename)
        del data

        # check sig to find actual endianess
        h_sig = stream.peek(len(SB_L_SIGNATURE))
        if h_sig == SB_L_SIGNATURE:
            big_endian = False
        elif h_sig == SB_B_SIGNATURE:
            big_endian = True
        else:
            raise ValueError("bad sig: {!r}".format(h_sig))

        # switch stream to correct endianess
        stream.set_endian(big_endian)
        (h_sig, self.version, self.header_version, self.crc, buildtime_raw_low, buildtime_raw_high,
         self.platform, h_simple_cue_count, h_complex_cue_count, h_unknown_count, h_cue_name_hash_count,
         h_wave_bank_count, h_sound_count, h_cue_names_length, simple_cue_offset_raw, complex_cue_offset_raw,
         cue_name_offset_raw, unknown_offset_raw, variation_offset_raw, transition_offset_raw, wave_bank_offset_raw,
         cue_name_hash_offset_raw, cue_name_table_offset_raw, sound_offset_raw, h_name_raw) = stream.unpack(_SB_HEADER)
        h_simple_cue_offset = fix_offset(simple_cue_offset_raw)
        h_complex_cue_offset = fix_offset(complex_cue_offset_raw)
        h_cue_name_offset = fix_offset(cue_name_offset_raw)
        h_unknown_offset = fix_offset(unknown_offset_raw)
        h_variation_offset = fix_offset(variation_offset_raw)
        h_transition_offset = fix_offset(transition_offset_raw)
        h_wave_bank_offset = fix_offset(wave_bank_offset_raw)
        h_cue_name_hash_offset = fix_offset(cue_name_hash_offset_raw)
        h_cue_name_table_offset = fix_offset(cue_name_table_offset_raw)
        h_sound_offset = fix_offset(sound_offset_raw)
        self.name = h_name_raw.rstrip(b'\x00').decode('iso8859-1')
        del h_name_raw
        self.buildtime = filetime_to_datetime(buildtime_raw_low, buildtime_raw_high)

        self.wave_banks = []
        if h_wave_bank_count and h_wave_bank_offset:
            stream.seek(h_wave_bank_offset)
            self.wave_banks = [stream.read(64).rstrip(b'\x00').decode('iso8859-1') for _ in range(h_wave_bank_count)]
        else:
            raise ReaderError("No wave banks found in sound bank")

        cue_name_hash = []
        if h_cue_name_hash_count and h_cue_name_hash_offset:
            stream.seek(h_cue_name_hash_offset)
            cue_name_hash = [stream.read_int16() for _ in range(h_cue_name_hash_count)]
        cue_name_hash_entry = []
        if h_cue_names_length and h_cue_name_table_offset:
            stream.seek(h_cue_name_table_offset)
            cue_name_hash_entry = [(stream.read_int32(), stream.read_int16())
                                   for _ in range(h_simple_cue_count + h_complex_cue_count)]
        cue_names = []
        for (name_offset, _) in cue_name_hash_entry:
            stream.seek(name_offset)
            cue_names.append(stream.read_cstring())

        self.cues = []
        self.cues_name = OrderedDict()
        if h_simple_cue_count and h_simple_cue_offset:
            stream.seek(h_simple_cue_offset)
            for i in range(h_simple_cue_count):
                cue_name = None
                if cue_names:
                    cue_name = cue_names[i]
                cue = Cue(cue_name, stream)
                self.cues.append(cue)
                if cue_name:
                    self.cues_name[cue_name] = cue
        if h_complex_cue_count and h_complex_cue_offset:
            stream.seek(h_complex_cue_offset)
            for i in range(h_complex_cue_count):
                cue_name = None
                if cue_names:
                    cue_name = cue_names[h_simple_cue_count + i]
                cue = Cue(cue_name, stream, is_complex=True)
                self.cues.append(cue)
                if cue_name:
                    self.cues_name[cue_name] = cue
Beispiel #15
0
    def __init__(self, data=None, filename=None, audio_engine=None):
        self.audio_engine = audio_engine

        # open in little endian initially
        stream = BinaryStream(data=data, filename=filename)
        del data

        # check sig to find actual endianess
        h_sig = stream.peek(len(SB_L_SIGNATURE))
        if h_sig == SB_L_SIGNATURE:
            big_endian = False
        elif h_sig == SB_B_SIGNATURE:
            big_endian = True
        else:
            raise ValueError("bad sig: {!r}".format(h_sig))

        # switch stream to correct endianess
        stream.set_endian(big_endian)
        (h_sig, self.version, self.header_version, self.crc, buildtime_raw_low,
         buildtime_raw_high, self.platform, h_simple_cue_count,
         h_complex_cue_count, h_unknown_count, h_cue_name_hash_count,
         h_wave_bank_count, h_sound_count, h_cue_names_length,
         simple_cue_offset_raw, complex_cue_offset_raw, cue_name_offset_raw,
         unknown_offset_raw, variation_offset_raw, transition_offset_raw,
         wave_bank_offset_raw, cue_name_hash_offset_raw,
         cue_name_table_offset_raw, sound_offset_raw,
         h_name_raw) = stream.unpack(_SB_HEADER)
        h_simple_cue_offset = fix_offset(simple_cue_offset_raw)
        h_complex_cue_offset = fix_offset(complex_cue_offset_raw)
        h_cue_name_offset = fix_offset(cue_name_offset_raw)
        h_unknown_offset = fix_offset(unknown_offset_raw)
        h_variation_offset = fix_offset(variation_offset_raw)
        h_transition_offset = fix_offset(transition_offset_raw)
        h_wave_bank_offset = fix_offset(wave_bank_offset_raw)
        h_cue_name_hash_offset = fix_offset(cue_name_hash_offset_raw)
        h_cue_name_table_offset = fix_offset(cue_name_table_offset_raw)
        h_sound_offset = fix_offset(sound_offset_raw)
        self.name = h_name_raw.rstrip(b'\x00').decode('iso8859-1')
        del h_name_raw
        self.buildtime = filetime_to_datetime(buildtime_raw_low,
                                              buildtime_raw_high)

        self.wave_banks = []
        if h_wave_bank_count and h_wave_bank_offset:
            stream.seek(h_wave_bank_offset)
            self.wave_banks = [
                stream.read(64).rstrip(b'\x00').decode('iso8859-1')
                for _ in range(h_wave_bank_count)
            ]
        else:
            raise ReaderError("No wave banks found in sound bank")

        cue_name_hash = []
        if h_cue_name_hash_count and h_cue_name_hash_offset:
            stream.seek(h_cue_name_hash_offset)
            cue_name_hash = [
                stream.read_int16() for _ in range(h_cue_name_hash_count)
            ]
        cue_name_hash_entry = []
        if h_cue_names_length and h_cue_name_table_offset:
            stream.seek(h_cue_name_table_offset)
            cue_name_hash_entry = [
                (stream.read_int32(), stream.read_int16())
                for _ in range(h_simple_cue_count + h_complex_cue_count)
            ]
        cue_names = []
        for (name_offset, _) in cue_name_hash_entry:
            stream.seek(name_offset)
            cue_names.append(stream.read_cstring())

        self.cues = []
        self.cues_name = OrderedDict()
        if h_simple_cue_count and h_simple_cue_offset:
            stream.seek(h_simple_cue_offset)
            for i in range(h_simple_cue_count):
                cue_name = None
                if cue_names:
                    cue_name = cue_names[i]
                cue = Cue(cue_name, stream)
                self.cues.append(cue)
                if cue_name:
                    self.cues_name[cue_name] = cue
        if h_complex_cue_count and h_complex_cue_offset:
            stream.seek(h_complex_cue_offset)
            for i in range(h_complex_cue_count):
                cue_name = None
                if cue_names:
                    cue_name = cue_names[h_simple_cue_count + i]
                cue = Cue(cue_name, stream, is_complex=True)
                self.cues.append(cue)
                if cue_name:
                    self.cues_name[cue_name] = cue
Beispiel #16
0
 def write(self, filename):
     h_s = BinaryStream()
     h_s.pack(self._waveformatex, self.h_format_tag, self.h_channels, self.h_samples_per_sec,
              self.h_avg_bytes_per_sec, self.h_block_align, self.h_bits_per_sample)
     if self.h_size is not None:
         h_s.write_uint16(self.h_size)
         if self.h_format_tag == WAVE_FORMAT_XMA2:
             # hack so mono sounds end up center rather than left
             if self.h_channels == 1 and self.hx_channel_mask == 1:
                 hx_channel_mask = 0
             else:
                 hx_channel_mask = self.hx_channel_mask
             h_s.pack(self._waveformat_xma2, self.hx_num_streams, hx_channel_mask, self.hx_samples_encoded,
                      self.hx_bytes_per_block, self.hx_play_begin, self.hx_play_length, self.hx_loop_begin,
                      self.hx_loop_length, self.hx_loop_count, self.hx_encoder_version, self.hx_block_count)
         elif self.h_format_tag == WAVE_FORMAT_EXTENSIBLE:
             h_s.pack(self._waveformat_extensible, self.he_valid_bits_per_sample, self.he_channel_mask,
                      self.he_subformat.bytes_le)
             if self.he_remainder:
                 h_s.write(self.he_remainder)
         if self.h_remainder:
             h_s.write(self.h_remainder)
     header_raw = h_s.getvalue()
     if self.dpds_raw:
         dpds_size = len(self.dpds_raw)
     else:
         dpds_size = None
     if self.seek_raw:
         seek_size = len(self.seek_raw)
     else:
         seek_size = None
     o_s = BinaryStream()
     if self.h_format_tag == WAVE_FORMAT_WMAUDIO2 or self.h_format_tag == WAVE_FORMAT_WMAUDIO3:
         riff_type = b'XWMA'
     else:
         riff_type = b'WAVE'
     self.write_header(o_s, riff_type, len(header_raw), len(self.data_raw), dpds_size, seek_size)
     self.write_chunk(o_s, b'fmt ', header_raw)
     if self.dpds_raw:
         self.write_chunk(o_s, b'dpds', self.dpds_raw)
     if self.seek_raw:
         self.write_chunk(o_s, b'seek', self.seek_raw)
     self.write_chunk(o_s, b'data', self.data_raw)
     if self.h_format_tag == WAVE_FORMAT_XMA2:
         full_filename = filename + '.xma'
     elif self.h_format_tag == WAVE_FORMAT_WMAUDIO2 or self.h_format_tag == WAVE_FORMAT_WMAUDIO2:
         full_filename = filename + '.xwma'
     else:
         full_filename = filename + '.wav'
     o_s.write_file(full_filename)
Beispiel #17
0
    def __init__(self, header, data, dpds=None, seek=None, needs_swap=False):
        self.header_raw = header
        self.data_raw = data
        self.dpds_raw = dpds
        self.seek_raw = seek
        self.needs_swap = needs_swap

        h_s = BinaryStream(data=self.header_raw, big_endian=needs_swap)
        waveformatex_size = h_s.calc_size(self._waveformatex)
        (self.h_format_tag, self.h_channels, self.h_samples_per_sec,
         self.h_avg_bytes_per_sec, self.h_block_align,
         self.h_bits_per_sample) = h_s.unpack(self._waveformatex)
        header_size = waveformatex_size

        # do we have a WAVEFORMATEX
        self.h_size = None
        if len(self.header_raw) >= waveformatex_size + 2:
            self.h_size = h_s.read_uint16()
            header_size += 2

            if self.h_format_tag == WAVE_FORMAT_XMA2:
                waveformat_xma2_size = h_s.calc_size(self._waveformat_xma2)
                if self.h_size != waveformat_xma2_size:
                    raise ReaderError(
                        "Unknown cbSize for XMA2WAVEFORMATEX: {}".format(
                            self.h_size))
                (self.hx_num_streams, self.hx_channel_mask,
                 self.hx_samples_encoded, self.hx_bytes_per_block,
                 self.hx_play_begin, self.hx_play_length, self.hx_loop_begin,
                 self.hx_loop_length, self.hx_loop_count,
                 self.hx_encoder_version,
                 self.hx_block_count) = h_s.unpack(self._waveformat_xma2)
                header_size += waveformat_xma2_size
            elif self.h_format_tag == WAVE_FORMAT_EXTENSIBLE:
                waveformat_extensible_size = h_s.calc_size(
                    self._waveformat_extensible)
                if self.h_size < waveformat_extensible_size:
                    raise ReaderError(
                        "Invalid cbSize for WAVEFORMATEXTENSIBLE: {}".format(
                            self.h_size))
                (self.he_valid_bits_per_sample, self.he_channel_mask,
                 he_subformat_bytes) = h_s.unpack(self._waveformat_extensible)
                self.he_subformat = UUID(bytes_le=he_subformat_bytes)
                header_size += waveformat_extensible_size
                self.he_remainder = None
                if self.h_size > waveformat_extensible_size:
                    self.he_remainder = h_s.read(self.h_size -
                                                 waveformat_extensible_size)
                    header_size += self.h_size - waveformat_extensible_size
                    raise ReaderError(
                        "Extra bytes in WAVEFORMATEXTENSIBLE: {}".format(
                            len(self.he_remainder)))
            self.h_remainder = h_s.read()
            if len(self.h_remainder):
                header_size += len(self.h_remainder)
        if header_size != len(self.header_raw):
            raise ReaderError("Header size mismatch: {} != {}".format(
                header_size, len(self.header_raw)))
Beispiel #18
0
 def write(self, filename):
     h_s = BinaryStream()
     h_s.pack(self._waveformatex, self.h_format_tag, self.h_channels,
              self.h_samples_per_sec, self.h_avg_bytes_per_sec,
              self.h_block_align, self.h_bits_per_sample)
     if self.h_size is not None:
         h_s.write_uint16(self.h_size)
         if self.h_format_tag == WAVE_FORMAT_XMA2:
             # hack so mono sounds end up center rather than left
             if self.h_channels == 1 and self.hx_channel_mask == 1:
                 hx_channel_mask = 0
             else:
                 hx_channel_mask = self.hx_channel_mask
             h_s.pack(self._waveformat_xma2, self.hx_num_streams,
                      hx_channel_mask, self.hx_samples_encoded,
                      self.hx_bytes_per_block, self.hx_play_begin,
                      self.hx_play_length, self.hx_loop_begin,
                      self.hx_loop_length, self.hx_loop_count,
                      self.hx_encoder_version, self.hx_block_count)
         elif self.h_format_tag == WAVE_FORMAT_EXTENSIBLE:
             h_s.pack(self._waveformat_extensible,
                      self.he_valid_bits_per_sample, self.he_channel_mask,
                      self.he_subformat.bytes_le)
             if self.he_remainder:
                 h_s.write(self.he_remainder)
         if self.h_remainder:
             h_s.write(self.h_remainder)
     header_raw = h_s.getvalue()
     if self.dpds_raw:
         dpds_size = len(self.dpds_raw)
     else:
         dpds_size = None
     if self.seek_raw:
         seek_size = len(self.seek_raw)
     else:
         seek_size = None
     o_s = BinaryStream()
     if self.h_format_tag == WAVE_FORMAT_WMAUDIO2 or self.h_format_tag == WAVE_FORMAT_WMAUDIO3:
         riff_type = b'XWMA'
     else:
         riff_type = b'WAVE'
     self.write_header(o_s, riff_type, len(header_raw), len(self.data_raw),
                       dpds_size, seek_size)
     self.write_chunk(o_s, b'fmt ', header_raw)
     if self.dpds_raw:
         self.write_chunk(o_s, b'dpds', self.dpds_raw)
     if self.seek_raw:
         self.write_chunk(o_s, b'seek', self.seek_raw)
     self.write_chunk(o_s, b'data', self.data_raw)
     if self.h_format_tag == WAVE_FORMAT_XMA2:
         full_filename = filename + '.xma'
     elif self.h_format_tag == WAVE_FORMAT_WMAUDIO2 or self.h_format_tag == WAVE_FORMAT_WMAUDIO2:
         full_filename = filename + '.xwma'
     else:
         full_filename = filename + '.wav'
     o_s.write_file(full_filename)
Beispiel #19
0
    def __init__(self, data=None, filename=None, audio_engine=None):
        self.audio_engine = audio_engine

        # open in little endian initially
        stream = BinaryStream(data=data, filename=filename)
        del data

        # check sig to find actual endianess
        h_sig = stream.peek(len(WB_L_SIGNATURE))
        if h_sig == WB_L_SIGNATURE:
            big_endian = False
        elif h_sig == WB_B_SIGNATURE:
            big_endian = True
        else:
            raise ValueError("bad sig: {!r}".format(h_sig))

        # switch stream to correct endianess
        stream.set_endian(big_endian)
        (h_sig, self.h_version,
         self.h_header_version) = stream.unpack(_WB_HEADER)
        regions = {
            k: XWBRegion._make(stream.unpack(_WB_REGION))
            for k in _REGIONS
        }  # pylint: disable-msg=W0212

        # check if we have a valid BANKDATA region and parse it
        bankdata_size = stream.calc_size(_WB_DATA)
        if regions['BANKDATA'].length != bankdata_size:
            raise ReaderError("Invalid BANKDATA size: {} != {}".format(
                regions['BANKDATA'].length, bankdata_size))
        stream.seek(regions['BANKDATA'].offset)
        (self.flags, h_entry_count, h_bank_name_raw,
         h_entry_metadata_element_size, h_entry_name_element_size,
         self.alignment, h_compact_format, buildtime_raw_low,
         buildtime_raw_high) = stream.unpack(_WB_DATA)
        self.bank_name = h_bank_name_raw.rstrip(b'\x00').decode('iso8859-1')
        del h_bank_name_raw
        self.buildtime = filetime_to_datetime(buildtime_raw_low,
                                              buildtime_raw_high)

        if self.flags & ~(WB_TYPE_MASK | WB_FLAGS_MASK):
            raise ReaderError("Unknown flags in WAVEBANK")

        # check what type of ENTRYMETADATA we have and parse it
        if self.has_compact:
            raise ReaderError("Compact format not supported")
        bankentry_size = stream.calc_size(_WB_ENTRY)
        if bankentry_size != h_entry_metadata_element_size:
            raise ReaderError(
                "Unknown EntryMetaDataElementSize: {} != {}".format(
                    bankentry_size, h_entry_metadata_element_size))
        if regions['ENTRYMETADATA'].length != bankentry_size * h_entry_count:
            raise ReaderError("Invalid ENTRYMETADATA size: {} != {}".format(
                regions['ENTRYMETADATA'].length,
                bankentry_size * h_entry_count))
        stream.seek(regions['ENTRYMETADATA'].offset)
        entry_metadata = [
            XWBEntry._make(
                stream.unpack(_WB_ENTRY))  # pylint: disable-msg=W0212,E1101
            for _ in range(h_entry_count)
        ]

        # read ENTRYNAMES if present
        entry_names = []
        if self.has_entry_names and regions['ENTRYNAMES'].offset and regions[
                'ENTRYNAMES'].length:
            if regions[
                    'ENTRYNAMES'].length != h_entry_name_element_size * h_entry_count:
                raise ReaderError(
                    "Invalid ENTRYNAMES region size: {} != {}".format(
                        regions['ENTRYNAMES'].length,
                        h_entry_name_element_size * h_entry_count))
            stream.seek(regions['ENTRYNAMES'].offset)
            entry_names = [
                stream.read(h_entry_name_element_size).rstrip(b'\x00').decode(
                    'iso8859-1') for _ in range(h_entry_count)
            ]

        # read SEEKTABLES if present
        entry_seektables = []
        if self.has_seek_tables and regions['SEEKTABLES'].offset and regions[
                'SEEKTABLES'].length:
            stream.seek(regions['SEEKTABLES'].offset)
            seek_offsets = []
            for _ in range(h_entry_count):
                seek_offsets.append(stream.read_int32())
            seek_data_offset = stream.tell()
            for cur_offset in seek_offsets:
                if cur_offset >= 0:
                    stream.seek(seek_data_offset + cur_offset)
                    packet_count = stream.read_uint32()
                    cur_seek_data = BinaryStream()
                    for _ in range(packet_count):
                        cur_seek_data.write_uint32(stream.read_uint32())
                    entry_seektables.append(cur_seek_data.getvalue())
                else:
                    entry_seektables.append(None)

        self.entries = []
        for i, cur_meta in enumerate(entry_metadata):
            c_entry_flags = cur_meta.flags_duration & WB_ENTRY_FLAGS_MASK
            c_duration = (cur_meta.flags_duration
                          & WB_ENTRY_DURATION_MASK) >> 4
            c_format_tag = cur_meta.format & WB_FORMAT_TAG_MASK
            c_channels = (cur_meta.format & WB_FORMAT_CHANNELS) >> 2
            c_samples_per_sec = (cur_meta.format
                                 & WB_FORMAT_SAMPLES_PER_SEC) >> 5
            c_block_align = (cur_meta.format & WB_FORMAT_BLOCK_ALIGN) >> 23
            c_bits_per_sample = (cur_meta.format
                                 & WB_FORMAT_BITS_PER_SAMPLE) >> 31
            entry_name = None
            if entry_names:
                entry_name = entry_names[i]
            entry_dpds = None
            entry_seek = None
            extra_header = bytes()
            # build format specific header and seek data
            if c_format_tag == WB_FORMAT_TAG_PCM:
                c_format_tag = WAVE_FORMAT_PCM
                if c_bits_per_sample == 1:
                    c_bits_per_sample = 16
                else:
                    c_bits_per_sample = 8
                c_avg_bytes_per_sec = c_samples_per_sec * c_block_align
            elif c_format_tag == WB_FORMAT_TAG_ADPCM:
                c_format_tag = WAVE_FORMAT_ADPCM
                c_bits_per_sample = 4
                c_block_align = (c_block_align +
                                 ADPCM_BLOCK_ALIGN_OFFSET) * c_channels
                cx_samples_per_block = (
                    (c_block_align - (7 * c_channels)) *
                    8) // (c_bits_per_sample * c_channels) + 2
                c_avg_bytes_per_sec = (c_samples_per_sec //
                                       cx_samples_per_block) * c_block_align
                cx_num_coef = len(ADPCM_COEF)
                extra_header = _ADPCM_WAVEFORMAT.pack(cx_samples_per_block,
                                                      cx_num_coef)
                for coef in ADPCM_COEF:
                    extra_header += _ADPCM_WAVEFORMAT_COEF.pack(
                        coef[0], coef[1])
            elif c_format_tag == WB_FORMAT_TAG_WMA:
                if c_bits_per_sample == 1:
                    c_format_tag = WAVE_FORMAT_WMAUDIO3
                else:
                    c_format_tag = WAVE_FORMAT_WMAUDIO2
                c_bits_per_sample = 16
                c_avg_bytes_per_sec = WMA_AVG_BYTES_PER_SEC[c_block_align >> 5]
                c_block_align = WMA_BLOCK_ALIGN[c_block_align & 0x1f]
                if entry_seektables:
                    entry_dpds = entry_seektables[i]
                else:
                    raise ReaderError("No SEEKTABLES found for xWMA format")
            elif c_format_tag == WB_FORMAT_TAG_XMA:
                # lots of placeholders in here but seems to decode ok
                c_format_tag = WAVE_FORMAT_XMA2
                c_bits_per_sample = 16
                c_avg_bytes_per_sec = 0
                cx_num_streams = 1
                if c_channels == 2:
                    cx_channel_mask = 3
                else:
                    cx_channel_mask = 0
                cx_samples_encoded = 0
                cx_bytes_per_block = 0
                cx_play_begin = 0
                cx_play_length = 0
                cx_loop_begin = 0
                cx_loop_length = 0
                cx_loop_count = 0
                cx_encoder_version = 4
                cx_block_count = 1
                extra_header = _XMA_WAVEFORMAT.pack(
                    cx_num_streams, cx_channel_mask, cx_samples_encoded,
                    cx_bytes_per_block, cx_play_begin, cx_play_length,
                    cx_loop_begin, cx_loop_length, cx_loop_count,
                    cx_encoder_version, cx_block_count)
                if entry_seektables:
                    entry_seek = entry_seektables[i]
                else:
                    raise ReaderError("No SEEKTABLES found for XMA2 format")
            else:
                raise ReaderError(
                    "Unhandled entry format: {}".format(c_format_tag))
            cx_size = len(extra_header)
            entry_header = _WAVEFORMATEX.pack(c_format_tag, c_channels,
                                              c_samples_per_sec,
                                              c_avg_bytes_per_sec,
                                              c_block_align, c_bits_per_sample,
                                              cx_size)
            entry_header += extra_header
            # read entry wave data
            stream.seek(regions['ENTRYWAVEDATA'].offset + cur_meta.play_offset)
            # manually swap PCM data if needed
            entry_data = stream.read(cur_meta.play_length)
            if big_endian and c_format_tag == WAVE_FORMAT_PCM and c_bits_per_sample == 16:
                entry_data = bytearray(entry_data)
                entry_data[1::2], entry_data[0::2] = entry_data[
                    0::2], entry_data[1::2]
            self.entries.append(
                Entry(entry_name, entry_header, entry_data, entry_dpds,
                      entry_seek))
Beispiel #20
0
    def __init__(self, data=None, filename=None, audio_engine=None):
        self.audio_engine = audio_engine

        # open in little endian initially
        stream = BinaryStream(data=data, filename=filename)
        del data

        # check sig to find actual endianess
        h_sig = stream.peek(len(WB_L_SIGNATURE))
        if h_sig == WB_L_SIGNATURE:
            big_endian = False
        elif h_sig == WB_B_SIGNATURE:
            big_endian = True
        else:
            raise ValueError("bad sig: {!r}".format(h_sig))

        # switch stream to correct endianess
        stream.set_endian(big_endian)
        (h_sig, self.h_version, self.h_header_version) = stream.unpack(_WB_HEADER)
        regions = {k: XWBRegion._make(stream.unpack(_WB_REGION)) for k in _REGIONS}  # pylint: disable-msg=W0212

        # check if we have a valid BANKDATA region and parse it
        bankdata_size = stream.calc_size(_WB_DATA)
        if regions['BANKDATA'].length != bankdata_size:
            raise ReaderError("Invalid BANKDATA size: {} != {}".format(regions['BANKDATA'].length, bankdata_size))
        stream.seek(regions['BANKDATA'].offset)
        (self.flags, h_entry_count, h_bank_name_raw, h_entry_metadata_element_size, h_entry_name_element_size,
         self.alignment, h_compact_format, buildtime_raw_low, buildtime_raw_high) = stream.unpack(_WB_DATA)
        self.bank_name = h_bank_name_raw.rstrip(b'\x00').decode('iso8859-1')
        del h_bank_name_raw
        self.buildtime = filetime_to_datetime(buildtime_raw_low, buildtime_raw_high)

        if self.flags & ~(WB_TYPE_MASK | WB_FLAGS_MASK):
            raise ReaderError("Unknown flags in WAVEBANK")

        # check what type of ENTRYMETADATA we have and parse it
        if self.has_compact:
            raise ReaderError("Compact format not supported")
        bankentry_size = stream.calc_size(_WB_ENTRY)
        if bankentry_size != h_entry_metadata_element_size:
            raise ReaderError("Unknown EntryMetaDataElementSize: {} != {}".format(bankentry_size,
                                                                                  h_entry_metadata_element_size))
        if regions['ENTRYMETADATA'].length != bankentry_size * h_entry_count:
            raise ReaderError("Invalid ENTRYMETADATA size: {} != {}".format(regions['ENTRYMETADATA'].length,
                                                                            bankentry_size * h_entry_count))
        stream.seek(regions['ENTRYMETADATA'].offset)
        entry_metadata = [XWBEntry._make(stream.unpack(_WB_ENTRY))  # pylint: disable-msg=W0212,E1101
                          for _ in range(h_entry_count)]

        # read ENTRYNAMES if present
        entry_names = []
        if self.has_entry_names and regions['ENTRYNAMES'].offset and regions['ENTRYNAMES'].length:
            if regions['ENTRYNAMES'].length != h_entry_name_element_size * h_entry_count:
                raise ReaderError("Invalid ENTRYNAMES region size: {} != {}".format(
                    regions['ENTRYNAMES'].length, h_entry_name_element_size * h_entry_count))
            stream.seek(regions['ENTRYNAMES'].offset)
            entry_names = [stream.read(h_entry_name_element_size).rstrip(b'\x00').decode('iso8859-1')
                           for _ in range(h_entry_count)]

        # read SEEKTABLES if present
        entry_seektables = []
        if self.has_seek_tables and regions['SEEKTABLES'].offset and regions['SEEKTABLES'].length:
            stream.seek(regions['SEEKTABLES'].offset)
            seek_offsets = []
            for _ in range(h_entry_count):
                seek_offsets.append(stream.read_int32())
            seek_data_offset = stream.tell()
            for cur_offset in seek_offsets:
                if cur_offset >= 0:
                    stream.seek(seek_data_offset + cur_offset)
                    packet_count = stream.read_uint32()
                    cur_seek_data = BinaryStream()
                    for _ in range(packet_count):
                        cur_seek_data.write_uint32(stream.read_uint32())
                    entry_seektables.append(cur_seek_data.getvalue())
                else:
                    entry_seektables.append(None)

        self.entries = []
        for i, cur_meta in enumerate(entry_metadata):
            c_entry_flags = cur_meta.flags_duration & WB_ENTRY_FLAGS_MASK
            c_duration = (cur_meta.flags_duration & WB_ENTRY_DURATION_MASK) >> 4
            c_format_tag = cur_meta.format & WB_FORMAT_TAG_MASK
            c_channels = (cur_meta.format & WB_FORMAT_CHANNELS) >> 2
            c_samples_per_sec = (cur_meta.format & WB_FORMAT_SAMPLES_PER_SEC) >> 5
            c_block_align = (cur_meta.format & WB_FORMAT_BLOCK_ALIGN) >> 23
            c_bits_per_sample = (cur_meta.format & WB_FORMAT_BITS_PER_SAMPLE) >> 31
            entry_name = None
            if entry_names:
                entry_name = entry_names[i]
            entry_dpds = None
            entry_seek = None
            extra_header = bytes()
            # build format specific header and seek data
            if c_format_tag == WB_FORMAT_TAG_PCM:
                c_format_tag = WAVE_FORMAT_PCM
                if c_bits_per_sample == 1:
                    c_bits_per_sample = 16
                else:
                    c_bits_per_sample = 8
                c_avg_bytes_per_sec = c_samples_per_sec * c_block_align
            elif c_format_tag == WB_FORMAT_TAG_ADPCM:
                c_format_tag = WAVE_FORMAT_ADPCM
                c_bits_per_sample = 4
                c_block_align = (c_block_align + ADPCM_BLOCK_ALIGN_OFFSET) * c_channels
                cx_samples_per_block = ((c_block_align - (7 * c_channels)) * 8) // (c_bits_per_sample * c_channels) + 2
                c_avg_bytes_per_sec = (c_samples_per_sec // cx_samples_per_block) * c_block_align
                cx_num_coef = len(ADPCM_COEF)
                extra_header = _ADPCM_WAVEFORMAT.pack(cx_samples_per_block, cx_num_coef)
                for coef in ADPCM_COEF:
                    extra_header += _ADPCM_WAVEFORMAT_COEF.pack(coef[0], coef[1])
            elif c_format_tag == WB_FORMAT_TAG_WMA:
                if c_bits_per_sample == 1:
                    c_format_tag = WAVE_FORMAT_WMAUDIO3
                else:
                    c_format_tag = WAVE_FORMAT_WMAUDIO2
                c_bits_per_sample = 16
                c_avg_bytes_per_sec = WMA_AVG_BYTES_PER_SEC[c_block_align >> 5]
                c_block_align = WMA_BLOCK_ALIGN[c_block_align & 0x1f]
                if entry_seektables:
                    entry_dpds = entry_seektables[i]
                else:
                    raise ReaderError("No SEEKTABLES found for xWMA format")
            elif c_format_tag == WB_FORMAT_TAG_XMA:
                # lots of placeholders in here but seems to decode ok
                c_format_tag = WAVE_FORMAT_XMA2
                c_bits_per_sample = 16
                c_avg_bytes_per_sec = 0
                cx_num_streams = 1
                if c_channels == 2:
                    cx_channel_mask = 3
                else:
                    cx_channel_mask = 0
                cx_samples_encoded = 0
                cx_bytes_per_block = 0
                cx_play_begin = 0
                cx_play_length = 0
                cx_loop_begin = 0
                cx_loop_length = 0
                cx_loop_count = 0
                cx_encoder_version = 4
                cx_block_count = 1
                extra_header = _XMA_WAVEFORMAT.pack(cx_num_streams, cx_channel_mask, cx_samples_encoded,
                                                    cx_bytes_per_block, cx_play_begin, cx_play_length, cx_loop_begin,
                                                    cx_loop_length, cx_loop_count, cx_encoder_version, cx_block_count)
                if entry_seektables:
                    entry_seek = entry_seektables[i]
                else:
                    raise ReaderError("No SEEKTABLES found for XMA2 format")
            else:
                raise ReaderError("Unhandled entry format: {}".format(c_format_tag))
            cx_size = len(extra_header)
            entry_header = _WAVEFORMATEX.pack(c_format_tag, c_channels, c_samples_per_sec, c_avg_bytes_per_sec,
                                              c_block_align, c_bits_per_sample, cx_size)
            entry_header += extra_header
            # read entry wave data
            stream.seek(regions['ENTRYWAVEDATA'].offset + cur_meta.play_offset)
            # manually swap PCM data if needed
            entry_data = stream.read(cur_meta.play_length)
            if big_endian and c_format_tag == WAVE_FORMAT_PCM and c_bits_per_sample == 16:
                entry_data = bytearray(entry_data)
                entry_data[1::2], entry_data[0::2] = entry_data[0::2], entry_data[1::2]
            self.entries.append(Entry(entry_name, entry_header, entry_data, entry_dpds, entry_seek))