• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# GStreamer
3# Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
4#
5# This library is free software; you can redistribute it and/or
6# modify it under the terms of the GNU Library General Public
7# License as published by the Free Software Foundation; either
8# version 2 of the License, or (at your option) any later version.
9#
10# This library is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13# Library General Public License for more details.
14#
15# You should have received a copy of the GNU Library General Public
16# License along with this library; if not, write to the
17# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18# Boston, MA 02111-1307, USA.
19#
20
21import binascii
22import sys
23import os
24import argparse
25
26autogenerated_notice = """/*
27 * This file is autogenerated by bin2array.py
28 */
29"""
30
31def make_sublist_group (lst: list, grp: int) -> list:
32    """
33    Group list elements into sublists.
34    make_sublist_group([1, 2, 3, 4, 5, 6, 7], 3) = [[1, 2, 3], [4, 5, 6], 7]
35    """
36    return [lst[i:i+grp] for i in range(0, len(lst), grp)]
37
38def generate_c_array_data (bindata: bytes,
39                           element_prefix: str,
40                           element_size: int,
41                           element_suffix: str,
42                           newline_value: str,
43                           newline_after: int,
44                           element_separator: str) -> str:
45    """
46    Generate the contents of a C array as a hex string
47    e.g:
48    0x10, 0x20, 0x30, 0x40,
49    0x50, 0x60, 0x70, 0x80,
50    """
51    hexstr = binascii.hexlify(bindata).decode("UTF-8")
52    array = []
53    for i in range(0, len(hexstr), 2 * element_size):
54        array += [element_prefix + hexstr[i:i + 2 * element_size] + element_suffix]
55
56    if newline_after:
57        array = make_sublist_group(array, newline_after)
58    else:
59        array = [array,]
60
61    return newline_value.join([element_separator.join(e) + element_separator for e in array])
62
63def decorate_c_array_data (hexdata: str,
64                           var_name: str,
65                           var_type: str,
66                           newline_value: str):
67    """
68    Place @hexdata into a valid C array named @var_name of C type @var_type.
69    """
70    ret = var_type + " " + var_name + "[] = {" + newline_value
71    ret += hexdata + newline_value
72    ret += "};" + newline_value
73    return ret
74
75def main(args):
76    parser = argparse.ArgumentParser(description='Convert binary file to C-style array initializer.')
77    parser.add_argument("-i", "--input", help="the file to be converted")
78    parser.add_argument("--output", help="c source file location")
79    parser.add_argument("--header-output", help="c header file location")
80    parser.add_argument("--linebreak", type=int, help="add linebreak after every N element")
81    parser.add_argument("--linebreak-value", default="\n", help="use what sequence to break lines, defaults to \"\\n\"")
82    parser.add_argument("--separator", default=", ", help="use what to separate elements, defaults to \", \"")
83    parser.add_argument("--array-name", default="array_data", help="name of the resulting array")
84    parser.add_argument("--element-type", default="char", help="C type for the array")
85    parser.add_argument("--element-size", type=int, default=1, help="how many bytes per element")
86    parser.add_argument("--element-prefix", default="0x", help="string to be added to the head of element, defaults to \"0x\"")
87    parser.add_argument("--element-suffix", default="", help="string to be added to the tail of element, defaults to none")
88    parser.add_argument("--c-include", help="header to include")
89    args = parser.parse_args(args)
90
91    with open(args.input, 'rb') as f:
92        file_content = f.read()
93
94    # don't deal with unaligned content
95    assert len(file_content) % args.element_size == 0
96
97    # get a relative path from the source file to the header to use in an #include
98    source_to_header = os.path.relpath (os.path.dirname (args.header_output),
99                                        os.path.dirname (args.output))
100
101    ret = autogenerated_notice
102    ret += "#include \""
103    ret += os.path.join (source_to_header,
104                         os.path.basename (args.header_output))
105    ret += "\"" + args.linebreak_value
106
107    arr_data = generate_c_array_data (file_content,
108                                      args.element_prefix,
109                                      args.element_size,
110                                      args.element_suffix,
111                                      args.linebreak_value,
112                                      args.linebreak,
113                                      args.separator)
114    ret += decorate_c_array_data (arr_data,
115                                  args.array_name,
116                                  args.element_type,
117                                  args.linebreak_value)
118
119    with open (args.output, 'w') as f:
120        f.write (ret)
121
122    # write companion header
123    with open(args.header_output, 'w') as f:
124        f.write (autogenerated_notice)
125        if args.c_include:
126            f.write ("#include <" + args.c_include + ">" + args.linebreak_value)
127        f.write ("extern " + args.element_type + " " + args.array_name + "[];" + args.linebreak_value)
128        f.write ("#define " + args.array_name + "_size " + str(len(file_content) // args.element_size))
129
130if __name__ == "__main__":
131    sys.exit(main(sys.argv[1:]))
132