| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmHexFileConverter.h" | |
| #include <cstdio> | |
| #include <cstring> | |
| #include "cmsys/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(char const* line) | |
| { | |
| if (!line) { | |
| 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, char const* 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; | |
| } | |
| if (outBufCount >= sizeof(outBuf)) { | |
| 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(char const* 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(char const* 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( | |
| std::string const& inFileName) | |
| { | |
| char buf[1024]; | |
| FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); | |
| if (!inFile) { | |
| 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 (!cmsysString_isxdigit(buf[i])) { | |
| return Binary; | |
| } | |
| } | |
| return type; | |
| } | |
| bool cmHexFileConverter::TryConvert(std::string const& inFileName, | |
| std::string const& 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 || !outFile) { | |
| if (inFile) { | |
| fclose(inFile); | |
| } | |
| if (outFile) { | |
| fclose(outFile); | |
| } | |
| return false; | |
| } | |
| // convert them line by line | |
| bool success = false; | |
| char buf[1024]; | |
| while (fgets(buf, 1024, inFile)) { | |
| 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; | |
| } |