cmHexFileConverter.cxx 5.3 KB

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