Source code for mltk.utils.bin2header
"""Utilities for generating a "C" header file from binary data
See the source code on Github: `mltk/utils/bin2header.py <https://github.com/siliconlabs/mltk/blob/master/mltk/utils/bin2header.py>`_
"""
import argparse
import sys
import os
import io
from typing import Union, List
[docs]def bin2header(
input:Union[str,bytes,List[int],List[float]], # pylint: disable=redefined-builtin
output_path:Union[bool, str, io.IOBase]=None,
var_name:str='DATA',
length_var_name:str='DATA_LENGTH',
dtype:str='const unsigned char',
attributes:str=None,
prepend_lines:List[int]=None,
fmt_str:str=None,
prepend_header=True
) -> str:
"""Generate C header file from binary input file
Arguments:
input: Either path to binary file or binary contents of previously loaded file, or list of integers or floats
output_path: Output file path or io stream. Use input with .h appended if True
var_name: Name of C array
length_var_name: Name of variable to hold length of C array in bytes
attributes: Attributes to prepend C array variable
dtype: The data type of the C array
prepend_lines: List of C lines of code to prepend before the generated array data
e.g.: #include <stdint.h>
fmt_str: The formatting string to use for each entry in the data, e.g.: 0x{:02X}
If omitted then it is automatically determined based on the given data
Returns:
Generated header as a string
"""
def _get_ascii(c):
if c >= 32 and c < 127 and chr(c) not in '*#\\':
return chr(c)
return '.'
if attributes is None:
attributes = ''
if isinstance(input, str):
if output_path is True:
output_path = input + '.h'
with open(input, 'rb') as f:
data = f.read()
else:
data = input
out = ''
if prepend_header:
out += 'pragma once\n'
out += '// The following was automatically generated by bin2header.py\n\n'
if prepend_lines:
if isinstance(prepend_lines, str):
prepend_lines = list(prepend_lines)
for line in prepend_lines:
if not line.endswith(('\n', '\r')):
line += '\n'
out += line
out += '\n'
out += f'const unsigned int {length_var_name} = {len(data)};\n'
out += f'{dtype} {var_name}[{len(data)}] {attributes} =\n{{\n'
l = [ data[i:i+16] for i in range(0, len(data), 16) ]
if fmt_str is None:
if isinstance(data, (bytes,bytearray)):
fmt_str = '0x{:02X}'
elif isinstance(data[0], float):
fmt_str = '{:6.3f}'
else:
fmt_str = '{:5d}'
max_line_len = 0
for i, x in enumerate(l):
line = ','.join([fmt_str.format(c) for c in x ])
if len(line) > max_line_len:
max_line_len = len(line) + 1
out += line
if i < len(l) -1:
out += ','
else:
out += ' ' * (max_line_len - len(line))
if isinstance(data, (bytes,bytearray)):
out += f' /* {i*16:6d}: '
out += ''.join([_get_ascii(c) for c in x])
out += ' */'
out += '\n'
out += '};\n'
if output_path:
if isinstance(output_path, str):
with open(output_path, 'w') as f:
f.write(out)
elif isinstance(output_path, io.IOBase):
output_path.write(out)
return out
[docs]def main():
parser = argparse.ArgumentParser(description='Generate C header file from binary input file')
parser.add_argument('input', help='Input file')
parser.add_argument('-o', '--output', required=False , help='Output file. Use input with .h appended if omitted')
parser.add_argument('-n', '--name', required=False , help='Name of C array. Use input filename if omitted')
parser.add_argument('-a', '--attributes', default=None, help='Attributes to prepend C array variable')
args = parser.parse_args()
if not args:
return 1
if not args.name:
args.name = os.path.splitext(os.path.basename(args.input))[0].upper().replace('-', '_').replace(' ', '_').replace('.', '_')
bin2header(
input=args.input,
output_path=args.output,
var_name=args.name,
attributes=args.attributes,
)
return 0
if __name__ == '__main__':
sys.exit(main())