diff --git a/game/scripts/doll/main.rpy b/game/scripts/doll/main.rpy index 80f33566..b200f6ad 100644 --- a/game/scripts/doll/main.rpy +++ b/game/scripts/doll/main.rpy @@ -538,7 +538,7 @@ init python: # Grab data if fromfile: try: - imported = ImagePayload().extract(path) + imported = image_payload.extract(path) except Exception as e: renpy.notify("Import failed: Corrupted file.") print(e) diff --git a/game/scripts/doll/outfits.rpy b/game/scripts/doll/outfits.rpy index d48c8869..d6e6313c 100644 --- a/game/scripts/doll/outfits.rpy +++ b/game/scripts/doll/outfits.rpy @@ -222,7 +222,7 @@ init python: ) displayable_to_file(d, path, size=(310, 470) ) - ImagePayload().inject("_temp.png", filename, str(exported)) + image_payload.inject("_temp.png", filename, str(exported)) os.remove(path) else: set_clipboard(exported) diff --git a/game/scripts/utility/punk.rpy b/game/scripts/utility/punk.rpy index d510f9a6..75ce236c 100644 --- a/game/scripts/utility/punk.rpy +++ b/game/scripts/utility/punk.rpy @@ -4,139 +4,134 @@ ## Implementation and changes ## ## LoafyLemon ## ##################################### -init python: +init python in image_payload: import binascii import struct import zlib - class ImagePayload(NoRollback): + CHUNK_TYPE_END = "IEND" + CHUNK_TYPE_PUNK = "wtSi" + MAX_BYTES = 2147483647 + SIGNATURE_BYTES = 8 + BYTES_IN_KB = 2014 - CHUNK_TYPE_END = "IEND" - CHUNK_TYPE_PUNK = "wtSi" - MAX_BYTES = 2147483647 - SIGNATURE_BYTES = 8 - BYTES_IN_KB = 2014 + def bytes_to_hex(b): + return b.hex() - def __init__(self): - pass + def bytes_to_utf(b): + return b.decode() - def bytes_to_hex(self, b): - return b.hex() + def bytes_to_int(b): + return int(bytes_to_hex(b), 16) - def bytes_to_utf(self, b): - return b.decode() + def read_bytes(f, byte_count: int): + return f.read(byte_count) - def bytes_to_int(self, b): - return int(self.bytes_to_hex(b), 16) + def rewind_bytes(f, byte_count): + f.seek(f.tell() - byte_count) - def read_bytes(self, f, byte_count: int): - return f.read(byte_count) + def get_file_length(f): + f.seek(0, os.SEEK_END) + file_length = f.tell() + f.seek(0) - def rewind_bytes(self, f, byte_count): - f.seek(f.tell() - byte_count) + return file_length - def get_file_length(self, f): - f.seek(0, os.SEEK_END) - file_length = f.tell() - f.seek(0) + def read_chunk(f): + chunk_size = read_bytes(f, 4) + chunk_type = read_bytes(f, 4) + chunk_content = read_bytes(f, bytes_to_int(chunk_size)) + chunk_crc = read_bytes(f, 4) - return file_length + return [chunk_size, chunk_type, chunk_content, chunk_crc] - def read_chunk(self, f): - chunk_size = self.read_bytes(f, 4) - chunk_type = self.read_bytes(f, 4) - chunk_content = self.read_bytes(f, self.bytes_to_int(chunk_size)) - chunk_crc = self.read_bytes(f, 4) + def inject_punk_chunk(f, content): + chunk_size = len(content) - return [chunk_size, chunk_type, chunk_content, chunk_crc] + if chunk_size > MAX_BYTES: + raise ValueError(f"Cannot inject more than {MAX_BYTES} bytes") - def inject_punk_chunk(self, f, content): - chunk_size = len(content) + print(f"Injecting {CHUNK_TYPE_PUNK} chunk {chunk_size / BYTES_IN_KB} kb") - if chunk_size > self.MAX_BYTES: - raise ValueError(f"Cannot inject more than {self.MAX_BYTES} bytes") + # Create a byte array to store our chunk data in. + tmp_bytes = bytearray() + # First write the chunk type + tmp_bytes.extend(CHUNK_TYPE_PUNK.encode()) + # Now write the bytes of whatever we're trying to hide + tmp_bytes.extend(content) - print(f"Injecting {self.CHUNK_TYPE_PUNK} chunk {chunk_size / self.BYTES_IN_KB} kb") + # Write the chunk size + f.write(bytearray(struct.pack("!i", chunk_size))) - # Create a byte array to store our chunk data in. - tmp_bytes = bytearray() - # First write the chunk type - tmp_bytes.extend(self.CHUNK_TYPE_PUNK.encode()) - # Now write the bytes of whatever we're trying to hide - tmp_bytes.extend(content) + # And the content + f.write(tmp_bytes) - # Write the chunk size - f.write(bytearray(struct.pack("!i", chunk_size))) + crc = binascii.crc32(tmp_bytes) + crc_bytes = crc.to_bytes(4, "big") + print("Chunk CRC", bytes_to_hex(crc_bytes)) + f.write(crc_bytes) - # And the content - f.write(tmp_bytes) + print("Chunk injected!") - crc = binascii.crc32(tmp_bytes) - crc_bytes = crc.to_bytes(4, "big") - print("Chunk CRC", self.bytes_to_hex(crc_bytes)) - f.write(crc_bytes) + def list(input): + path = os.path.join(config.gamedir, "outfits", input) - print("Chunk injected!") + with open(path, "rb") as input_file: - def list(self, input): - path = os.path.join(config.gamedir, "outfits", input) + input_file_length = get_file_length(input_file) + input_file.read(SIGNATURE_BYTES) - with open(path, "rb") as input_file: + while True: + chunk_size, chunk_type, chunk_content, chunk_crc = read_chunk(input_file) + chunk_type_str = bytes_to_utf(chunk_type) + print(f"Chunk {chunk_type_str}, {bytes_to_int(chunk_size)} bytes") - input_file_length = self.get_file_length(input_file) - input_file.read(self.SIGNATURE_BYTES) + if input_file.tell() >= input_file_length: + return - while True: - chunk_size, chunk_type, chunk_content, chunk_crc = self.read_chunk(input_file) - chunk_type_str = self.bytes_to_utf(chunk_type) - print(f"Chunk {chunk_type_str}, {self.bytes_to_int(chunk_size)} bytes") + def inject(input, output, content): + input_path = os.path.join(config.gamedir, "outfits", input) + output_path = os.path.join(config.gamedir, "outfits", output) + content = zlib.compress(str(content).encode()) - if input_file.tell() >= input_file_length: - return + with open(input_path, "rb") as input_file, open(output_path, "wb") as output_file: - def inject(self, input, output, content): - input_path = os.path.join(config.gamedir, "outfits", input) - output_path = os.path.join(config.gamedir, "outfits", output) - content = zlib.compress(str(content).encode()) + input_file_length = get_file_length(input_file) + output_file.write(input_file.read(SIGNATURE_BYTES)) - with open(input_path, "rb") as input_file, open(output_path, "wb") as output_file: + while True: + chunk_size, chunk_type, chunk_content, chunk_crc = read_chunk(input_file) + chunk_type_str = bytes_to_utf(chunk_type) + print(f"Chunk {chunk_type_str}, {bytes_to_int(chunk_size)} bytes") - input_file_length = self.get_file_length(input_file) - output_file.write(input_file.read(self.SIGNATURE_BYTES)) + if chunk_type_str == CHUNK_TYPE_END: + inject_punk_chunk(output_file, content) - while True: - chunk_size, chunk_type, chunk_content, chunk_crc = self.read_chunk(input_file) - chunk_type_str = self.bytes_to_utf(chunk_type) - print(f"Chunk {chunk_type_str}, {self.bytes_to_int(chunk_size)} bytes") + output_file.write(chunk_size) + output_file.write(chunk_type) + output_file.write(chunk_content) + output_file.write(chunk_crc) - if chunk_type_str == self.CHUNK_TYPE_END: - self.inject_punk_chunk(output_file, content) + if input_file.tell() >= input_file_length: + return - output_file.write(chunk_size) - output_file.write(chunk_type) - output_file.write(chunk_content) - output_file.write(chunk_crc) + def extract(input): + print("Attempting to extract punked data from", input) + path = os.path.join(config.gamedir, "outfits", input) - if input_file.tell() >= input_file_length: - return + with open(path, "rb") as input_file: - def extract(self, input): - print("Attempting to extract punked data from", input) - path = os.path.join(config.gamedir, "outfits", input) + input_file_length = get_file_length(input_file) + input_file.read(SIGNATURE_BYTES) - with open(path, "rb") as input_file: + while True: + chunk_size, chunk_type, chunk_content, chunk_crc = read_chunk(input_file) + chunk_type_str = bytes_to_utf(chunk_type) - input_file_length = self.get_file_length(input_file) - input_file.read(self.SIGNATURE_BYTES) + if chunk_type_str == CHUNK_TYPE_PUNK: + print("Found a punk chunk worth", bytes_to_int(chunk_size), "bytes") + return zlib.decompress(chunk_content).decode() - while True: - chunk_size, chunk_type, chunk_content, chunk_crc = self.read_chunk(input_file) - chunk_type_str = self.bytes_to_utf(chunk_type) - - if chunk_type_str == self.CHUNK_TYPE_PUNK: - print("Found a punk chunk worth", self.bytes_to_int(chunk_size), "bytes") - return zlib.decompress(chunk_content).decode() - - if input_file.tell() >= input_file_length: - print("No punked data found") - return + if input_file.tell() >= input_file_length: + print("No punked data found") + return