bin2c.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * Copyright 2011-2025 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bx/blob/master/LICENSE
  4. */
  5. #include <bx/allocator.h>
  6. #include <bx/commandline.h>
  7. #include <bx/file.h>
  8. #include <bx/string.h>
  9. #include <bx/debug.h>
  10. class Bin2cWriter : public bx::WriterI
  11. {
  12. public:
  13. Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
  14. : m_mb(_allocator)
  15. , m_mw(&m_mb)
  16. , m_name(_name)
  17. , m_outputAsCStr(false)
  18. {
  19. }
  20. virtual ~Bin2cWriter()
  21. {
  22. }
  23. virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
  24. {
  25. bool asCStr = true;
  26. const char* data = (const char*)_data;
  27. for (int32_t ii = 0; ii < _size && asCStr; ++ii)
  28. {
  29. char ch = data[ii];
  30. asCStr &= false
  31. || bx::isPrint(ch)
  32. || bx::isSpace(ch)
  33. ;
  34. }
  35. m_outputAsCStr = asCStr;
  36. return bx::write(&m_mw, _data, _size, _err);
  37. }
  38. void output(bx::WriterI* _writer)
  39. {
  40. if (m_outputAsCStr)
  41. {
  42. outputString(_writer);
  43. }
  44. else
  45. {
  46. outputHex(_writer);
  47. }
  48. }
  49. void outputString(bx::WriterI* _writer)
  50. {
  51. const uint8_t* data = (const uint8_t*)m_mb.more(0);
  52. uint32_t size = uint32_t(bx::seek(&m_mw) );
  53. bx::Error err;
  54. bx::write(
  55. _writer
  56. , &err
  57. , "static const char* %.*s = /* Generated with bin2c. */\n\t\""
  58. , m_name.getLength()
  59. , m_name.getPtr()
  60. );
  61. if (NULL != data)
  62. {
  63. bool escaped = false;
  64. for (uint32_t ii = 0; ii < size; ++ii)
  65. {
  66. const char ch = char(data[ii]);
  67. if (!escaped)
  68. {
  69. switch (ch)
  70. {
  71. case '\"': bx::write(_writer, "\\\"", &err); break;
  72. case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
  73. case '\r': bx::write(_writer, "\\r", &err); break;
  74. case '\\': escaped = true; [[fallthrough]];
  75. default: bx::write(_writer, ch, &err); break;
  76. }
  77. }
  78. else
  79. {
  80. switch (ch)
  81. {
  82. case '\n': bx::write(_writer, "\\\"\n\t\"", &err); break;
  83. case '\r': [[fallthrough]];
  84. case '\t': bx::write(_writer, "\\", &err); [[fallthrough]];
  85. default : bx::write(_writer, ch, &err); break;
  86. }
  87. escaped = false;
  88. }
  89. }
  90. }
  91. bx::write(_writer, &err, "\"\n\t;\n");
  92. }
  93. void outputHex(bx::WriterI* _writer)
  94. {
  95. #define HEX_DUMP_WIDTH 16
  96. #define HEX_DUMP_SPACE_WIDTH 96
  97. #define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
  98. const uint8_t* data = (const uint8_t*)m_mb.more(0);
  99. uint32_t size = uint32_t(bx::seek(&m_mw) );
  100. bx::Error err;
  101. bx::write(
  102. _writer
  103. , &err
  104. , "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
  105. , m_name.getLength()
  106. , m_name.getPtr()
  107. , size
  108. );
  109. if (NULL != data)
  110. {
  111. char hex[HEX_DUMP_SPACE_WIDTH+1];
  112. char ascii[HEX_DUMP_WIDTH+1];
  113. uint32_t hexPos = 0;
  114. uint32_t asciiPos = 0;
  115. for (uint32_t ii = 0; ii < size; ++ii)
  116. {
  117. bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
  118. hexPos += 6;
  119. ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
  120. asciiPos++;
  121. if (HEX_DUMP_WIDTH == asciiPos)
  122. {
  123. ascii[asciiPos] = '\0';
  124. bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
  125. data += asciiPos;
  126. hexPos = 0;
  127. asciiPos = 0;
  128. }
  129. }
  130. if (0 != asciiPos)
  131. {
  132. ascii[asciiPos] = '\0';
  133. bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
  134. }
  135. }
  136. bx::write(_writer, &err, "};\n");
  137. #undef HEX_DUMP_WIDTH
  138. #undef HEX_DUMP_SPACE_WIDTH
  139. #undef HEX_DUMP_FORMAT
  140. }
  141. bx::MemoryBlock m_mb;
  142. bx::MemoryWriter m_mw;
  143. bx::StringView m_name;
  144. bool m_outputAsCStr;
  145. };
  146. void error(const char* _format, ...)
  147. {
  148. bx::WriterI* stdOut = bx::getStdOut();
  149. bx::Error err;
  150. va_list argList;
  151. va_start(argList, _format);
  152. bx::write(stdOut, &err, "Error:\n");
  153. bx::write(stdOut, _format, argList, &err);
  154. bx::write(stdOut, &err, "\n\n");
  155. va_end(argList);
  156. }
  157. void help(const char* _error = NULL)
  158. {
  159. bx::WriterI* stdOut = bx::getStdOut();
  160. bx::Error err;
  161. if (NULL != _error)
  162. {
  163. error(_error);
  164. }
  165. bx::write(stdOut, &err
  166. , "bin2c, binary to C\n"
  167. "Copyright 2011-2025 Branimir Karadzic. All rights reserved.\n"
  168. "License: https://github.com/bkaradzic/bx/blob/master/LICENSE\n\n"
  169. );
  170. bx::write(stdOut, &err
  171. , "Usage: bin2c -f <in> -o <out> -n <name>\n"
  172. "\n"
  173. "Options:\n"
  174. " -f <file path> Input file path.\n"
  175. " -o <file path> Output file path.\n"
  176. " -n <name> Array name.\n"
  177. "\n"
  178. "For additional information, see https://github.com/bkaradzic/bx\n"
  179. );
  180. }
  181. int main(int _argc, const char* _argv[])
  182. {
  183. bx::CommandLine cmdLine(_argc, _argv);
  184. if (cmdLine.hasArg('h', "help") )
  185. {
  186. help();
  187. return bx::kExitFailure;
  188. }
  189. bx::FilePath filePath = cmdLine.findOption('f');
  190. if (filePath.isEmpty() )
  191. {
  192. help("Input file name must be specified.");
  193. return bx::kExitFailure;
  194. }
  195. bx::FilePath outFilePath = cmdLine.findOption('o');
  196. if (outFilePath.isEmpty() )
  197. {
  198. help("Output file name must be specified.");
  199. return bx::kExitFailure;
  200. }
  201. bx::StringView name = cmdLine.findOption('n');
  202. if (name.isEmpty() )
  203. {
  204. name.set("data");
  205. }
  206. void* data = NULL;
  207. uint32_t size = 0;
  208. bx::FileReader fr;
  209. if (bx::open(&fr, filePath) )
  210. {
  211. size = uint32_t(bx::getSize(&fr) );
  212. bx::DefaultAllocator allocator;
  213. data = bx::alloc(&allocator, size);
  214. bx::read(&fr, data, size, bx::ErrorAssert{});
  215. bx::close(&fr);
  216. bx::FileWriter fw;
  217. if (bx::open(&fw, outFilePath) )
  218. {
  219. Bin2cWriter writer(&allocator, name);
  220. bx::write(&writer, data, size, bx::ErrorAssert{});
  221. writer.output(&fw);
  222. bx::close(&fw);
  223. }
  224. else
  225. {
  226. bx::StringView path = outFilePath;
  227. error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
  228. }
  229. bx::free(&allocator, data);
  230. }
  231. else
  232. {
  233. bx::StringView path = filePath;
  234. error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
  235. }
  236. return 0;
  237. }