1
0

cmHexFileConverter.cxx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmHexFileConverter.h"
  4. #include <cctype>
  5. #include <cstdio>
  6. #include <cstring>
  7. #include "cmSystemTools.h"
  8. #define INTEL_HEX_MIN_LINE_LENGTH (1 + 8 + 2)
  9. #define INTEL_HEX_MAX_LINE_LENGTH (1 + 8 + (256 * 2) + 2)
  10. #define MOTOROLA_SREC_MIN_LINE_LENGTH (2 + 2 + 4 + 2)
  11. #define MOTOROLA_SREC_MAX_LINE_LENGTH (2 + 2 + 8 + (256 * 2) + 2)
  12. static unsigned int ChompStrlen(const char* line)
  13. {
  14. if (!line) {
  15. return 0;
  16. }
  17. unsigned int length = static_cast<unsigned int>(strlen(line));
  18. if ((line[length - 1] == '\n') || (line[length - 1] == '\r')) {
  19. length--;
  20. }
  21. if ((line[length - 1] == '\n') || (line[length - 1] == '\r')) {
  22. length--;
  23. }
  24. return length;
  25. }
  26. static bool OutputBin(FILE* file, const char* buf, unsigned int startIndex,
  27. unsigned int stopIndex)
  28. {
  29. bool success = true;
  30. char hexNumber[3];
  31. hexNumber[2] = '\0';
  32. char outBuf[256];
  33. unsigned int outBufCount = 0;
  34. for (unsigned int i = startIndex; i < stopIndex; i += 2) {
  35. hexNumber[0] = buf[i];
  36. hexNumber[1] = buf[i + 1];
  37. unsigned int convertedByte = 0;
  38. if (sscanf(hexNumber, "%x", &convertedByte) != 1) {
  39. success = false;
  40. break;
  41. }
  42. outBuf[outBufCount] = static_cast<char>(convertedByte & 0xff);
  43. outBufCount++;
  44. }
  45. if (success) {
  46. success = (fwrite(outBuf, 1, outBufCount, file) == outBufCount);
  47. }
  48. return success;
  49. }
  50. // see http://www.die.net/doc/linux/man/man5/srec.5.html
  51. static bool ConvertMotorolaSrecLine(const char* buf, FILE* outFile)
  52. {
  53. unsigned int slen = ChompStrlen(buf);
  54. if ((slen < MOTOROLA_SREC_MIN_LINE_LENGTH) ||
  55. (slen > MOTOROLA_SREC_MAX_LINE_LENGTH)) {
  56. return false;
  57. }
  58. // line length must be even
  59. if (slen % 2 == 1) {
  60. return false;
  61. }
  62. if (buf[0] != 'S') {
  63. return false;
  64. }
  65. unsigned int dataStart = 0;
  66. // ignore extra address records
  67. if ((buf[1] == '5') || (buf[1] == '7') || (buf[1] == '8') ||
  68. (buf[1] == '9')) {
  69. return true;
  70. }
  71. if (buf[1] == '1') {
  72. dataStart = 8;
  73. } else if (buf[1] == '2') {
  74. dataStart = 10;
  75. } else if (buf[1] == '3') {
  76. dataStart = 12;
  77. } else // unknown record type
  78. {
  79. return false;
  80. }
  81. // ignore the last two bytes (checksum)
  82. return OutputBin(outFile, buf, dataStart, slen - 2);
  83. }
  84. // see http://en.wikipedia.org/wiki/Intel_hex
  85. static bool ConvertIntelHexLine(const char* buf, FILE* outFile)
  86. {
  87. unsigned int slen = ChompStrlen(buf);
  88. if ((slen < INTEL_HEX_MIN_LINE_LENGTH) ||
  89. (slen > INTEL_HEX_MAX_LINE_LENGTH)) {
  90. return false;
  91. }
  92. // line length must be odd
  93. if (slen % 2 == 0) {
  94. return false;
  95. }
  96. if ((buf[0] != ':') || (buf[7] != '0')) {
  97. return false;
  98. }
  99. unsigned int dataStart = 0;
  100. if ((buf[8] == '0') || (buf[8] == '1')) {
  101. dataStart = 9;
  102. }
  103. // ignore extra address records
  104. else if ((buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') ||
  105. (buf[8] == '5')) {
  106. return true;
  107. } else // unknown record type
  108. {
  109. return false;
  110. }
  111. // ignore the last two bytes (checksum)
  112. return OutputBin(outFile, buf, dataStart, slen - 2);
  113. }
  114. cmHexFileConverter::FileType cmHexFileConverter::DetermineFileType(
  115. const std::string& inFileName)
  116. {
  117. char buf[1024];
  118. FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb");
  119. if (!inFile) {
  120. return Binary;
  121. }
  122. if (!fgets(buf, 1024, inFile)) {
  123. buf[0] = 0;
  124. }
  125. fclose(inFile);
  126. FileType type = Binary;
  127. unsigned int minLineLength = 0;
  128. unsigned int maxLineLength = 0;
  129. if (buf[0] == ':') // might be an intel hex file
  130. {
  131. type = IntelHex;
  132. minLineLength = INTEL_HEX_MIN_LINE_LENGTH;
  133. maxLineLength = INTEL_HEX_MAX_LINE_LENGTH;
  134. } else if (buf[0] == 'S') // might be a motorola srec file
  135. {
  136. type = MotorolaSrec;
  137. minLineLength = MOTOROLA_SREC_MIN_LINE_LENGTH;
  138. maxLineLength = MOTOROLA_SREC_MAX_LINE_LENGTH;
  139. } else {
  140. return Binary;
  141. }
  142. unsigned int slen = ChompStrlen(buf);
  143. if ((slen < minLineLength) || (slen > maxLineLength)) {
  144. return Binary;
  145. }
  146. for (unsigned int i = 1; i < slen; i++) {
  147. if (!isxdigit(buf[i])) {
  148. return Binary;
  149. }
  150. }
  151. return type;
  152. }
  153. bool cmHexFileConverter::TryConvert(const std::string& inFileName,
  154. const std::string& outFileName)
  155. {
  156. FileType type = DetermineFileType(inFileName);
  157. if (type == Binary) {
  158. return false;
  159. }
  160. // try to open the file
  161. FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb");
  162. FILE* outFile = cmsys::SystemTools::Fopen(outFileName, "wb");
  163. if (!inFile || !outFile) {
  164. if (inFile) {
  165. fclose(inFile);
  166. }
  167. if (outFile) {
  168. fclose(outFile);
  169. }
  170. return false;
  171. }
  172. // convert them line by line
  173. bool success = false;
  174. char buf[1024];
  175. while (fgets(buf, 1024, inFile)) {
  176. if (type == MotorolaSrec) {
  177. success = ConvertMotorolaSrecLine(buf, outFile);
  178. } else if (type == IntelHex) {
  179. success = ConvertIntelHexLine(buf, outFile);
  180. }
  181. if (!success) {
  182. break;
  183. }
  184. }
  185. // close them again
  186. fclose(inFile);
  187. fclose(outFile);
  188. return success;
  189. }