blob: 6ea597ea5068395deb9fc787b2e37c4b8cd86d27 [file] [log] [blame]
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmHexFileConverter.h"
#include <stdio.h>
#include <string.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)
// might go to SystemTools ?
static bool cm_IsHexChar(char c)
{
return (((c >= '0') && (c <= '9'))
|| ((c >= 'a') && (c <= 'f'))
|| ((c >= 'A') && (c <= 'F')));
}
static unsigned int ChompStrlen(const char* line)
{
if (line == 0)
{
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;
}
else 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 char* inFileName)
{
char buf[1024];
FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb");
if (inFile == 0)
{
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 (!cm_IsHexChar(buf[i]))
{
return Binary;
}
}
return type;
}
bool cmHexFileConverter::TryConvert(const char* inFileName,
const char* 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 == 0) || (outFile == 0))
{
if (inFile != 0)
{
fclose(inFile);
}
if (outFile != 0)
{
fclose(outFile);
}
return false;
}
// convert them line by line
bool success = false;
char buf[1024];
while (fgets(buf, 1024, inFile) != 0)
{
if (type == MotorolaSrec)
{
success = ConvertMotorolaSrecLine(buf, outFile);
}
else if (type == IntelHex)
{
success = ConvertIntelHexLine(buf, outFile);
}
if (success == false)
{
break;
}
}
// close them again
fclose(inFile);
fclose(outFile);
return success;
}