#!/usr/bin/env python3 import argparse import os, re, sys, struct import textwrap import traceback def sanitize_name(name): # replace special characters with underscores name = re.sub('[^a-zA-Z0-9_]', '_', name) # clean up name = name.strip('_') # not needed: 'DATA_' prefix #if name[0] in "0123456789": name = '_' + name return name def main(): parser = argparse.ArgumentParser( description="Convert data files into byte arrays in a C header") parser.add_argument('--width', type=int, default=8, help="data width") parser.add_argument('input', nargs='*', action='append', help="Input data file to convert to a header file. If " +"none are specified, data is read from standard input.") parser.add_argument('--output', type=str, default=None, help="Output header file path, standard output if none specified") args = parser.parse_args() #print(repr(args), file=sys.stderr) inputfiles = args.input[0] datafiles = {} if len(inputfiles) == 0: # need to read from stdio datafiles['data'] = sys.stdin.buffer.read() else: for path in inputfiles: # cannot use os.access, POSIX-only if not os.path.exists(path) or os.path.isdir(path): print("ERROR: input path '%s' does not exist, or is not a file" % path) sys.exit(1) # get base name of the path (no folder names, no extension) name = os.path.splitext(os.path.split(path)[1])[0] # make it usable for C code names name = sanitize_name(name) # now read the data data = None try: with open(path, 'rb') as f: data = f.read() except IOError: print("ERROR: input file '%s' not readable" % path) traceback.print_exc() sys.exit(1) wlut = {8:'B',16:'H',32:'I',64:'Q'} denom= args.width//8 data = struct.unpack('<'+((wlut[args.width])*(len(data)//denom)), data) # add it to the list datafiles[name] = data #print(repr(datafiles), file=sys.stderr) outname = "hardcoded_data" if args.output is not None: outname = os.path.splitext(os.path.split(args.output)[1])[0] outname = sanitize_name(outname) # generate the C source code: one uint8_t array for each data file # variable name is "DATA_" # (width=84 is the equivalent of 16 bytes/line) headersrc = '\n\n'.join( "static uint%d_t DATA_%s[%d] = {\n%s\n};" % (args.width, name, len(data), \ textwrap.fill(', '.join(("0x%0"+str(args.width//4)+"x") % b for b in data), width=81, initial_indent=" ", subsequent_indent=" ") ) for name, data in datafiles.items() ) # a C header needs include guards headersrc = """ #ifndef AUTOGEN_{0}_H_ #define AUTOGEN_{0}_H_ {1} #endif """.format(outname.upper(), headersrc) #print(headersrc, file=sys.stderr) if args.output is None: # no output specified -> output to stdout sys.stdout.write(headersrc) else: try: with open(args.output, 'w') as f: f.write(headersrc) except IOError: print("ERROR: output file '%s' not writable" % args.output) traceback.print_exc() sys.exit(1) if __name__ == '__main__': main()