def load(filepath): reader = binary.BinaryReader(filename = filepath) tempName = filepath.split("//")[-1] if tempName == "": print("Warning: Invalid filename!") tempName = "NULL" return File(name=tempName, data=reader.data)
def load(self, data): def getBankString(reader, offsetString): bankString = {} reader.seek(offsetString) while reader.hasDataRemaining(): index = reader.tell() - offsetString bankString[index] = reader.readNullTerminatedString( 'shift-jis') reader.seek(1, 1) return bankString def getBankOperand(reader, offsetOperands, countOperands, bankString): reader.seek(offsetOperands) bankOperands = {} for indexOperand in range(countOperands): tempOperandType = reader.readUInt(1) if tempOperandType == 0: tempOperand = reader.readS4() elif tempOperandType == 1: tempOperand = reader.readF4() elif tempOperandType == 2: tempOperand = bankString[reader.readU4()] else: tempOperand = reader.read(4) bankOperands[indexOperand] = Operand(tempOperandType, tempOperand) return bankOperands def populateInstructionOperands(bankOperands): for command in self.commands: for indexInstruction in range( command.indexOperandsStart, command.indexOperandsStart + command.countOperands): command.operands.append(bankOperands[indexInstruction]) reader = binary.BinaryReader(data=data) if reader.read(4) == b'LSCR': countCommand = reader.readU2() countOperands = 0 offsetHeader = reader.readU2() offsetOperands = reader.readU4() offsetString = reader.readU4() bankString = getBankString(reader, offsetString) reader.seek(offsetHeader) for indexCommand in range(countCommand): self.commands.append(FutureInstruction.fromData( reader.read(8))) countOperands = max( countOperands, self.commands[indexCommand].indexOperandsStart + self.commands[indexCommand].countOperands) bankOperand = getBankOperand(reader, offsetOperands, countOperands, bankString) populateInstructionOperands(bankOperand) return True return False
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 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 load(self, data): reader = binary.BinaryReader(data = data) if reader.read(4) == b'LPC2': countFile = reader.readU4() offsetFile = reader.readU4() _lengthArchive = reader.readU4() offsetMetadata = reader.readU4() offsetName = reader.readU4() for indexFile in range(countFile): reader.seek(offsetMetadata + (12 * indexFile)) fileOffsetName = reader.readU4() fileOffsetData = reader.readU4() fileLengthData = reader.readU4() reader.seek(offsetName + fileOffsetName) tempName = reader.readNullTerminatedString('shift-jis') reader.seek(offsetFile + fileOffsetData) tempData = reader.read(fileLengthData) self.files.append(File(tempName, data=tempData)) return True return False
def load(self, data): reader = binary.BinaryReader(data=data) if reader.read(4) == b'LIMG': lengthHeader = reader.readU4() offsetSubImageData = reader.readU2() countSubImage = reader.readU2() offsetImageParam = reader.readU2() reader.seek(2, 1) # UNK offsetTableTile = reader.readU2() lengthTableTile = reader.readU2() offsetTile = reader.readU2() countTile = reader.readU2() countPalette = reader.readU2() # Always 1 lengthPalette = reader.readU2() resolution = (reader.readU2(), reader.readU2()) bpp = math.ceil(math.ceil(math.log(lengthPalette, 2)) / 4) * 4 reader.seek(offsetSubImageData) for _subImageCount in range(countSubImage): self.subImageCropRegions.append( (reader.readUInt(1) * 8, reader.readUInt(1) * 8, reader.readUInt(1) * 8, reader.readUInt(1) * 8)) reader.seek(4, 1) reader.seek(lengthHeader) palette = [] for _indexColour in range(lengthPalette): palette.extend(Colour.fromInt(reader.readU2()).toList()) self.imageAtlas = Image.new("P", resolution) self.imageAtlas.putpalette(palette) self.imageAtlas.paste(0, (0, 0, resolution[0], resolution[1])) reader.seek(offsetTile) tilePilMap = {} for index in range(countTile): tilePilMap[index] = Tile(data=reader.read(int((bpp * 64) / 8))).decodeToPil( palette, bpp) reader.seek(offsetTableTile) width, height = self.imageAtlas.size for y in range(height // 8): for x in range(width // 8): tempSelectedTile = reader.readU2() tileSelectedIndex = tempSelectedTile & (2**10 - 1) tileSelectedFlipX = tempSelectedTile & (2**11) tileSelectedFlipY = tempSelectedTile & (2**10) if tileSelectedIndex < (2**10 - 1): tileFocus = tilePilMap[tileSelectedIndex % countTile] if tileSelectedFlipX: tileFocus = tileFocus.transpose( method=Image.FLIP_LEFT_RIGHT) if tileSelectedFlipY: tileFocus = tileFocus.transpose( method=Image.FLIP_TOP_BOTTOM) self.imageAtlas.paste(tileFocus, (x * 8, y * 8)) else: print("Failed magic test!")
"RGBA", (command.operands[3].value, command.operands[4].value)) # TODO : Offset not implemented elif command.opcode == b'\xfe\x03': targetSubImage = atlasesAsIndex[command.operands[ 0].value].subImages[command.operands[1].value] tempFrame.paste( targetSubImage, (command.operands[2].value, command.operands[3].value), targetSubImage) # TODO : Another UNK # TODO : alpha_composite elif command.opcode == b'\xfd\x03': self.frames[tempName] = tempFrame def export(self, filename): for frameName in list(self.frames.keys()): self.frames[frameName].save( path.splitext(filename)[0] + "_" + frameName + "." + EXPORT_EXTENSION) if __name__ == "__main__": import sys if len(sys.argv) > 1: if sys.argv[1].split(".")[-1] == "cani": testImage = LaytonAnimatedImage() else: testImage = LaytonBackgroundImage() testImage.load(binary.BinaryReader(filename=sys.argv[1]).data) testImage.export(".".join(sys.argv[1].split(".")[:-1]))
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
def setFromData(self, data): reader = binary.BinaryReader(data=data) self.opcode = reader.read(2) self.countOperands = reader.readU2() self.indexOperandsStart = reader.readU4()
for indexCommand in range(countCommand): self.commands.append(FutureInstruction.fromData( reader.read(8))) countOperands = max( countOperands, self.commands[indexCommand].indexOperandsStart + self.commands[indexCommand].countOperands) bankOperand = getBankOperand(reader, offsetOperands, countOperands, bankString) populateInstructionOperands(bankOperand) return True return False def __str__(self): output = "" for operation in debug.commands: output += "\n\n" + str(operation) return output if __name__ == "__main__": import sys if len(sys.argv) > 1: debug = LaytonScript() debug.load(binary.BinaryReader(filename=sys.argv[1]).data) for operation in debug.commands: print(operation) print() exit = input("Return to exit...")