def _open(self): io = self._io io.seek(0) magic = IOHelper.read_ascii_string(io, 16) if magic != FILE_MAGIC: raise Exception('Not NPK File.') [count] = IOHelper.read_struct(io, 'i') files = [] for i in range(count): offset, size = IOHelper.read_struct(io, '<2i') name_data = NPK._decrypt_name(io.read(256)) try: name = name_data.decode('euc_kr') name = name[:name.find('\x00')] except: name = name_data[:name_data.find(b'\x00')].decode( 'euc_kr', errors='ignore') print('Bad Filename: ', name_data) file = { 'name': name, 'offset': offset, 'size': size, 'data': None, } files.append(file) self._files = files
def _indexes_to_raw(data, color_board): with BytesIO(data) as io_indexes: with BytesIO() as io_raw: temp = IOHelper.read_struct(io_indexes, '<B', False) while temp is not None: [index] = temp IOHelper.write_struct(io_raw, '<4B', *color_board[index]) temp = IOHelper.read_struct(io_indexes, '<B', False) data_raw = IOHelper.read_range(io_raw) return data_raw
def _open_color_board(self): io = self._io [count] = IOHelper.read_struct(io, 'i') colors = [] for i in range(count): color = IOHelper.read_struct(io, '<4B') colors.append(color) return colors
def _open_images(self, count): io = self._io version = self._version images = [] for i in range(count): image = {} [fmt] = IOHelper.read_struct(io, '<i') image['format'] = fmt if fmt == IMAGE_FORMAT_LINK: [link] = IOHelper.read_struct(io, '<i') image['link'] = link else: extra, w, h, size, x, y, mw, mh = IOHelper.read_struct( io, '<8i') # fix size to real size. if (version == FILE_VERSION_1 or version == FILE_VERSION_2) and extra == IMAGE_EXTRA_NONE: size = w * h * PIX_SIZE[fmt] image['extra'] = extra image['w'] = w image['h'] = h image['size'] = size image['x'] = x image['y'] = y image['mw'] = mw image['mh'] = mh # temp image['data'] = None if extra == IMAGE_EXTRA_MAP_ZLIB: keep_1, map_index, lx, ly, rx, ry, rotate = IOHelper.read_struct( io, '<7i') image['keep_1'] = keep_1 image['map_index'] = map_index image['left'] = lx image['top'] = ly image['right'] = rx image['bottom'] = ry # horizontal, vertical image['rotate'] = rotate if self._version == FILE_VERSION_1: image['offset'] = io.tell() io.seek(size, SEEK_CUR) images.append(image) return images
def load_file(self, index): file = self._files[index] if file['data'] is None: if file['size'] != 0: file['data'] = IOHelper.read_range(self._io, file['offset'], file['size']) else: print('size is zero, read all data.') file['data'] = IOHelper.read_range(self._io, file['offset'], -1) return file['data']
def load_file(self, index): file = self._files[index] if file['data'] is None: file['data'] = IOHelper.read_range(self._io, file['offset'], file['size']) return file['data']
def _png_to_nximg(data, image_format): data_nximg = bytes() with BytesIO(data) as io_png: with BytesIO() as io_nximg: png = PngImageFile(io_png) w, h = png.width, png.height if image_format == IMAGE_FORMAT_1555: for y in range(h): for x in range(w): [r, g, b, a] = png.getpixel((x, y)) IOHelper.write_struct(io_nximg, "<2B", *NXColor.to_1555(r, g, b, a)) elif image_format == IMAGE_FORMAT_4444: for y in range(h): for x in range(w): [r, g, b, a] = png.getpixel((x, y)) IOHelper.write_struct(io_nximg, "<2B", *NXColor.to_4444(r, g, b, a)) elif image_format == IMAGE_FORMAT_8888: for y in range(h): for x in range(w): [r, g, b, a] = png.getpixel((x, y)) IOHelper.write_struct(io_nximg, "<4B", b, g, r, a) else: raise Exception('Unsupport image format: %s' % image_format) data_nximg = IOHelper.read_range(io_nximg) return data_nximg, w, h
def raw_to_png(data, w, h, rotate=0): raw_image = Image.frombytes('RGBA', (w, h), data) if rotate == 1: raw_image = raw_image.transpose(Image.ROTATE_90) with BytesIO() as io_png: raw_image.save(io_png, 'png') data_png = IOHelper.read_range(io_png) return data_png
def load_image_map(self, index): io = self._io map_image = self._map_images[index] if map_image['data'] is not None: return map_image['data'] data = IOHelper.read_range(io, map_image['offset'], map_image['data_size']) data = zlib.decompress(data) map_image['data'] = data return data
def dds_to_png(data, box=None, rotate=0): with BytesIO(data) as io_dds: map_image = DdsImageFile(io_dds) if box is not None: map_image = map_image.crop(box) if rotate == 1: map_image = map_image.transpose(Image.ROTATE_90) with BytesIO() as io_png: map_image.save(io_png, 'png') data_png = IOHelper.read_range(io_png) return data_png
def data(self, index): if index < len(self._files): file = self._files[index] # type: dict if file['data'] is None: if not file['is_zip']: data = IOHelper.read_range(self._io, file['offset'], file['data_size']) file['data'] = data else: raise Exception('Unsupport File.') else: data = file['data'] return data
def save(self, io=None): # load all file data. self.load_all() files = self._files if io is None: io = self._io # clean file. io.seek(0) io.truncate() head_data = b'' # build head in memory. with BytesIO() as io_head: IOHelper.write_ascii_string(io_head, FILE_MAGIC) count = len(files) IOHelper.write_struct(io_head, 'i', count) # count file offset. # magic(16) + count(4) + info(264 * n) + hash(32) offset = 52 + count * 264 for file in files: file['offset'] = offset file['size'] = len(file['data']) IOHelper.write_struct(io_head, '<2i', file['offset'], file['size']) if isinstance(file['name'], str): name_data = file['name'].encode(encoding='euc_kr') elif isinstance(file['name'], bytes): name_data = file['name'] else: raise Exception('Filename type Error: %s(%s)' % (file['name'], type(file['name']))) name = NPK._decrypt_name(name_data) io_head.write(name) offset += file['size'] head_data = IOHelper.read_range(io_head) io.write(head_data) # write hash. io.write( hashlib.sha256(head_data[:len(head_data) // 17 * 17]).digest()) for file in files: io.seek(file['offset']) io.write(file['data'])
def load(io: FileIO): instance = MPK(io) magic = IOHelper.read_ascii_string(io, 4) if magic == MPK_MAGIC: version, count = IOHelper.read_struct(io, '<2i') io.seek(52, SEEK_CUR) instance.set_version(version) for i in range(count): is_zip, index, offset, data_size, zip_size = IOHelper.read_struct( io, '<2i3q') name_data = io.read(224) name = name_data[:name_data.find(b'\x00')].decode( encoding='ascii') instance.insert_file({ 'is_zip': is_zip != 0, 'index': index, 'offset': offset, 'data_size': data_size, 'zip_size': zip_size, 'name': name, 'data': None, }) return instance
def load_image(self, index): io = self._io image = self._images[index] if image['format'] == IMAGE_FORMAT_LINK: return None if image['data'] is not None: return image['data'] data = IOHelper.read_range(io, image['offset'], image['size']) if image['extra'] == IMAGE_EXTRA_ZLIB: data = zlib.decompress(data) elif image['extra'] != IMAGE_EXTRA_NONE: raise Exception('Unknown Extra Type.', image['extra']) image['data'] = data return data
def _open_map_images(self, map_count): io = self._io map_images = [] for i in range(map_count): keep, fmt, index, data_size, raw_size, w, h = IOHelper.read_struct( io, '<7i') map_image = { 'keep': keep, 'format': fmt, 'index': index, 'data_size': data_size, 'raw_size': raw_size, 'w': w, 'h': h, 'data': None, } map_images.append(map_image) return map_images
def save(self, io=None): # load all file data. self.load_all() files = self._files if io is None: io = self._io # clean file. io.seek(0) io.truncate() # build head in memory. with BytesIO() as io_head: IOHelper.write_ascii_string(io_head, FILE_MAGIC) count = len(files) IOHelper.write_struct(io_head, 'i', count) # count file offset. # magic(16) + count(4) + info(264 * n) + hash(32) offset = 52 + count * 264 for file in files: IOHelper.write_struct(io_head, '<2i', file['offset'], file['size']) name = NPK._decrypt_name(file['name'].encode(encoding='ascii')) io_head.write(name) offset += file['size'] head_data = IOHelper.read_range(io_head) io.write(head_data) # write hash. io.write( hashlib.sha256(head_data[:len(head_data) // 17 * 17]).digest()) for file in files: io.seek(file['offset']) io.write(file['data'])
def print_exception(type, value, tb): with StringIO() as io: traceback.print_exception(type, value, tb, file=io) err_str = IOHelper.read_range(io) print(err_str) QMessageBox.warning(None, '错误:', err_str)
def _open(self): io = self._io io.seek(0) magic = IOHelper.read_ascii_string(io, 18) if magic == FILE_MAGIC or magic == FILE_MAGIC_OLD: if magic == FILE_MAGIC: # images_size without version,count,extra(color_board,map_images)... [images_size] = IOHelper.read_struct(io, 'i') else: # unknown. [unknown] = IOHelper.read_struct(io, 'h') images_size = 0 # keep: 0 [keep, version, img_count] = IOHelper.read_struct(io, '<3i') self._version = version if version == FILE_VERSION_4: # single color board. self._color_board = self._open_color_board() elif version == FILE_VERSION_5: # map image. map_count, file_size = IOHelper.read_struct(io, '<2i') self._color_board = self._open_color_board() self._map_images = self._open_map_images(map_count) elif version == FILE_VERSION_6: # multiple color board. color_boards = [] [color_board_count] = IOHelper.read_struct(io, 'i') for i in range(color_board_count): color_board = self._open_color_board() color_boards.append(color_board) self._color_boards = color_boards images = self._open_images(img_count) self._images = images # count image offset. if version != FILE_VERSION_1: # behind header. if images_size != 0: offset = images_size + 32 else: offset = io.tell() if version == FILE_VERSION_5: map_images = self._map_images for i in range(len(map_images)): map_image = map_images[i] map_image['offset'] = offset offset += map_image['data_size'] for i in range(len(images)): image = images[i] if image['format'] != IMAGE_FORMAT_LINK and image[ 'extra'] != IMAGE_EXTRA_MAP_ZLIB: image['offset'] = offset offset += image['size'] else: raise Exception('Not IMG File.')
def save(self, io=None): self.load_all() images = self._images color_board = self._color_board color_boards = self._color_boards map_images = self._map_images version = self._version images_data = [] # compress data, get size, add to data_list. if version == FILE_VERSION_5: for map_image in sorted(map_images): data = map_image['data'] map_image['raw_size'] = len(data) data = zlib.compress(data) map_image['data_size'] = len(data) images_data.append(data) else: for image in images: if image['format'] != IMAGE_FORMAT_LINK: data = image['data'] if image['extra'] == IMAGE_EXTRA_ZLIB or image[ 'extra'] == IMAGE_EXTRA_MAP_ZLIB: data = zlib.compress(data) image['size'] = len(data) images_data.append(data) images_size = self._save_count_images_size() file_size = self._save_count_file_size(images_size, images_data) if io is None: io = self._io io.seek(0) io.truncate() with BytesIO() as io_head: if version == FILE_VERSION_1: IOHelper.write_ascii_string(io_head, FILE_MAGIC_OLD) # TODO: unknown, now be zero. IOHelper.write_struct(io_head, 'h', 0) else: # images_size IOHelper.write_ascii_string(io_head, FILE_MAGIC) IOHelper.write_struct(io_head, 'i', images_size) # keep, version, img_count IOHelper.write_struct(io_head, '<3i', 0, version, len(images)) is_ver5 = version == FILE_VERSION_5 if is_ver5: # map_count, file_size IOHelper.write_struct(io_head, '<2i', len(map_images), file_size) if version == FILE_VERSION_4 or is_ver5: # color_count IOHelper.write_struct(io_head, 'i', len(color_board)) for color in color_board: # color IOHelper.write_struct(io_head, '<4B', *color) if is_ver5: for map_image in map_images: IOHelper.write_struct(io_head, '<7i', map_image['keep'], map_image['format'], map_image['index'], map_image['data_size'], map_image['raw_size'], map_image['w'], map_image['h']) if version == FILE_VERSION_6: # color_board count. IOHelper.write_struct(io_head, 'i', len(color_boards)) for color_board_v6 in color_boards: # color_count IOHelper.write_struct(io_head, 'i', len(color_board_v6)) for color in color_board_v6: # color IOHelper.write_struct(io_head, '<4B', *color) for image in images: # format IOHelper.write_struct(io_head, 'i', image['format']) if image['format'] == IMAGE_FORMAT_LINK: # link IOHelper.write_struct(io_head, 'i', image['link']) else: # extra, w, h, size, x, y, mw, mh IOHelper.write_struct(io_head, '<8i', image['extra'], image['w'], image['h'], image['size'], image['x'], image['y'], image['mw'], image['mh']) if image['extra'] == IMAGE_EXTRA_MAP_ZLIB: # keep_1, map_index, left, top, right, bottom, rotate IOHelper.write_struct(io_head, '<7i', image['keep_1'], image['map_index'], image['left'], image['top'], image['right'], image['bottom'], image['rotate']) head_data = IOHelper.read_range(io_head) io.write(head_data) for data in images_data: io.write(data)
def _nximg_to_raw(data, image_format, w=None, box=None): data_raw = bytes() ps = PIX_SIZE[image_format] with BytesIO(data) as io_nximg: with BytesIO() as io_raw: if image_format == IMAGE_FORMAT_1555: if box is not None and w is not None: [left, top, right, bottom] = box for y in range(top, bottom): o = y * w * ps for x in range(left, right): io_nximg.seek(o + x * ps) temp = IOHelper.read_struct( io_nximg, '<2B', False) if temp is not None: [v1, v2] = temp IOHelper.write_struct( io_raw, '<4B', *NXColor.from_1555(v1, v2)) else: temp = IOHelper.read_struct(io_nximg, '<2B', False) while temp is not None: [v1, v2] = temp IOHelper.write_struct(io_raw, '<4B', *NXColor.from_1555(v1, v2)) temp = IOHelper.read_struct(io_nximg, '<2B', False) elif image_format == IMAGE_FORMAT_4444: if box is not None and w is not None: [left, top, right, bottom] = box for y in range(top, bottom): o = y * w * ps for x in range(left, right): io_nximg.seek(o + x * ps) temp = IOHelper.read_struct( io_nximg, '<2B', False) if temp is not None: [v1, v2] = temp IOHelper.write_struct( io_raw, '<4B', *NXColor.from_4444(v1, v2)) else: temp = IOHelper.read_struct(io_nximg, '<2B', False) while temp is not None: [v1, v2] = temp IOHelper.write_struct(io_raw, '<4B', *NXColor.from_4444(v1, v2)) temp = IOHelper.read_struct(io_nximg, '<2B', False) elif image_format == IMAGE_FORMAT_8888: if box is not None and w is not None: [left, top, right, bottom] = box for y in range(top, bottom): o = y * w * ps for x in range(left, right): io_nximg.seek(o + x * ps) temp = IOHelper.read_struct( io_nximg, '<4B', False) if temp is not None: [b, g, r, a] = temp IOHelper.write_struct( io_raw, '<4B', r, g, b, a) else: temp = IOHelper.read_struct(io_nximg, '<4B', False) while temp is not None: [b, g, r, a] = temp IOHelper.write_struct(io_raw, '<4B', r, g, b, a) temp = IOHelper.read_struct(io_nximg, '<4B', False) else: raise Exception('Unsupport Image Format.', image_format) data_raw = IOHelper.read_range(io_raw) return data_raw