/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 | outBuf[outBufCount] = static_cast<char>(convertedByte & 0xff); |
49 | 0 | outBufCount++; |
50 | 0 | } |
51 | 0 | if (success) { |
52 | 0 | success = (fwrite(outBuf, 1, outBufCount, file) == outBufCount); |
53 | 0 | } |
54 | 0 | return success; |
55 | 0 | } |
56 | | |
57 | | // see http://www.die.net/doc/linux/man/man5/srec.5.html |
58 | | static bool ConvertMotorolaSrecLine(char const* buf, FILE* outFile) |
59 | 0 | { |
60 | 0 | unsigned int slen = ChompStrlen(buf); |
61 | 0 | if ((slen < MOTOROLA_SREC_MIN_LINE_LENGTH) || |
62 | 0 | (slen > MOTOROLA_SREC_MAX_LINE_LENGTH)) { |
63 | 0 | return false; |
64 | 0 | } |
65 | | |
66 | | // line length must be even |
67 | 0 | if (slen % 2 == 1) { |
68 | 0 | return false; |
69 | 0 | } |
70 | | |
71 | 0 | if (buf[0] != 'S') { |
72 | 0 | return false; |
73 | 0 | } |
74 | | |
75 | 0 | unsigned int dataStart = 0; |
76 | | // ignore extra address records |
77 | 0 | if ((buf[1] == '5') || (buf[1] == '7') || (buf[1] == '8') || |
78 | 0 | (buf[1] == '9')) { |
79 | 0 | return true; |
80 | 0 | } |
81 | 0 | if (buf[1] == '1') { |
82 | 0 | dataStart = 8; |
83 | 0 | } else if (buf[1] == '2') { |
84 | 0 | dataStart = 10; |
85 | 0 | } else if (buf[1] == '3') { |
86 | 0 | dataStart = 12; |
87 | 0 | } else // unknown record type |
88 | 0 | { |
89 | 0 | return false; |
90 | 0 | } |
91 | | |
92 | | // ignore the last two bytes (checksum) |
93 | 0 | return OutputBin(outFile, buf, dataStart, slen - 2); |
94 | 0 | } |
95 | | |
96 | | // see http://en.wikipedia.org/wiki/Intel_hex |
97 | | static bool ConvertIntelHexLine(char const* buf, FILE* outFile) |
98 | 0 | { |
99 | 0 | unsigned int slen = ChompStrlen(buf); |
100 | 0 | if ((slen < INTEL_HEX_MIN_LINE_LENGTH) || |
101 | 0 | (slen > INTEL_HEX_MAX_LINE_LENGTH)) { |
102 | 0 | return false; |
103 | 0 | } |
104 | | |
105 | | // line length must be odd |
106 | 0 | if (slen % 2 == 0) { |
107 | 0 | return false; |
108 | 0 | } |
109 | | |
110 | 0 | if ((buf[0] != ':') || (buf[7] != '0')) { |
111 | 0 | return false; |
112 | 0 | } |
113 | | |
114 | 0 | unsigned int dataStart = 0; |
115 | 0 | if ((buf[8] == '0') || (buf[8] == '1')) { |
116 | 0 | dataStart = 9; |
117 | 0 | } |
118 | | // ignore extra address records |
119 | 0 | else if ((buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') || |
120 | 0 | (buf[8] == '5')) { |
121 | 0 | return true; |
122 | 0 | } else // unknown record type |
123 | 0 | { |
124 | 0 | return false; |
125 | 0 | } |
126 | | |
127 | | // ignore the last two bytes (checksum) |
128 | 0 | return OutputBin(outFile, buf, dataStart, slen - 2); |
129 | 0 | } |
130 | | |
131 | | cmHexFileConverter::FileType cmHexFileConverter::DetermineFileType( |
132 | | std::string const& inFileName) |
133 | 0 | { |
134 | 0 | char buf[1024]; |
135 | 0 | FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); |
136 | 0 | if (!inFile) { |
137 | 0 | return Binary; |
138 | 0 | } |
139 | | |
140 | 0 | if (!fgets(buf, 1024, inFile)) { |
141 | 0 | buf[0] = 0; |
142 | 0 | } |
143 | 0 | fclose(inFile); |
144 | 0 | FileType type = Binary; |
145 | 0 | unsigned int minLineLength = 0; |
146 | 0 | unsigned int maxLineLength = 0; |
147 | 0 | if (buf[0] == ':') // might be an intel hex file |
148 | 0 | { |
149 | 0 | type = IntelHex; |
150 | 0 | minLineLength = INTEL_HEX_MIN_LINE_LENGTH; |
151 | 0 | maxLineLength = INTEL_HEX_MAX_LINE_LENGTH; |
152 | 0 | } else if (buf[0] == 'S') // might be a motorola srec file |
153 | 0 | { |
154 | 0 | type = MotorolaSrec; |
155 | 0 | minLineLength = MOTOROLA_SREC_MIN_LINE_LENGTH; |
156 | 0 | maxLineLength = MOTOROLA_SREC_MAX_LINE_LENGTH; |
157 | 0 | } else { |
158 | 0 | return Binary; |
159 | 0 | } |
160 | | |
161 | 0 | unsigned int slen = ChompStrlen(buf); |
162 | 0 | if ((slen < minLineLength) || (slen > maxLineLength)) { |
163 | 0 | return Binary; |
164 | 0 | } |
165 | | |
166 | 0 | for (unsigned int i = 1; i < slen; i++) { |
167 | 0 | if (!cmsysString_isxdigit(buf[i])) { |
168 | 0 | return Binary; |
169 | 0 | } |
170 | 0 | } |
171 | 0 | return type; |
172 | 0 | } |
173 | | |
174 | | bool cmHexFileConverter::TryConvert(std::string const& inFileName, |
175 | | std::string const& outFileName) |
176 | 0 | { |
177 | 0 | FileType type = DetermineFileType(inFileName); |
178 | 0 | if (type == Binary) { |
179 | 0 | return false; |
180 | 0 | } |
181 | | |
182 | | // try to open the file |
183 | 0 | FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); |
184 | 0 | FILE* outFile = cmsys::SystemTools::Fopen(outFileName, "wb"); |
185 | 0 | if (!inFile || !outFile) { |
186 | 0 | if (inFile) { |
187 | 0 | fclose(inFile); |
188 | 0 | } |
189 | 0 | if (outFile) { |
190 | 0 | fclose(outFile); |
191 | 0 | } |
192 | 0 | return false; |
193 | 0 | } |
194 | | |
195 | | // convert them line by line |
196 | 0 | bool success = false; |
197 | 0 | char buf[1024]; |
198 | 0 | while (fgets(buf, 1024, inFile)) { |
199 | 0 | if (type == MotorolaSrec) { |
200 | 0 | success = ConvertMotorolaSrecLine(buf, outFile); |
201 | 0 | } else if (type == IntelHex) { |
202 | 0 | success = ConvertIntelHexLine(buf, outFile); |
203 | 0 | } |
204 | 0 | if (!success) { |
205 | 0 | break; |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | // close them again |
210 | 0 | fclose(inFile); |
211 | 0 | fclose(outFile); |
212 | 0 | return success; |
213 | 0 | } |