#! /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.")