def decompress(data: bytes): rdr = BinaryReader(data) wtr = BinaryWriter() type_ = rdr.read_uint8() if type_ != 0x30: raise Exception("Tried to decompress commands that isn't RLE") ds = rdr.read_uint24() if ds == 0: rdr.read_uint32() while True: flag = rdr.read_uint8() if flag is None: break # we've hit the end compressed = (flag & 0x80) > 0 length = flag & 0x7f length += 3 if compressed else 1 if compressed: next_ = rdr.read_uint8() for i in range(length): wtr.write_uint8(next_) else: wtr.write(rdr.read(length)) return wtr.data
def read(self, rdr: BinaryReader): self.chunk_id = rdr.read(4)[::-1] if self.chunk_id != b"CGLP": raise ValueError("CGLPChunk does not start with magic value") self.chunk_size = rdr.read_uint32() self.tile_width = rdr.read_uint8() self.tile_height = rdr.read_uint8() self.tile_bytes = rdr.read_uint16() bitmap_items = self.tile_width * self.tile_height self.underline_location = rdr.read_uint8() self.max_proportional_width = rdr.read_uint8() self.tile_depth = rdr.read_uint8() self.tile_rotation = rdr.read_uint8() self.tile_bitmaps = [] tile_bytes = self.tile_bytes tile_width = self.tile_width tile_height = self.tile_height tile_depth = self.tile_depth # To avoid byte boundaries efficiently # If bit depth is 2, we can read in blocks of 2 # If bit depth is 1, we can only read in blocks of 1 bit_steps = get_bit_steps(self.tile_depth) bit_mask = ((1 << bit_steps) - 1) for _ in range((self.chunk_size - 0x10) // tile_bytes): buffer = rdr.read(tile_bytes) bitmap = np.zeros(bitmap_items, dtype=np.uint8) current_bit = 0 for i in range(bitmap_items): # Get value from bytes to bit depth v = 0 for _ in range(tile_depth // bit_steps): # Shift the value bit steps v <<= bit_steps # Get the bit index and the byte index bit_i = current_bit % 8 byte_i = (current_bit - bit_i) // 8 # Read <bit steps> bits from byte (bit 7 is the MSB) byte = buffer[byte_i] bit = (byte >> (7 - bit_i)) & bit_mask # Add the value and change the current bit v += bit current_bit += bit_steps bitmap[i] = v bitmap.shape = tile_height, tile_width self.tile_bitmaps.append(bitmap)
def decompress(data: bytes) -> bytes: rdr = BinaryReader(data) wtr = BinaryWriter() compression_type = rdr.read_uint8() if compression_type == 0x24: blocksize = 4 elif compression_type == 0x28: blocksize = 8 else: raise Exception( "Tried to decompress something as huffman that isn't huffman") ds = rdr.read_uint24() if ds == 0: rdr.read_uint32() # Read the tree treesize = (rdr.read_uint8() + 1) * 2 tree_end = (rdr.c - 1) + treesize rootNode = HuffTreeNode.from_rdr(rdr, False, 5, tree_end) rdr.c = tree_end # Decompress with the tree bitsleft = 0 # amount of bits left to read from {commands} current_size = 0 currentNode = rootNode cashedbyte = -1 while current_size < ds: # Find next refrence to commands node while not currentNode.is_data: if bitsleft == 0: data = rdr.read_uint32() bitsleft = 32 bitsleft -= 1 nextIsOne = (data & (1 << bitsleft)) != 0 if nextIsOne: currentNode = currentNode.child1 else: currentNode = currentNode.child0 if blocksize == 8: current_size += 1 wtr.write_uint8(currentNode.data) elif blocksize == 4: if cashedbyte < 0: cashedbyte = currentNode.data else: cashedbyte |= currentNode.data << 4 wtr.write_uint8(cashedbyte) current_size += 1 cashedbyte = -1 currentNode = rootNode return wtr.data
def read(self, rdr: BinaryReader, fmt_chunk: FmtChunk): self.chunk_id = rdr.read(4) self.chunk_size = rdr.read_uint32() if fmt_chunk.bits_per_sample == 0x10: data = np.frombuffer(rdr.read(self.chunk_size), dtype="<h") elif fmt_chunk.bits_per_sample == 0x08: data = np.frombuffer(rdr.read(self.chunk_size), dtype="<b") else: raise NotImplementedError() data = data.reshape( (data.shape[0] // fmt_chunk.num_channels, fmt_chunk.num_channels)) self.data = data.swapaxes(0, 1)
def read_stream(self, stream: any): if isinstance(stream, BinaryReader): rdr: BinaryReader = stream else: rdr: BinaryReader = BinaryReader(stream) self.header = NFTRHeader() self.font_info = FINFChunk() self.char_glyph = CGLPChunk() self.char_width = CWDHChunk() self.char_maps = [] self.header.read(rdr) rdr.seek(self.header.offset_to_fnif) self.font_info.read(rdr) rdr.seek(self.font_info.offset_to_cglp_chunk) self.char_glyph.read(rdr) rdr.seek(self.font_info.offset_to_cwdh_chunk) self.char_width.read(rdr, len(self.char_glyph.tile_bitmaps)) next_offset = self.font_info.offset_to_cmap_chunk for i in range(self.header.following_chunk_count - 3): rdr.seek(next_offset) cmap = CMAPChunk() cmap.read(rdr) self.char_maps.append(cmap) next_offset = cmap.offset_to_next_cmap
def swd_read_samplebank(stream) -> SampleBank: rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) rdr.seek(0x0c) assert rdr.read_uint16() == 0x0415 # Correct version assert rdr.read_bool() # This is a SampleBank group = rdr.read_uint8() rdr.seek(0x20) label = rdr.read_string(16, pad=b"\xaa") samples = swd_read_samples(stream) return SampleBank(label, group, samples)
def read(self, rdr: BinaryReader): self.chunk_id = rdr.read(4) self.chunk_size = rdr.read_uint32() self.audio_format = rdr.read_uint16() self.num_channels = rdr.read_uint16() self.sample_rate = rdr.read_uint32() self.byte_rate = rdr.read_uint32() self.block_align = rdr.read_uint16() self.bits_per_sample = rdr.read_uint16()
def swd_read_presetbank(stream) -> PresetBank: rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) rdr.seek(0x0c) assert rdr.read_uint16() == 0x0415 # Correct version assert not rdr.read_bool() # This is not a SampleBank group = rdr.read_ubyte() rdr.seek(0x20) label = rdr.read_string(16, pad=b"\xaa") samples_info = swd_read_samples_info(stream) presets = swd_read_presets(stream, samples_info) return PresetBank(label, group, presets, samples_info)
def from_rdr(cls, rdr: BinaryReader, is_data: bool, relative_offset, max_stream_pos, parent=None): self = cls(is_data, parent=parent) if rdr.c >= max_stream_pos: return self.data = rdr.read_uint8() if not is_data: offset = self.data & 0x3F zeroIsData = (self.data & 0x80) > 0 oneIsData = (self.data & 0x40) > 0 # off AND NOT == off XOR (off AND 1) zeroRelOffset = (relative_offset ^ (relative_offset & 1)) + offset * 2 + 2 currStreamPos = rdr.c rdr.c += (zeroRelOffset - relative_offset) - 1 # Node after 0 self.child0 = HuffTreeNode.from_rdr(rdr, zeroIsData, zeroRelOffset, max_stream_pos, parent=self) # Node after 1 directly located after the node after 0 self.child1 = HuffTreeNode.from_rdr(rdr, oneIsData, zeroRelOffset + 1, max_stream_pos, parent=self) # reset stream rdr.c = currStreamPos return self
def read_stream(self, stream: BinaryIO): if isinstance(stream, BinaryReader): rdr = stream else: rdr = BinaryReader(stream) n_entries = rdr.read_uint16() header_length = rdr.read_uint16() entry_length = rdr.read_uint16() rdr.seek(header_length) self._entries = [] for i in range(n_entries): self._entries.append(rdr.read(entry_length))
def read(self, rdr: BinaryReader): self.magic_value = rdr.read(4)[::-1] if self.magic_value != b"NFTR": raise ValueError("NFTRHeader does not start with magic value") self.byte_order = rdr.read_uint16() self.version = rdr.read_uint16() self.decompressed_resource_size = rdr.read_uint32() self.offset_to_fnif = rdr.read_uint16() self.following_chunk_count = rdr.read_uint16()
def read(self, rdr: BinaryReader, tile_count: int): self.chunk_id = rdr.read(4)[::-1] if self.chunk_id != b"CWDH": raise ValueError("CWDHChunk does not start with magic value") self.chunk_size = rdr.read_uint32() self.first_tile_no = rdr.read_uint16() self.last_tile_no = rdr.read_uint16() rdr.read(4) self.left_spacing = [] self.width = [] self.total_width = [] for i in range(tile_count): left_spacing = rdr.read_uint8() width = rdr.read_uint8() total_width = rdr.read_uint8() self.left_spacing.append(left_spacing) self.width.append(width) self.total_width.append(total_width)
def swd_read_sections(stream) -> Dict[str, Tuple[int, int]]: rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) rdr.seek(0x50) # goto the start of the sections sections: Dict[str, Tuple[int, int]] = { } # dict of the name of the section and then the start and length. while rdr.tell() < len(rdr): header_start = rdr.tell() name = rdr.read_string(4) rdr.seek(4, SEEK_CUR) header_length = rdr.read_uint32() start = header_start + header_length length = rdr.read_uint32() sections[name] = (start, length) rdr.seek(start + length) rdr.align(0x10) return sections
def swd_read_presets(stream, samples_info) -> Dict[int, Preset]: rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) rdr.seek(0x48) n_prgi_slots = rdr.read_uint16() sections = swd_read_sections(stream) prgi_offset, prgi_len = sections["prgi"] presets: Dict[int, Preset] = {} rdr.seek(prgi_offset) for preset_index, preset_section_offset in enumerate( rdr.read_uint16_array(n_prgi_slots)): if not preset_section_offset: continue preset_offset = prgi_offset + preset_section_offset rdr.seek(preset_offset) assert rdr.read_uint16() == preset_index n_splits = rdr.read_uint16() rdr.seek(preset_offset + 0xb) n_lfos = rdr.read_uint8() rdr.seek(preset_offset + 0x10) lfos = [] for lfo_index in range(n_lfos): lfo_offset = rdr.tell() rdr.seek(lfo_offset + 2) destination = LFODestination(rdr.read_int8()) wave_shape = LFOWaveShape(rdr.read_int8()) rate = rdr.read_uint16() depth = rdr.read_uint16() delay = rdr.read_uint16() lfos.append(LFO(destination, wave_shape, rate, depth, delay)) rdr.seek(preset_offset + 0x10 + 0x10 * n_lfos + 0x10) splits = [] for split_index in range(n_splits): split_offset = rdr.tell() rdr.seek(split_offset + 0x04) lowkey = rdr.read_int8() highkey = rdr.read_int8() rdr.seek(split_offset + 0x12) sample_index = rdr.read_uint16() tuning = rdr.read_int8() rdr.seek(split_offset + 0x16) rootkey = rdr.read_int8() rdr.seek(split_offset + 0x30) splits.append( SplitEntry(highkey, lowkey, samples_info[sample_index], tuning, rootkey)) presets[preset_index] = Preset(splits, lfos) return presets
def read_stream(self, stream: BinaryIO): if isinstance(stream, BinaryReader): rdr = stream else: rdr = BinaryReader(stream) self.smdl_header = SMDLHeader() self.song_chunk = SongChunk() self.tracks = [] self.eoc_chunk = EOCChunk() self.smdl_header.read(rdr) self.song_chunk.read(rdr) for i in range(self.song_chunk.num_tracks): new_track = Track() new_track.read(rdr) self.tracks.append(new_track) self.eoc_chunk.read(rdr)
def swd_read_samples_info(stream) -> Dict[int, SampleInfo]: rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) rdr.seek(0x46) n_wavi_slots = rdr.read_uint16() sections = swd_read_sections(stream) wavi_offset, wavi_len = sections["wavi"] samples_info: Dict[int, SampleInfo] = {} rdr.seek(wavi_offset) for sample_index, sample_info_section_offset in enumerate( rdr.read_uint16_array(n_wavi_slots)): if not sample_info_section_offset: continue sample_info_offset = wavi_offset + sample_info_section_offset rdr.seek(sample_info_offset + 0x04) tuning = rdr.read_int8() rdr.seek(sample_info_offset + 0x15) loop_enabled = rdr.read_bool() rdr.seek(sample_info_offset + 0x28) loop = rdr.read_uint32() * 8 - 9 samples_info[sample_index] = SampleInfo(sample_index, loop_enabled, loop, tuning) return samples_info
def swd_read_samples(stream) -> Dict[int, Sample]: rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) rdr.seek(0x46) n_wavi_slots = rdr.read_uint16() sections = swd_read_sections(stream) wavi_offset, wavi_len = sections["wavi"] pcmd_offset, pcmd_len = sections["pcmd"] samples: Dict[int, Sample] = {} rdr.seek(wavi_offset) for sample_index, sample_info_offset in enumerate( rdr.read_uint16_array(n_wavi_slots)): if not sample_info_offset: continue rdr.seek(wavi_offset + sample_info_offset + 0x20) samplerate = rdr.read_uint32() adpcm_pos = rdr.read_uint32() adpcm_loop_position = rdr.read_uint32() * 4 adpcm_loop_lenght = rdr.read_uint32() * 4 adpcm_lenght = (adpcm_loop_position + adpcm_loop_lenght) rdr.seek(pcmd_offset + adpcm_pos) samples[sample_index] = Sample(samplerate, rdr.read(adpcm_lenght)) return samples
def read_stream(self, stream: BinaryIO): if isinstance(stream, BinaryReader): rdr = stream else: rdr = BinaryReader(stream) self.commands = [] self.params = [] file_length = rdr.read_uint32() while rdr.c < file_length: datatype = rdr.read_uint16() if datatype == 0: break elif datatype == 1: self.params.append(rdr.read_uint32()) elif datatype == 2: self.params.append(rdr.read_float()) elif datatype == 3: self.params.append(rdr.read_string(rdr.read_uint16())) elif datatype == 0xc: return while rdr.c < file_length: self.commands.append(command := GDSCommand(rdr.read_uint16())) while rdr.c < file_length: datatype = rdr.read_uint16() if datatype == 0: break if datatype == 1: command.params.append(rdr.read_uint32()) elif datatype == 2: command.params.append(rdr.read_float()) elif datatype == 3: command.params.append(rdr.read_string(rdr.read_uint16())) elif datatype == 0xc: return
def compress(data: bytes): rdr = BinaryReader(data) wtr = BinaryWriter() wtr.write_uint8(0x30) # rle identifier wtr.write_uint24(len(data) if len(data) < 0xffffff else 0) if len(data) > 0xffffff: wtr.write_uint32(len(data)) repCount = 1 currentBlockLenght = 0 dataBlock = [0 for _ in range(130)] while rdr.c < len(rdr.data): foundRepetition = False while (currentBlockLenght < 130 and rdr.c < len(rdr.data)): nextByte = rdr.read_uint8() dataBlock[currentBlockLenght] = nextByte currentBlockLenght += 1 if (currentBlockLenght > 1): if nextByte == dataBlock[currentBlockLenght - 2]: repCount += 1 else: repCount = 1 foundRepetition = repCount > 2 if foundRepetition: break if foundRepetition: numUncompToCopy = currentBlockLenght - 3 else: numUncompToCopy = min(currentBlockLenght, 130 - 2) if numUncompToCopy > 0: flag = numUncompToCopy - 1 wtr.write_uint8(flag) for i in range(numUncompToCopy): wtr.write_uint8(dataBlock[i]) for i in range(numUncompToCopy, currentBlockLenght): dataBlock[i - numUncompToCopy] = dataBlock[i] currentBlockLenght -= numUncompToCopy if foundRepetition: while currentBlockLenght < 130 and rdr.c < len(rdr.data): nextByte = rdr.read_uint8() dataBlock[currentBlockLenght] = nextByte currentBlockLenght += 1 if nextByte != dataBlock[0]: break else: repCount += 1 flag = 0x80 | (repCount - 3) wtr.write_uint8(flag) wtr.write_uint8(dataBlock[0]) if (repCount != currentBlockLenght): dataBlock[0] = dataBlock[currentBlockLenght - 1] currentBlockLenght -= repCount if currentBlockLenght > 0: flag = currentBlockLenght - 1 wtr.write_uint8(flag) for i in range(currentBlockLenght): wtr.write_uint8(dataBlock[i]) currentBlockLenght = 0 return wtr.data
def load(self, rdr): if not isinstance(rdr, BinaryReader): rdr = BinaryReader(rdr) rdr: BinaryReader self.original = rdr.read() rdr.seek(0) self.number = rdr.read_uint16() rdr.read_uint16() # 112 self.title = rdr.read_string(encoding=self.encoding) rdr.seek(0x34) self.tutorial_id = rdr.read_uint8() for i in range(3): self.picarat_decay[i] = rdr.read_uint8() self._flags = rdr.read_uint8() self.location_id = rdr.read_uint8() self.type = rdr.read_uint8() self.bg_btm_id = rdr.read_uint8() rdr.read_uint16() self.bg_top_id = rdr.read_uint8() self.reward_id = rdr.read_uint8() puzzle_text_offset = 0x70 + rdr.read_uint32() puzzle_correct_answer_offset = 0x70 + rdr.read_uint32() puzzle_incorrect_answer_offset = 0x70 + rdr.read_uint32() puzzle_hint1_offset = 0x70 + rdr.read_uint32() puzzle_hint2_offset = 0x70 + rdr.read_uint32() puzzle_hint3_offset = 0x70 + rdr.read_uint32() rdr.seek(puzzle_text_offset) self.text = subs.replace_substitutions(rdr.read_string(encoding=self.encoding), True) rdr.seek(puzzle_correct_answer_offset) self.correct_answer = subs.replace_substitutions(rdr.read_string(encoding=self.encoding), True) rdr.seek(puzzle_incorrect_answer_offset) self.incorrect_answer = subs.replace_substitutions(rdr.read_string(encoding=self.encoding), True) rdr.seek(puzzle_hint1_offset) self.hint1 = subs.replace_substitutions(rdr.read_string(encoding=self.encoding), True) rdr.seek(puzzle_hint2_offset) self.hint2 = subs.replace_substitutions(rdr.read_string(encoding=self.encoding), True) rdr.seek(puzzle_hint3_offset) self.hint3 = subs.replace_substitutions(rdr.read_string(encoding=self.encoding), True)
def read_stream(self, stream): if not isinstance(stream, BinaryReader): rdr = BinaryReader(stream) else: rdr = stream self.chunk_id = rdr.read(4) self.chunk_size = rdr.read_uint32() self.format = rdr.read(4) if self.chunk_id != b"RIFF" or self.format != b"WAVE": raise NotImplementedError() self.fmt.read(rdr) if self.fmt.audio_format != WaveFormat.WAVE_FORMAT_PCM or self.fmt.bits_per_sample == 0x08: raise NotImplementedError() rdr.seek(0x14 + self.fmt.chunk_size) data_id = rdr.read(4) while data_id != b"data": offset = rdr.read_uint32() rdr.seek(rdr.tell() + offset) data_id = rdr.read(4) rdr.seek(-4, os.SEEK_CUR) self.data.read(rdr, self.fmt)
def compress(data: bytes): rdr = BinaryReader(data) wtr = BinaryWriter() wtr.write_uint8(0x30) # rle identifier wtr.write_uint24(len(data) if len(data) < 0xffffff else 0) if len(data) > 0xffffff: wtr.write_uint32(len(data)) rep_count = 1 current_block_length = 0 data_block = [0 for _ in range(130)] while rdr.c < len(rdr.data): found_repetition = False while current_block_length < 130 and rdr.c < len(rdr.data): next_byte = rdr.read_uint8() data_block[current_block_length] = next_byte current_block_length += 1 if current_block_length > 1: if next_byte == data_block[current_block_length - 2]: rep_count += 1 else: rep_count = 1 found_repetition = rep_count > 2 if found_repetition: break if found_repetition: num_uncomp_to_copy = current_block_length - 3 else: num_uncomp_to_copy = min(current_block_length, 130 - 2) if num_uncomp_to_copy > 0: flag = num_uncomp_to_copy - 1 wtr.write_uint8(flag) for i in range(num_uncomp_to_copy): wtr.write_uint8(data_block[i]) for i in range(num_uncomp_to_copy, current_block_length): data_block[i - num_uncomp_to_copy] = data_block[i] current_block_length -= num_uncomp_to_copy if found_repetition: while current_block_length < 130 and rdr.c < len(rdr.data): next_byte = rdr.read_uint8() data_block[current_block_length] = next_byte current_block_length += 1 if next_byte != data_block[0]: break else: rep_count += 1 flag = 0x80 | (rep_count - 3) wtr.write_uint8(flag) wtr.write_uint8(data_block[0]) if rep_count != current_block_length: data_block[0] = data_block[current_block_length - 1] current_block_length -= rep_count if current_block_length > 0: flag = current_block_length - 1 wtr.write_uint8(flag) for i in range(current_block_length): wtr.write_uint8(data_block[i]) return wtr.data
def read(self, br: BinaryReader): br.seek(0x40, io.SEEK_SET) self.label = br.read(4) if self.label != b"song": raise ValueError("SongChunk does not start with magic value") self.unk1 = br.read_uint32() br.read_uint32() # 0xFF10 br.read_uint32() # 0xFFFFFFB0 br.read_uint16() # 0x1 self.tpqn = br.read_uint16() br.read_uint16() # 0xFF10 self.num_tracks = br.read_uint8() self.num_channels = br.read_uint8() br.read_uint32() # 0x0F000000 br.read_uint32() # 0xFFFFFFFF br.read_uint32() # 0x40000000 br.read_uint32() # 0x00404000 br.read_uint16() # 0x0200 br.read_uint16() # 0x0800 br.read_uint32() # 0xFFFFFF00 br.read(16) # 16 0xFF Padding
def read(self, br: BinaryReader): br.seek(0, io.SEEK_SET) self.magic = br.read(4) if self.magic != b"smdl": raise ValueError("SMDHeader does not start with magic value") br.read_uint32() # 0 self.file_length = br.read_uint32() self.version = br.read_uint16() self.unk1 = br.read_uint8() self.unk2 = br.read_uint8() # 8 bytes of 0 br.read_uint32() br.read_uint32() self.year = br.read_uint16() self.month = br.read_uint8() self.day = br.read_uint8() self.hour = br.read_uint8() self.minute = br.read_uint8() self.second = br.read_uint8() self.centisecond = br.read_uint8() self.file_name = br.read_string(16, encoding=None, pad=b"\xFF") br.read_uint32() # 0x1 br.read_uint32() # 0x1 br.read_uint32() # 0xFFFFFFFF br.read_uint32() # 0xFFFFFFFF
def read(self, br: BinaryReader, track_header: TrackChunkHeader): eb = br.read_char_array(track_header.chunk_length - 4) self.event_bytes = b"".join(eb)
def read(self, br: BinaryReader): self.track_id = br.read_uint8() self.channel_id = br.read_uint8() self.unk1 = br.read_uint8() self.unk2 = br.read_uint8()
def read_stream(self, stream: BinaryIO): if isinstance(stream, BinaryReader): rdr = stream else: rdr = BinaryReader(stream) with open("test.bin", "wb+") as f: f.write(rdr.read()) rdr.seek(0) palette_lenght = rdr.read_uint32() for color_i in range(palette_lenght): self.palette[color_i] = ndspy.color.unpack255(rdr.read_uint16()) if color_i: self.palette[color_i, 3] = 255 n_tiles = rdr.read_uint32() tiles = np.frombuffer(rdr.read(n_tiles * 0x40), np.uint8).reshape((n_tiles, 8, 8)) map_w = rdr.read_uint16() map_h = rdr.read_uint16() img_w = map_w * 8 img_h = map_h * 8 self.image = np.zeros((img_h, img_w), np.uint8) for map_y in range(map_h): for map_x in range(map_w): img_y = map_y * 8 img_x = map_x * 8 self.image[img_y:img_y + 8, img_x:img_x + 8] = tiles[rdr.read_uint16()]
def read_stream(self, stream: BinaryIO): if isinstance(stream, BinaryReader): rdr = stream else: rdr = BinaryReader(stream) n_images = rdr.read_uint16() self.colordepth = 4 if rdr.read_uint16() == 3 else 8 # import the images self.images = [] for img_i in range(n_images): img_w = rdr.read_uint16() img_h = rdr.read_uint16() n_parts = rdr.read_uint16() img = np.zeros((img_h, img_w), np.uint8) rdr.seek(2, SEEK_CUR) for part_i in range(n_parts): part_x = rdr.read_uint16() part_y = rdr.read_uint16() part_w = 2**(3 + rdr.read_uint16()) part_h = 2**(3 + rdr.read_uint16()) part: np.ndarray if self.colordepth == 8: part = np.frombuffer(rdr.read(part_h * part_w), np.uint8) part = part.reshape((part_h, part_w)) else: bufpart = np.frombuffer(rdr.read(part_h * part_w // 2), np.uint8) part = np.zeros((part_w * part_h), np.uint8) part[0::2] = bufpart & 0xf part[1::2] = bufpart >> 4 part = part.reshape((part_h, part_w)) if (part_x + part_w) > img_w: part_w = img_w - part_x if (part_y + part_h) > img_h: part_h = img_h - part_y img[part_y:part_y + part_h, part_x:part_x + part_w] = part[:part_h, :part_w] self.images.append(img) self.palette = np.zeros((256, 4), np.uint8) palette_length = rdr.read_uint32() for color_i in range(palette_length): self.palette[color_i] = ndspy.color.unpack255(rdr.read_uint16()) if color_i: self.palette[color_i, 3] = 255 rdr.seek(0x1E, SEEK_CUR) n_animations = rdr.read_uint32() animation_names = rdr.read_string_array(n_animations, 0x1e) animation_frame_sets = [] for i in range(n_animations): n_frames = rdr.read_uint32() frame_indexes = rdr.read_uint32_array(n_frames) frame_durations = rdr.read_uint32_array(n_frames) image_indexes = rdr.read_uint32_array(n_frames) animation_frame_sets.append([ AnimationFrame(index=frame_indexes[i], duration=frame_durations[i], image_index=image_indexes[i]) for i in range(n_frames) ]) self.animations = [ Animation(name=animation_names[i], frames=animation_frame_sets[i]) for i in range(n_animations) ] if rdr.read_uint16() != 0x1234: return # We hit end of stream variable_labels = rdr.read_string_array(16, 16) variable_data = [[] for _ in range(16)] for _ in range(8): for var_i in range(16): variable_data[var_i].append(rdr.read_int16()) self.variables = { variable_labels[i]: variable_data[i] for i in range(16) } for anim in self.animations: anim.child_image_x = rdr.read_uint16() for anim in self.animations: anim.child_image_y = rdr.read_uint16() for anim in self.animations: anim.child_image_animation_index = rdr.read_uint8() self.child_image = rdr.read_string(128)
def read_stream(self, stream): if not isinstance(stream, BinaryReader): rdr = BinaryReader(stream) else: rdr = stream self.original_header = rdr.read(0x100) rdr.seek(0) self.chunk_id = rdr.read(4) if self.chunk_id != b"sadl": raise ValueError("SADL does not start with magic value") rdr.seek(0x31) self.loop_flag = rdr.read_uint8() self.channels = rdr.read_uint8() coding = rdr.read_uint8() if coding & 0x06 == 4: self.sample_rate = 32728 elif coding & 0x06 == 2: self.sample_rate = 16364 self.coding = coding & 0xf0 rdr.seek(0x40) self.file_size = rdr.read_uint32() if self.coding == Coding.INT_IMA: self.num_samples = int((self.file_size - 0x100) / self.channels * 2) elif self.coding == Coding.NDS_PROCYON: self.num_samples = int((self.file_size - 0x100) / self.channels / 16 * 30) else: raise NotImplementedError() rdr.seek(0x54) if self.loop_flag != 0: if self.coding == Coding.INT_IMA: self.loop_offset = int((rdr.read_uint32() - 0x100) / self.channels * 2) elif self.coding == Coding.NDS_PROCYON: self.loop_offset = int((rdr.read_uint32() - 0x100) / self.channels / 16 * 30) rdr.seek(0x100) buffer = rdr.read(self.file_size - 0x100) buffer = np.frombuffer(buffer, dtype=np.uint8) buffer = buffer.reshape(((self.file_size - 0x100) // 0x10 // self.channels, self.channels, 0x10)) buffer = buffer.swapaxes(0, 1) self.buffer = buffer.reshape((buffer.shape[0], buffer.shape[1] * buffer.shape[2])) self.offset = [0] * self.channels self.ima_decoders = [] self.procyon_decoders = [] for i in range(self.channels): self.ima_decoders.append(ima_adpcm.ImaAdpcm()) self.procyon_decoders.append(procyon.Procyon()) self.blocks_done = 0
def read_stream(self, stream: BinaryIO): rdr = stream if isinstance(stream, BinaryReader) else BinaryReader(stream) n_images = rdr.read_uint16() self.colordepth = 4 if rdr.read_uint16() == 3 else 8 palette_length = rdr.read_uint32() # import the images self.images = [] for img_i in range(n_images): img_w = rdr.read_uint16() img_h = rdr.read_uint16() n_parts = rdr.read_uint16() img = np.zeros((img_h, img_w), np.uint8) rdr.seek(2, SEEK_CUR) for part_i in range(n_parts): _part_glb_x = rdr.read_uint16() _part_glb_y = rdr.read_uint16() part_x = rdr.read_uint16() part_y = rdr.read_uint16() part_w = 2 ** (3 + rdr.read_uint16()) part_h = 2 ** (3 + rdr.read_uint16()) part: np.ndarray if self.colordepth == 8: part = np.frombuffer(rdr.read(part_h * part_w), np.uint8) else: bufpart = np.frombuffer(rdr.read(part_h * part_w // 2), np.uint8) part = np.zeros((part_w * part_h), np.uint8) part[0::2] = bufpart & 0xf part[1::2] = bufpart >> 4 part = part.reshape((part_h, part_w)) part.resize((part_h // 8, part_w // 8, 8, 8)) part_w = min(img_w - part_x, part_w) part_h = min(img_h - part_y, part_h) for yt, yslice in enumerate(part): for xt, xslice in enumerate(yslice): offset_y = part_y + yt * 8 offset_x = part_x + xt * 8 end_y = min(part_y + yt * 8 + 8, part_y + part_h) end_x = min(part_x + xt * 8 + 8, part_x + part_w) copy_h = max(end_y - offset_y, 0) copy_w = max(end_x - offset_x, 0) img[offset_y:end_y, offset_x:end_x] = \ xslice[:copy_h, :copy_w] self.images.append(img) self.palette = np.zeros((256, 4), np.uint8) for color_i in range(palette_length): self.palette[color_i] = ndspy.color.unpack255(rdr.read_uint16()) if color_i: self.palette[color_i, 3] = 255 rdr.seek(0x1E, SEEK_CUR) n_animations = rdr.read_uint32() animation_names = rdr.read_string_array(n_animations, 0x1e) animation_frame_sets = [] for i in range(n_animations): n_frames = rdr.read_uint32() frame_indexes = rdr.read_uint32_array(n_frames) frame_durations = rdr.read_uint32_array(n_frames) image_indexes = rdr.read_uint32_array(n_frames) animation_frame_sets.append([ AnimationFrame(index=frame_indexes[i], duration=frame_durations[i], image_index=image_indexes[i]) for i in range(n_frames)]) self.animations = [Animation(name=animation_names[i], frames=animation_frame_sets[i]) for i in range(n_animations)] if rdr.read_uint16() != 0x1234: return # We hit end of stream variable_labels = rdr.read_string_array(16, 16) variable_data = [[] for _ in range(16)] for _ in range(8): for var_i in range(16): variable_data[var_i].append(rdr.read_int16()) self.variables = {variable_labels[i]: variable_data[i] for i in range(16)} for anim in self.animations: anim.child_image_x = rdr.read_int16() for anim in self.animations: anim.child_image_y = rdr.read_int16() for anim in self.animations: anim.child_spr_index = rdr.read_uint8() self.child_image = rdr.read_string(128)