bin2c.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. # Copyright (c) 2024-2025 Le Juez Victor
  2. #
  3. # This software is provided "as-is", without any express or implied warranty. In no event
  4. # will the authors be held liable for any damages arising from the use of this software.
  5. #
  6. # Permission is granted to anyone to use this software for any purpose, including commercial
  7. # applications, and to alter it and redistribute it freely, subject to the following restrictions:
  8. #
  9. # 1. The origin of this software must not be misrepresented; you must not claim that you
  10. # wrote the original software. If you use this software in a product, an acknowledgment
  11. # in the product documentation would be appreciated but is not required.
  12. #
  13. # 2. Altered source versions must be plainly marked as such, and must not be misrepresented
  14. # as being the original software.
  15. #
  16. # 3. This notice may not be removed or altered from any source distribution.
  17. import sys, os, argparse
  18. def to_identifier(name):
  19. """Convert filename to valid C identifier"""
  20. return name.upper().replace('.', '_').replace('-', '_')
  21. def write_header_from_file(file_path, out_path):
  22. """Convert a file into a C header with null termination"""
  23. name = os.path.basename(file_path)
  24. guard = to_identifier(name) + '_H'
  25. array_name = to_identifier(name)
  26. with open(file_path, 'rb') as f:
  27. data = f.read()
  28. write_data_to_header(data, out_path, guard, array_name)
  29. def write_header_from_string(input_string, array_name, out_path):
  30. """Convert a string into a C header with null termination"""
  31. guard = to_identifier(array_name) + '_H'
  32. array_name = to_identifier(array_name)
  33. # Interpret escape sequences like \n, \t, etc., then encode to bytes
  34. data = input_string.encode('utf-8').decode('unicode_escape').encode('utf-8')
  35. write_data_to_header(data, out_path, guard, array_name)
  36. def write_data_to_header(data, out_path, guard, array_name):
  37. """Write binary data to C header with null termination"""
  38. # Always ensure there is a null terminator, even if it's duplicated
  39. # Because some formats will include one by default and others won't
  40. # A '\0' must therefore always be added for simplicity
  41. data = data + b'\0'
  42. # Size without null terminator for convenience
  43. data_size = len(data) - 1
  44. with open(out_path, 'w') as f:
  45. f.write(f"#ifndef {guard}\n")
  46. f.write(f"#define {guard}\n\n")
  47. f.write(f"static const char {array_name}[] = {{\n")
  48. # Write bytes in groups of 16 per line for readability
  49. for i, byte in enumerate(data):
  50. if i % 16 == 0:
  51. f.write(" ")
  52. f.write(f"0x{byte:02x}")
  53. if i < len(data) - 1:
  54. f.write(", ")
  55. if (i + 1) % 16 == 0 or i == len(data) - 1:
  56. f.write("\n")
  57. f.write("};\n\n")
  58. f.write(f"#define {array_name}_SIZE {data_size}\n\n")
  59. f.write(f"#endif // {guard}\n")
  60. def main():
  61. parser = argparse.ArgumentParser(
  62. description='Convert a file or string to a null-terminated C array header'
  63. )
  64. parser.add_argument('output', help='Output header file (.h)')
  65. input_group = parser.add_mutually_exclusive_group(required=True)
  66. input_group.add_argument('-f', '--file', help='Input file to convert')
  67. input_group.add_argument('-s', '--string', help='String to convert')
  68. parser.add_argument('-n', '--name', help='Array name (required with --string)')
  69. args = parser.parse_args()
  70. if args.string and not args.name:
  71. parser.error("--name is required when using --string")
  72. if args.file:
  73. write_header_from_file(args.file, args.output)
  74. elif args.string:
  75. write_header_from_string(args.string, args.name, args.output)
  76. if __name__ == "__main__":
  77. main()