| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- #!/usr/bin/env python3
- import argparse
- import io
- import logging
- import os
- import struct
- from PIL import Image, ImageOps
- # XBM flipper sprite (.fxbm) is 1-bit depth, width-padded to 8 bits
- # file format:
- # uint32 size of the rest of the file in bytes
- # uint32 width in px
- # uint32 height in px
- # uint8[] pixel data, every row is padded to 8 bits (like XBM)
- def image2xbm(input_file_path):
- with Image.open(input_file_path) as im:
- with io.BytesIO() as output:
- bw = im.convert("1")
- bw = ImageOps.invert(bw)
- bw.save(output, format="XBM")
- return output.getvalue()
- def xbm2fxbm(data):
- # hell as it is, but it works
- f = io.StringIO(data.decode().strip())
- width = int(f.readline().strip().split(" ")[2])
- height = int(f.readline().strip().split(" ")[2])
- data = f.read().strip().replace("\n", "").replace(" ", "").split("=")[1][:-1]
- data_str = data[1:-1].replace(",", " ").replace("0x", "")
- image_bin = bytearray.fromhex(data_str)
- output = struct.pack("<I", len(image_bin) + 8)
- output += struct.pack("<II", width, height)
- output += image_bin
- return output
- def process_sprites(input_dir, output_dir):
- for root, dirs, files in os.walk(input_dir):
- for file in files:
- if file.startswith('.'):
- continue
-
- try:
- input_file_path = os.path.join(root, file)
- rel_path = os.path.relpath(input_file_path, input_dir)
- new_rel_path = os.path.splitext(rel_path)[0] + ".fxbm"
- output_file_path = os.path.join(output_dir, new_rel_path)
- os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
- print(f"Converting '{rel_path}' to '{new_rel_path}'")
- xbm = image2xbm(input_file_path)
- img_data = xbm2fxbm(xbm)
- with open(output_file_path, "wb") as f:
- f.write(img_data)
- except Exception as e:
- print(f"Cannot convert '{rel_path}': {e}")
- def clear_directory(directory):
- for root, dirs, files in os.walk(directory, topdown=False):
- for name in files:
- os.remove(os.path.join(root, name))
- for name in dirs:
- os.rmdir(os.path.join(root, name))
- def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("source", help="Source directory")
- parser.add_argument("target", help="Target directory")
- args = parser.parse_args()
- logging.basicConfig(level=logging.ERROR)
- logger = logging.getLogger(__name__)
- logger.debug(f"Building sprites from {args.source} to {args.target}")
- clear_directory(args.target)
- process_sprites(args.source, args.target)
- return 0
- if __name__ == "__main__":
- main()
|