| #!/usr/bin/env python3 | |
| # GStreamer | |
| # Copyright (C) 2020 Matthew Waters <matthew@centricular.com> | |
| # | |
| # This library is free software; you can redistribute it and/or | |
| # modify it under the terms of the GNU Library General Public | |
| # License as published by the Free Software Foundation; either | |
| # version 2 of the License, or (at your option) any later version. | |
| # | |
| # This library is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| # Library General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU Library General Public | |
| # License along with this library; if not, write to the | |
| # Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
| # Boston, MA 02111-1307, USA. | |
| # | |
| import binascii | |
| import sys | |
| import os | |
| import argparse | |
| autogenerated_notice = """/* | |
| * This file is autogenerated by bin2array.py | |
| */ | |
| """ | |
| def make_sublist_group (lst: list, grp: int) -> list: | |
| """ | |
| Group list elements into sublists. | |
| make_sublist_group([1, 2, 3, 4, 5, 6, 7], 3) = [[1, 2, 3], [4, 5, 6], 7] | |
| """ | |
| return [lst[i:i+grp] for i in range(0, len(lst), grp)] | |
| def generate_c_array_data (bindata: bytes, | |
| element_prefix: str, | |
| element_size: int, | |
| element_suffix: str, | |
| newline_value: str, | |
| newline_after: int, | |
| element_separator: str) -> str: | |
| """ | |
| Generate the contents of a C array as a hex string | |
| e.g: | |
| 0x10, 0x20, 0x30, 0x40, | |
| 0x50, 0x60, 0x70, 0x80, | |
| """ | |
| hexstr = binascii.hexlify(bindata).decode("UTF-8") | |
| array = [] | |
| for i in range(0, len(hexstr), 2 * element_size): | |
| array += [element_prefix + hexstr[i:i + 2 * element_size] + element_suffix] | |
| if newline_after: | |
| array = make_sublist_group(array, newline_after) | |
| else: | |
| array = [array,] | |
| return newline_value.join([element_separator.join(e) + element_separator for e in array]) | |
| def decorate_c_array_data (hexdata: str, | |
| var_name: str, | |
| var_type: str, | |
| newline_value: str): | |
| """ | |
| Place @hexdata into a valid C array named @var_name of C type @var_type. | |
| """ | |
| ret = var_type + " " + var_name + "[] = {" + newline_value | |
| ret += hexdata + newline_value | |
| ret += "};" + newline_value | |
| return ret | |
| def main(args): | |
| parser = argparse.ArgumentParser(description='Convert binary file to C-style array initializer.') | |
| parser.add_argument("-i", "--input", help="the file to be converted") | |
| parser.add_argument("--output", help="c source file location") | |
| parser.add_argument("--header-output", help="c header file location") | |
| parser.add_argument("--linebreak", type=int, help="add linebreak after every N element") | |
| parser.add_argument("--linebreak-value", default="\n", help="use what sequence to break lines, defaults to \"\\n\"") | |
| parser.add_argument("--separator", default=", ", help="use what to separate elements, defaults to \", \"") | |
| parser.add_argument("--array-name", default="array_data", help="name of the resulting array") | |
| parser.add_argument("--element-type", default="char", help="C type for the array") | |
| parser.add_argument("--element-size", type=int, default=1, help="how many bytes per element") | |
| parser.add_argument("--element-prefix", default="0x", help="string to be added to the head of element, defaults to \"0x\"") | |
| parser.add_argument("--element-suffix", default="", help="string to be added to the tail of element, defaults to none") | |
| parser.add_argument("--c-include", help="header to include") | |
| args = parser.parse_args(args) | |
| with open(args.input, 'rb') as f: | |
| file_content = f.read() | |
| # don't deal with unaligned content | |
| assert len(file_content) % args.element_size == 0 | |
| # get a relative path from the source file to the header to use in an #include | |
| source_to_header = os.path.relpath (os.path.dirname (args.header_output), | |
| os.path.dirname (args.output)) | |
| ret = autogenerated_notice | |
| ret += "#include \"" | |
| ret += os.path.join (source_to_header, | |
| os.path.basename (args.header_output)) | |
| ret += "\"" + args.linebreak_value | |
| arr_data = generate_c_array_data (file_content, | |
| args.element_prefix, | |
| args.element_size, | |
| args.element_suffix, | |
| args.linebreak_value, | |
| args.linebreak, | |
| args.separator) | |
| ret += decorate_c_array_data (arr_data, | |
| args.array_name, | |
| args.element_type, | |
| args.linebreak_value) | |
| with open (args.output, 'w') as f: | |
| f.write (ret) | |
| # write companion header | |
| with open(args.header_output, 'w') as f: | |
| f.write (autogenerated_notice) | |
| if args.c_include: | |
| f.write ("#include <" + args.c_include + ">" + args.linebreak_value) | |
| f.write ("extern " + args.element_type + " " + args.array_name + "[];" + args.linebreak_value) | |
| f.write ("#define " + args.array_name + "_size " + str(len(file_content) // args.element_size)) | |
| if __name__ == "__main__": | |
| sys.exit(main(sys.argv[1:])) |