#!/usr/bin/env python3 import re import sys import argparse ROMSIZE = 512 * 1024 class FileMap: def __init__(self, base: int, filename: str): self.base = base self.filename = filename with open(filename, 'rb') as f: self.data = f.read() if len(self.data) == 0: raise AttributeError(f'File {filename} is empty') if len(self.data) > 4096: raise AttributeError(f'File {filename} exceeds 4KiB') self.end = self.base + len(self.data) if self.base < 0 or self.end > ROMSIZE: raise AttributeError(f'File {filename} out of bounds: {hex(self.base)[2:]}:{hex(self.end-1)[2:]}') if self.base & 0x0fff != 0: raise AttributeError(f'File {filename} not aligned to 4KiB boundary: {hex(self.base)[2:]}:{hex(self.end-1)[2:]}') @staticmethod def at(base: int): def _filemap(f): return FileMap(base, f) return _filemap def baseimage(byte): b = int(byte, 16) return bytearray([b]) * ROMSIZE def create_image(ns): buf = ns.fill used = {} for m in ns.maps: if m.base in used: raise KeyError(f'{m.filename} and {used[m.base]} are both mapped at {hex(m.base)[2:]}') used[m.base] = m.filename buf[m.base:m.end] = m.data with open(ns.output, 'wb') as f: f.write(buf) def main(): ap = argparse.ArgumentParser() ap.add_argument('--output', '-o', type=str, metavar='FILE', default='27c040.bin', help='Write image to FILE (default: 27c040.bin)') ap.add_argument('--', type=ValueError, metavar='FILE', help='Map FILE at the 4KiB-aligned sector starting at hex-address NNNNN.') ap.add_argument('--fill', '-f', type=baseimage, default='FF', help='Fill unmapped areas with this hex byte (default: FF)') # https://stackoverflow.com/a/37367814 ns, unknown = ap.parse_known_args(sys.argv[1:]) for arg in unknown: m = re.match('^--([0-9a-fA-F]{5})$', arg) if m is None: continue base = int(m.group(1), 16) ap.add_argument(arg, type=FileMap.at(base), dest='maps', action='append') ns = ap.parse_args(sys.argv[1:]) create_image(ns) if __name__ == '__main__': main()