diff options
Diffstat (limited to 'Source/cmHexFileConverter.cxx')
-rw-r--r-- | Source/cmHexFileConverter.cxx | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/Source/cmHexFileConverter.cxx b/Source/cmHexFileConverter.cxx new file mode 100644 index 0000000..190f2e3 --- /dev/null +++ b/Source/cmHexFileConverter.cxx @@ -0,0 +1,212 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmHexFileConverter.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include "cmSystemTools.h" + +#define INTEL_HEX_MIN_LINE_LENGTH (1 + 8 + 2) +#define INTEL_HEX_MAX_LINE_LENGTH (1 + 8 + (256 * 2) + 2) +#define MOTOROLA_SREC_MIN_LINE_LENGTH (2 + 2 + 4 + 2) +#define MOTOROLA_SREC_MAX_LINE_LENGTH (2 + 2 + 8 + (256 * 2) + 2) + +static unsigned int ChompStrlen(const char* line) +{ + if (line == nullptr) { + return 0; + } + unsigned int length = static_cast<unsigned int>(strlen(line)); + if ((line[length - 1] == '\n') || (line[length - 1] == '\r')) { + length--; + } + if ((line[length - 1] == '\n') || (line[length - 1] == '\r')) { + length--; + } + return length; +} + +static bool OutputBin(FILE* file, const char* buf, unsigned int startIndex, + unsigned int stopIndex) +{ + bool success = true; + char hexNumber[3]; + hexNumber[2] = '\0'; + char outBuf[256]; + unsigned int outBufCount = 0; + for (unsigned int i = startIndex; i < stopIndex; i += 2) { + hexNumber[0] = buf[i]; + hexNumber[1] = buf[i + 1]; + unsigned int convertedByte = 0; + if (sscanf(hexNumber, "%x", &convertedByte) != 1) { + success = false; + break; + } + outBuf[outBufCount] = static_cast<char>(convertedByte & 0xff); + outBufCount++; + } + if (success) { + success = (fwrite(outBuf, 1, outBufCount, file) == outBufCount); + } + return success; +} + +// see http://www.die.net/doc/linux/man/man5/srec.5.html +static bool ConvertMotorolaSrecLine(const char* buf, FILE* outFile) +{ + unsigned int slen = ChompStrlen(buf); + if ((slen < MOTOROLA_SREC_MIN_LINE_LENGTH) || + (slen > MOTOROLA_SREC_MAX_LINE_LENGTH)) { + return false; + } + + // line length must be even + if (slen % 2 == 1) { + return false; + } + + if (buf[0] != 'S') { + return false; + } + + unsigned int dataStart = 0; + // ignore extra address records + if ((buf[1] == '5') || (buf[1] == '7') || (buf[1] == '8') || + (buf[1] == '9')) { + return true; + } + if (buf[1] == '1') { + dataStart = 8; + } else if (buf[1] == '2') { + dataStart = 10; + } else if (buf[1] == '3') { + dataStart = 12; + } else // unknown record type + { + return false; + } + + // ignore the last two bytes (checksum) + return OutputBin(outFile, buf, dataStart, slen - 2); +} + +// see http://en.wikipedia.org/wiki/Intel_hex +static bool ConvertIntelHexLine(const char* buf, FILE* outFile) +{ + unsigned int slen = ChompStrlen(buf); + if ((slen < INTEL_HEX_MIN_LINE_LENGTH) || + (slen > INTEL_HEX_MAX_LINE_LENGTH)) { + return false; + } + + // line length must be odd + if (slen % 2 == 0) { + return false; + } + + if ((buf[0] != ':') || (buf[7] != '0')) { + return false; + } + + unsigned int dataStart = 0; + if ((buf[8] == '0') || (buf[8] == '1')) { + dataStart = 9; + } + // ignore extra address records + else if ((buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') || + (buf[8] == '5')) { + return true; + } else // unknown record type + { + return false; + } + + // ignore the last two bytes (checksum) + return OutputBin(outFile, buf, dataStart, slen - 2); +} + +cmHexFileConverter::FileType cmHexFileConverter::DetermineFileType( + const std::string& inFileName) +{ + char buf[1024]; + FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); + if (inFile == nullptr) { + return Binary; + } + + if (!fgets(buf, 1024, inFile)) { + buf[0] = 0; + } + fclose(inFile); + FileType type = Binary; + unsigned int minLineLength = 0; + unsigned int maxLineLength = 0; + if (buf[0] == ':') // might be an intel hex file + { + type = IntelHex; + minLineLength = INTEL_HEX_MIN_LINE_LENGTH; + maxLineLength = INTEL_HEX_MAX_LINE_LENGTH; + } else if (buf[0] == 'S') // might be a motorola srec file + { + type = MotorolaSrec; + minLineLength = MOTOROLA_SREC_MIN_LINE_LENGTH; + maxLineLength = MOTOROLA_SREC_MAX_LINE_LENGTH; + } else { + return Binary; + } + + unsigned int slen = ChompStrlen(buf); + if ((slen < minLineLength) || (slen > maxLineLength)) { + return Binary; + } + + for (unsigned int i = 1; i < slen; i++) { + if (!isxdigit(buf[i])) { + return Binary; + } + } + return type; +} + +bool cmHexFileConverter::TryConvert(const std::string& inFileName, + const std::string& outFileName) +{ + FileType type = DetermineFileType(inFileName); + if (type == Binary) { + return false; + } + + // try to open the file + FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); + FILE* outFile = cmsys::SystemTools::Fopen(outFileName, "wb"); + if ((inFile == nullptr) || (outFile == nullptr)) { + if (inFile != nullptr) { + fclose(inFile); + } + if (outFile != nullptr) { + fclose(outFile); + } + return false; + } + + // convert them line by line + bool success = false; + char buf[1024]; + while (fgets(buf, 1024, inFile) != nullptr) { + if (type == MotorolaSrec) { + success = ConvertMotorolaSrecLine(buf, outFile); + } else if (type == IntelHex) { + success = ConvertIntelHexLine(buf, outFile); + } + if (!success) { + break; + } + } + + // close them again + fclose(inFile); + fclose(outFile); + return success; +} |