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