def read_object(self, expected_type_reader=None, type_params=None, expected_type=None): type_id = self.read_7bit_encoded_int() if type_id == 0: # null object return None try: type_reader = self.type_readers[type_id - 1] except IndexError: raise ReaderError("type id out of range: {} > {}".format( type_id, len(self.type_readers))) if expected_type_reader is not None: try: if expected_type_reader.is_generic_type and expected_type_reader.target_type is None: expected_type = generic_reader_type( expected_type_reader, type_params) elif expected_type_reader.is_enum_type: expected_type = generic_reader_type( EnumReader, [expected_type_reader.target_type]) else: expected_type = expected_type_reader.target_type except AttributeError: raise ReaderError("bad expected_type_reader: '{}'".format( expected_type_reader)) if expected_type is not None: if type_reader.target_type != expected_type: raise ReaderError("Unexpected type: '{}' != '{}'".format( type_reader.target_type, expected_type)) return type_reader.read()
def __init__(self, width, height, surface_format, data, needs_swap=False): if surface_format not in self._FORMATS: raise ReaderError( "Unknown DXT format: '{}'".format(surface_format)) if (width | height) & 3: raise ReaderError("Bad dimensions for DXT: {}x{}".format( width, height)) self.width = width self.height = height self.surface_format = surface_format self.data = data self.block_size = self._FORMATS[self.surface_format] stride = (self.width >> 2) * self.block_size expected_len = stride * (self.height >> 2) if len(self.data) != expected_len: raise ReaderError("Invalid data size for DXT: {} != {}".format( len(data), expected_len)) self.out_rows = [ bytearray([0] * self.width * 4), bytearray([0] * self.width * 4), bytearray([0] * self.width * 4), bytearray([0] * self.width * 4) ] if needs_swap: self.swap_struct = Struct('>HHHH') else: self.swap_struct = Struct('<HHHH') self.explicit_alphas = [] for cur_a in range(16): self.explicit_alphas.append(cur_a * 17)
def __init__(self): self.type_readers = {} self.type_readers_type = {} self.generic_type_readers = {} self.generic_type_readers_type = {} for class_ in TypeReaderPlugin.__subclasses__(): if issubclass(class_, GenericTypeReader): if class_.generic_reader_name in self.generic_type_readers: raise ReaderError( "Duplicate generic type reader name: '{}'".format( class_.generic_reader_name)) self.generic_type_readers[class_.generic_reader_name] = class_ if class_.generic_target_type in self.generic_type_readers_type: raise ReaderError( "Duplicate generic type reader type: '{}'".format( class_.generic_target_type)) self.generic_type_readers_type[ class_.generic_target_type] = class_ elif issubclass(class_, BaseTypeReader): if class_.reader_name in self.type_readers: raise ReaderError( "Duplicate type reader name: '{}'".format( class_.reader_name)) self.type_readers[class_.reader_name] = class_ if class_.target_type in self.type_readers_type: raise ReaderError( "Duplicate type reader type: '{}'".format( class_.target_type)) self.type_readers_type[class_.target_type] = class_ else: raise ReaderError( "Unknown base class for reader: '{!s}'".format(class_))
def get_type_reader_by_type(self, type_reader): try: reader_type = type_reader.target_type except AttributeError: reader_type = type_reader type_spec = TypeSpec.parse(reader_type) if type_spec.full_name in self.type_readers_type: return self.type_readers_type[type_spec.full_name] if type_spec.generic_params: if type_spec.name in self.generic_type_readers_type: generic_type_class = self.generic_type_readers_type[ type_spec.name] generic_type_reader_class = generic_type_class.create_from_type( type_spec) if generic_type_reader_class.reader_name in self.type_readers: raise ReaderError( "Duplicate type reader name from generic: '{}' '{}'". format(generic_type_reader_class.reader_name, generic_type_class.generic_reader_name)) self.type_readers[generic_type_reader_class. reader_name] = generic_type_reader_class if generic_type_reader_class.target_type in self.type_readers_type: raise ReaderError( "Duplicate type reader type from generic: '{}' '{}'". format(generic_type_reader_class.target_type, generic_type_class.generic_target_type)) self.type_readers_type[generic_type_reader_class. target_type] = generic_type_reader_class return generic_type_reader_class raise ReaderError("Type reader not found: '{}'".format( type_spec.full_name))
def __init__(self, stream): start_pos = stream.tell() (self.flags, self.category, self.volume, self.pitch, self.priority, entry_len) = stream.unpack(_SB_SOUND) if self.flags & ~SB_SOUND_FLAGS_MASK: raise ReaderError("Unknown flags in SB_SOUND") clip_count = 0 if self.is_complex: clip_count = stream.read_byte() else: self.track = stream.read_uint16() self.wavebank = stream.read_byte() if self.has_rpc_sound or self.has_rpc_track or self.has_rpc_effect: rpc_pos = stream.tell() rpc_extra = stream.read_uint16() # TODO: parse RPC data stream.seek(rpc_pos + rpc_extra) if self.has_dsp: dsp_pos = stream.tell() dsp_extra = stream.read_uint16() # TODO: parse DSP data stream.seek(dsp_pos + dsp_extra) self.clips = [] if self.is_complex: self.clips = [Clip(stream) for _ in range(clip_count)] if stream.tell() > entry_len + start_pos: raise ReaderError("SB_SOUND length mismatch")
def decode32(data, width, height, conv, alpha='yes'): if conv not in ('rgba_rgba', 'abgr_rgba', 'bgra_rgba', 'argb_rgba'): raise ReaderError("Unknown conversion: '{}'".format(conv)) if alpha not in ('yes', 'no', 'only'): raise ValueError("Invalid alpha parameter: '{}'".format(alpha)) stride = width * 4 expected_len = stride * height if len(data) != expected_len: raise ReaderError("Invalid data size: {} != {}".format( len(data), expected_len)) full_row_ff = bytearray([0xff] * width) for pos in range(0, len(data), stride): row = bytearray(data[pos:pos + stride]) if conv == 'bgra_rgba': row[2::4], row[1::4], row[0::4], row[3::4] = row[0::4], row[ 1::4], row[2::4], row[3::4] elif conv == 'argb_rgba': row[3::4], row[0::4], row[1::4], row[2::4] = row[0::4], row[ 1::4], row[2::4], row[3::4] elif conv == 'abgr_rgba': row[3::4], row[2::4], row[1::4], row[0::4] = row[0::4], row[ 1::4], row[2::4], row[3::4] if alpha == 'no': row[3::4] = full_row_ff elif alpha == 'only': row[0::4] = full_row_ff row[1::4] = full_row_ff row[2::4] = full_row_ff yield row
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)))
def decode8(data, width, height, conv): if conv not in ('a_xxxa', ): raise ReaderError("Unknown conversion: '{}'".format(conv)) stride = width expected_len = stride * height if len(data) != expected_len: raise ReaderError("Invalid data size: {} != {}".format( len(data), expected_len)) for pos in range(0, len(data), stride): row = bytearray([0xff] * width * 4) row[3::4] = data[pos:pos + stride] yield row
def __init__(self, root_dir): root_dir = os.path.normpath(root_dir) if not os.path.isdir(root_dir): raise ReaderError("Content root directory not found: '%s'" % root_dir) self.root_dir = root_dir self._asset_dict = OrderedDict(self.find_assets()) self.assets = self._asset_dict.keys()
def get_surface_format(xna_version, surface_format): try: if xna_version >= VERSION_40: return SurfaceFormat4(surface_format) else: return SurfaceFormat(surface_format) except KeyError: raise ReaderError("Invalid surface format for V{}: {}".format( xna_version, surface_format))
def read_type_id(self): type_id = self.read_7bit_encoded_int() if type_id == 0: # null object return None try: return self.type_readers[type_id - 1] except IndexError: raise ReaderError("type id out of range: {} > {}".format( type_id, len(self.type_readers)))
def __init__(self, name, stream, is_complex=False): self.name = name self.flags = stream.read_byte() if self.flags & ~SB_CUE_FLAGS_MASK: raise ReaderError("Unknown flags in SB_CUE") sound_offset = None unknown_offset = None variation_offset = None transition_offset = None if self.is_complex: if not is_complex: raise ReaderError( "SB_CUE_FLAGS_COMPLEX not set for complex cue") if self.has_sound: sound_offset = fix_offset(stream.read_int32()) else: variation_offset = fix_offset(stream.read_int32()) if self.has_transition: transition_offset = fix_offset(stream.read_int32()) else: unknown_offset = fix_offset(stream.read_int32()) if unknown_offset: raise ReaderError("unknown_offset set in complex cue") (self.limit, self.fade_in, self.fade_out, limit_fade_raw) = stream.unpack(_SB_LIMIT) self.fade_type = limit_fade_raw & 0x03 self.limit_type = limit_fade_raw >> 3 else: if is_complex: raise ReaderError("SB_CUE_FLAGS_COMPLEX is set for simple cue") if self.has_sound: sound_offset = fix_offset(stream.read_int32()) else: unknown_offset = fix_offset(stream.read_int32()) raise ReaderError("SB_CUE_FLAGS_SOUND not set for simple cue") next_cue_offset = stream.tell() if sound_offset: stream.seek(sound_offset) self.sound = Sound(stream) stream.seek(next_cue_offset)
def read_object(self, expected_type_reader=None, type_params=None, expected_type=None): type_id = self.read_7bit_encoded_int() if type_id == 0: # null object return None try: type_reader = self.type_readers[type_id - 1] except IndexError: raise ReaderError("type id out of range: {} > {}".format( type_id, len(self.type_readers))) if expected_type_reader is not None: try: if expected_type_reader.is_generic_type and expected_type_reader.target_type is None: expected_type = generic_reader_type( expected_type_reader, type_params) elif expected_type_reader.is_enum_type: expected_type = generic_reader_type( EnumReader, [expected_type_reader.target_type]) else: expected_type = expected_type_reader.target_type except AttributeError: raise ReaderError("bad expected_type_reader: '{}'".format( expected_type_reader)) if expected_type is not None: if expected_type != 'System.Object': if type_reader.target_type != expected_type: # check parent type readers for cls in type_reader.__class__.__mro__: if hasattr(cls, 'target_type'): if cls.target_type == expected_type: break else: raise ReaderError( "Unexpected type: '{}' != '{}'".format( type_reader.target_type, expected_type)) return type_reader.read()
def export(self, filename, export_file=True, export_xml=True): if not hasattr(self, 'content'): raise ReaderError("XNB content deleted") if self.content is None: self.parse() filename = os.path.normpath(filename) dirname = os.path.dirname(filename) if not os.path.isdir(dirname): os.makedirs(dirname) if export_file and hasattr(self.content, 'export'): self.content.export(filename) if export_xml and hasattr(self.content, 'xml'): output_xml(self.content.xml(), filename + '.xml')
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)
def full_data(self, alpha='yes'): if not self.surface_format.reader: raise ReaderError("No decoder found: '{}'".format( self.surface_format)) rows = self.surface_format.reader(self.mip_levels[0], self.width, self.height, self.needs_swap, alpha=alpha) data = bytearray() for row in rows: data.extend(row) return bytes(data)
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 parse(self, expected_type=None, verbose=False): if self.content is not None: return self.content reader_count = self.read_7bit_encoded_int() for _ in range(reader_count): reader_name = self.read_string() reader_version = self.read_int32() reader = self.get_type_reader(reader_name, reader_version) self.type_readers.append(reader) if verbose: print("Type: {!s}".format(self.type_readers[0])) for reader in self.type_readers: reader.init_reader(self.file_platform, self.file_version) shared_count = self.read_7bit_encoded_int() if shared_count: raise ReaderError("Shared resources present") self.content = self.read_object(expected_type=expected_type) if verbose: print("Asset: {!s}".format(self.content)) for i in range(shared_count): obj = self.read_object() self.shared_objects.append(obj) if verbose: print("Shared resource {}: {!s}".format(i, obj)) remaining = self.read() if len(remaining): raise ReaderError("remaining: {}".format(len(remaining))) return self.content
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
def export(self, filename): if not self.surface_format.reader: raise ReaderError("No decoder found: '{}'".format( self.surface_format)) dirname = os.path.dirname(filename) if not os.path.isdir(dirname): os.makedirs(dirname) # hack for ArtObject/TrileSet alpha channel alpha = 'yes' if 'art objects' in filename or 'trile sets' in filename: alpha = 'no' rows = self.surface_format.reader(self.mip_levels[0], self.width, self.height, self.needs_swap, alpha='only') write_png(filename + '_alpha', self.width, self.height, rows) rows = self.surface_format.reader(self.mip_levels[0], self.width, self.height, self.needs_swap, alpha=alpha) write_png(filename, self.width, self.height, rows)
def read(self): raise ReaderError("TextureReader should never be invoked directly")
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))
def read(self): raise ReaderError("ObjectReader should never be invoked directly")
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
def read(self): raise ReaderError('ObjectReader invoked for {}'.format( self.target_type))
def init_reader(self, file_platform=None, file_version=None): GenericValueTypeReader.init_reader(self, file_platform, file_version) if not self.readers[0].is_enum_type: ReaderError("Not enum type reader: '{}'".format(self.readers[0]))
def read(self): raise ReaderError('ModifierReader invoked for {}'.format( self.target_type))