1#!/usr/bin/env python3 2# 3# Copyright (c) 2017 Intel Corporation 4# Copyright (c) 2025 Siemens AG 5# 6# SPDX-License-Identifier: Apache-2.0 7 8 9"""Convert a file to a list of hex characters 10 11The list of hex characters can then be included to a source file. Optionally, 12the output can be compressed. 13 14""" 15 16import argparse 17import codecs 18import gzip 19import io 20 21 22def parse_args(): 23 global args 24 25 parser = argparse.ArgumentParser( 26 description=__doc__, 27 formatter_class=argparse.RawDescriptionHelpFormatter, 28 allow_abbrev=False, 29 ) 30 31 parser.add_argument("-f", "--file", required=True, help="Input file") 32 parser.add_argument( 33 "-o", "--offset", type=lambda x: int(x, 0), default=0, help="Byte offset in the input file" 34 ) 35 parser.add_argument( 36 "-l", 37 "--length", 38 type=lambda x: int(x, 0), 39 default=-1, 40 help="""Length in bytes to read from the input file. 41 Defaults to reading till the end of the input file.""", 42 ) 43 parser.add_argument( 44 "-m", 45 "--format", 46 default="list", 47 help="Output format: 'list' (default) or 'literal' (string literal)", 48 ) 49 parser.add_argument( 50 "-g", "--gzip", action="store_true", help="Compress the file using gzip before output" 51 ) 52 parser.add_argument( 53 "-t", 54 "--gzip-mtime", 55 type=int, 56 default=0, 57 nargs='?', 58 const=None, 59 help="""mtime seconds in the gzip header. 60 Defaults to zero to keep builds deterministic. For 61 current date and time (= "now") use this option 62 without any value.""", 63 ) 64 args = parser.parse_args() 65 66 67def get_nice_string(list_or_iterator): 68 # Convert into comma separated list form. 69 s = ", ".join("0x" + str(x) for x in list_or_iterator) 70 71 # Format the list to eight values per line. 72 return "\n".join(s[i : i + 47] for i in range(0, len(s), 48)) 73 74 75def make_hex(chunk): 76 hexdata = codecs.encode(chunk, 'hex').decode("utf-8") 77 hexlist = map(''.join, zip(*[iter(hexdata)] * 2, strict=False)) 78 print(get_nice_string(hexlist) + ',') 79 80 81def make_string_literal(chunk): 82 hexdata = codecs.encode(chunk, 'hex').decode("utf-8") 83 hexlist = map(''.join, zip(*[iter(hexdata)] * 2, strict=False)) 84 print(''.join("\\x" + str(x) for x in hexlist), end='') 85 86 87def chunker(source, remaining=-1): 88 while chunk_raw := source.read(1024): 89 if chunk_raw == b"": 90 break 91 if remaining == -1: 92 yield chunk_raw 93 elif remaining < len(chunk_raw): 94 yield chunk_raw[:remaining] 95 else: 96 yield chunk_raw 97 remaining -= len(chunk_raw) 98 99 100def main(): 101 parse_args() 102 103 if args.gzip: 104 with io.BytesIO() as content: 105 with open(args.file, 'rb') as fg: 106 fg.seek(args.offset) 107 with gzip.GzipFile( 108 fileobj=content, mode='w', mtime=args.gzip_mtime, compresslevel=9 109 ) as gz_obj: 110 gz_obj.write(fg.read(args.length)) 111 112 content.seek(0) 113 if args.format == "literal": 114 print('"', end='') 115 for chunk in chunker(content): 116 make_string_literal(chunk) 117 print('"', end='') 118 else: 119 for chunk in chunker(content): 120 make_hex(chunk) 121 else: 122 with open(args.file, "rb") as fp: 123 fp.seek(args.offset) 124 125 if args.format == "literal": 126 print('"', end='') 127 for chunk in chunker(fp, args.length): 128 make_string_literal(chunk) 129 print('"', end='') 130 else: 131 for chunk in chunker(fp, args.length): 132 make_hex(chunk) 133 134 135if __name__ == "__main__": 136 main() 137