#! /usr/bin/python3 # -*- coding: utf-8 -*- # The MIT License (MIT) # # Copyright (c) 2016 Peter Hinch # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import argparse import sys import os # UTILITIES FOR WRITING PYTHON SOURCECODE TO A FILE # ByteWriter takes as input a variable name and data values and writes # Python source to an output stream of the form # my_variable = b'\x01\x02\x03\x04\x05\x06\x07\x08'\ # Lines are broken with \ for readability. class ByteWriter(object): bytes_per_line = 16 def __init__(self, stream, varname): self.stream = stream self.stream.write('{} =\\\n'.format(varname)) self.bytecount = 0 # For line breaks def _eol(self): self.stream.write("'\\\n") def _eot(self): self.stream.write("'\n") def _bol(self): self.stream.write("b'") # Output a single byte def obyte(self, data): if not self.bytecount: self._bol() self.stream.write('\\x{:02x}'.format(data)) self.bytecount += 1 self.bytecount %= self.bytes_per_line if not self.bytecount: self._eol() # Output from a sequence def odata(self, bytelist): for byt in bytelist: self.obyte(byt) # ensure a correct final line def eot(self): # User force EOL if one hasn't occurred if self.bytecount: self._eot() self.stream.write('\n') # PYTHON FILE WRITING STR01 = """# Code generated by data_to_py.py. version = '0.1' """ STR02 = """_mvdata = memoryview(_data) def data(): return _mvdata """ def write_func(stream, name, arg): stream.write('def {}():\n return {}\n\n'.format(name, arg)) def write_data(op_path, ip_path): try: with open(ip_path, 'rb') as ip_stream: try: with open(op_path, 'w') as op_stream: write_stream(ip_stream, op_stream) except OSError: print("Can't open", op_path, 'for writing') return False except OSError: print("Can't open", ip_path) return False return True def write_stream(ip_stream, op_stream): op_stream.write(STR01) op_stream.write('\n') data = ip_stream.read() bw_data = ByteWriter(op_stream, '_data') bw_data.odata(data) bw_data.eot() op_stream.write(STR02) # PARSE COMMAND LINE ARGUMENTS def quit(msg): print(msg) sys.exit(1) DESC = """data_to_py.py Utility to convert an arbitrary binary file to Python source. Sample usage: data_to_py.py image.jpg image.py """ if __name__ == "__main__": parser = argparse.ArgumentParser(__file__, description=DESC, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('infile', type=str, help='Input file path') parser.add_argument('outfile', type=str, help='Path and name of output file. Must have .py extension.') args = parser.parse_args() if not os.path.isfile(args.infile): quit("Data filename does not exist") if not os.path.splitext(args.outfile)[1].upper() == '.PY': quit('Output filename must have a .py extension.') print('Writing Python file.') if not write_data(args.outfile, args.infile): sys.exit(1) print(args.outfile, 'written successfully.')