def save(self): metadata = binary.BinaryWriter() sectionName = binary.BinaryWriter() sectionData = binary.BinaryWriter() for fileIndex, fileChunk in enumerate(self.files): metadata.writeU4(sectionName.tell()) metadata.writeU4(sectionData.tell()) metadata.writeU4(len(fileChunk.data)) sectionName.writeString(fileChunk.name, 'shift-jis') if fileIndex < len(self.files): sectionName.write(b'\x00') sectionData.write(fileChunk.data) sectionData.dsAlign(4, 4) sectionName.dsAlign(4, 4) writer = binary.BinaryWriter() writer.write(b'LPC2') writer.writeU4(len(self.files)) writer.writeU4(LaytonPack2.HEADER_BLOCK_SIZE + metadata.tell() + sectionName.tell()) writer.writeU4(0) # EOFC, not written until end writer.writeU4(LaytonPack2.HEADER_BLOCK_SIZE) writer.writeU4(LaytonPack2.HEADER_BLOCK_SIZE + metadata.tell()) writer.writeU4(LaytonPack2.HEADER_BLOCK_SIZE + metadata.tell() + sectionName.tell()) writer.pad(LaytonPack2.HEADER_BLOCK_SIZE - writer.tell()) writer.write(metadata.data) writer.write(sectionName.data) writer.write(sectionData.data) writer.insert(writer.tell().to_bytes(4, byteorder = 'little'), 12) self.data = writer.data
def compressRle(self, addHeader=False): writer = binary.BinaryWriter() reader = binary.BinaryReader(data = self.data) tempCompressedByte = b'' tempCompressedByteLength = 0 tempUncompressedSection = bytearray(b'') compressRepetition = False def getRleFlagByte(isCompressed, length): if isCompressed: return (0x80 | (length - 3)).to_bytes(1, byteorder = 'little') # Enable MSB compression flag return (length - 1).to_bytes(1, byteorder = 'little') def writeData(): if len(tempUncompressedSection) > 0: writer.write(getRleFlagByte(False, len(tempUncompressedSection)) + tempUncompressedSection) if tempCompressedByteLength > 0: writer.write(getRleFlagByte(True, tempCompressedByteLength) + tempCompressedByte) while reader.hasDataRemaining(): tempByte = reader.read(1) if compressRepetition: if tempByte == tempCompressedByte: tempCompressedByteLength += 1 if tempCompressedByteLength == 130 or tempByte != tempCompressedByte: # If max size has been reached or there's no more repetition compressRepetition = False if tempCompressedByteLength < 3: # Free data if compression won't do much tempUncompressedSection.extend((tempCompressedByte * tempCompressedByteLength) + tempByte) else: # Else, write uncompressed section, then compressed data writeData() if tempByte == tempCompressedByte: # If the compression ended because the max block size was met, tempUncompressedSection = bytearray(b'') # reinitiate the uncompressed section. else: tempUncompressedSection = bytearray(tempByte) # Else, continue the uncompressed section as normal. tempCompressedByteLength = 0 else: tempUncompressedSection.extend(tempByte) if len(tempUncompressedSection) == 128: # Reinitiate block if max size met writeData() tempUncompressedSection = bytearray(b'') elif len(tempUncompressedSection) > 1 and tempUncompressedSection[-2] == tempUncompressedSection[-1]: tempCompressedByte = tempByte tempCompressedByteLength = 2 compressRepetition = True tempUncompressedSection = tempUncompressedSection[0:-2] # Write anything left, as there may be blocks remaining after the reader ran out of data writeData() if addHeader: self.data = bytearray(File.LAYTON_1_COMPRESSION[File.COMP_RLE] + File.COMP_RLE + len(self.data).to_bytes(3, byteorder = 'little') + writer.data) else: self.data = bytearray(File.COMP_RLE + len(self.data).to_bytes(3, byteorder = 'little') + writer.data)
def decompressHuffman(self, offsetIn=0): reader = binary.BinaryReader(data = self.data) reader.seek(offsetIn) magic = reader.readUInt(1) if magic & 0xF0 != 0x20: return False elif magic & 0x0F == 0x04: useHalfByteBlocks = True else: useHalfByteBlocks = False tempFilesize = reader.readUInt(3) tempTreeLength = (reader.readUInt(1) * 2) + 1 tree = _HuffmanTree.decode(reader, offsetIn, offsetIn + tempTreeLength + 5) reader.seek(offsetIn + tempTreeLength + 5) writer = binary.BinaryWriter() bitsLeft = 0 currentNode = tree.root isMsbNibble = True while writer.tell() < tempFilesize: # Ported from DsDecmp while currentNode.data == None: if bitsLeft == 0: data = reader.readU4() bitsLeft = 32 bitsLeft-=1 nextIsRight = (data & (1 << bitsLeft)) != 0 if nextIsRight: currentNode = currentNode.right else: currentNode = currentNode.left if useHalfByteBlocks: if isMsbNibble: tempIntData = int.from_bytes(currentNode.data, byteorder = 'little') << 4 else: tempIntData |= int.from_bytes(currentNode.data, byteorder = 'little') writer.writeInt(tempIntData, 1) isMsbNibble = not(isMsbNibble) else: writer.write(currentNode.data) currentNode = tree.root if useHalfByteBlocks and not(isMsbNibble): writer.writeInt(tempIntData, 1) self.data = writer.data[:tempFilesize] return True
def encode(self): writer = binary.BinaryWriter() writer.write(b'\x00') breadthQueue = [self.root] # Ported from DsDecmp while len(breadthQueue) > 0: node = breadthQueue[0] breadthQueue = breadthQueue[1:] if node.data != None: writer.write(node.data) else: tempData = (len(breadthQueue) // 2) & 0x3F if node.left.data != None: tempData = tempData | 0x80 if node.right.data != None: tempData = tempData | 0x40 breadthQueue.extend((node.left, node.right)) writer.writeInt(tempData, 1) writer.insert(((writer.tell() // 2) - 1).to_bytes(1, byteorder = 'little'), 0) return writer.data
def decompressRle(self, offsetIn=0): reader = binary.BinaryReader(data = self.data) reader.seek(offsetIn) if reader.readUInt(1) != File.COMP_RLE: return False tempFilesize = reader.readUInt(3) writer = binary.BinaryWriter() while writer.tell() < tempFilesize: flag = int.from_bytes(reader.read(1), byteorder = 'little') isCompressed = (flag & 0x80) > 0 if isCompressed: decompressedLength = (flag & 0x7f) + 3 decompressedData = reader.read(1) for _indexByte in range(decompressedLength): writer.write(decompressedData) else: decompressedLength = (flag & 0x7f) + 1 writer.write(reader.read(decompressedLength)) self.data = writer.data return True
def save(self): writer = binary.BinaryWriter() countColours = countPilPaletteColours(self.image) writer.writeU4(countColours) for colour in pilPaletteToRgbTriplets(self.image)[0:countColours]: r, g, b = colour tempEncodedColour = (b << 7) + (g << 2) + (r >> 3) writer.writeU2(tempEncodedColour) tiles = [] tilemap = [] tileOptimisationMap = self.image.resize( (self.image.size[0] // 8, self.image.size[1] // 8), resample=Image.BILINEAR) tileOptimisationMap = tileOptimisationMap.quantize(colors=256) tileOptimisationDict = {} for yTile in range(self.image.size[1] // 8): # TODO - Evaluate each tile for any similar tiles for xTile in range(self.image.size[0] // 8): tempTile = self.image.crop( (xTile * 8, yTile * 8, (xTile + 1) * 8, (yTile + 1) * 8)) if tempTile in tiles: tilemap.append(tiles.index(tempTile)) else: tilemap.append(len(tiles)) tiles.append(tempTile) writer.writeU4(len(tiles)) for tile in tiles: writer.write(tile.tobytes()) writer.writeU2(self.image.size[0] // 8) writer.writeU2(self.image.size[1] // 8) for key in tilemap: writer.writeU2(key) self.data = writer.data
def compressHuffman(self, useHalfByteBlocks = False, addHeader=False): reader = binary.BinaryReader(data = self.data) freqDict = {} while reader.hasDataRemaining(): # Build frequency table tempByte = [bytes(reader.read(1))] if useHalfByteBlocks: tempByte[0] = int.from_bytes(tempByte[0], byteorder = 'little') tempByte = [(tempByte[0] >> 4).to_bytes(1, byteorder = 'little'), (tempByte[0] & 0x0F).to_bytes(1, byteorder = 'little')] for block in tempByte: if block not in freqDict.keys(): freqDict[block] = _HuffmanCompressionNode(data = block) freqDict[block].weight += 1 nodes = freqDict.values() if len(nodes) > 2**9: raise Exception("Huffman encode: Tree too long to be encoded!") while len(nodes) > 1: # Build Huffman tree by grouping nodes nodes = sorted(nodes, key=lambda huffNode : huffNode.weight) newNode = _HuffmanCompressionNode(left = nodes[0], right = nodes[1], weight = nodes[0].weight + nodes[1].weight) newNode.left.parent = newNode newNode.right.parent = newNode nodes = nodes[2:] nodes.append(newNode) tree = _HuffmanTree(nodes[0]) writer = binary.BinaryWriter() if useHalfByteBlocks: writer.writeInt(File.COMP_HUFFMAN_4_BIT, 1) else: writer.writeInt(File.COMP_HUFFMAN_8_BIT, 1) writer.writeInt(len(self.data), 3) writer.write(tree.encode()) keyDict = {} for key in freqDict.keys(): keyDict[freqDict[key].data] = freqDict[key].getBoolCode() reader.seek(0) compressionBlock = 0 compressionBlockBitsRemaining = 32 while reader.hasDataRemaining(): # Ported from DsDecmp tempByte = [reader.read(1)] if useHalfByteBlocks: tempByte[0] = int.from_bytes(tempByte[0], byteorder = 'little') tempByte = [(tempByte[0] >> 4).to_bytes(1, byteorder = 'little'), (tempByte[0] & 0x0F).to_bytes(1, byteorder = 'little')] for data in tempByte: for bit in keyDict[bytes(data)]: if compressionBlockBitsRemaining == 0: writer.writeU4(compressionBlock) compressionBlock = 0 compressionBlockBitsRemaining = 32 compressionBlockBitsRemaining -= 1 if bit: compressionBlock = compressionBlock | (1 << compressionBlockBitsRemaining) if compressionBlockBitsRemaining != 32: writer.writeU4(compressionBlock) writer.dsAlign(4, 4) if addHeader: if useHalfByteBlocks: self.data = File.LAYTON_1_COMPRESSION[File.COMP_HUFFMAN_4_BIT] + writer.data else: self.data = File.LAYTON_1_COMPRESSION[File.COMP_HUFFMAN_8_BIT] + writer.data else: self.data = writer.data