diff options
Diffstat (limited to 'CPP/7zip/Archive/IhexHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/IhexHandler.cpp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/IhexHandler.cpp b/CPP/7zip/Archive/IhexHandler.cpp new file mode 100644 index 0000000..05453ee --- /dev/null +++ b/CPP/7zip/Archive/IhexHandler.cpp | |||
@@ -0,0 +1,497 @@ | |||
1 | // IhexHandler.cpp | ||
2 | |||
3 | #include "StdAfx.h" | ||
4 | |||
5 | #include "../../../C/CpuArch.h" | ||
6 | |||
7 | #include "../../Common/ComTry.h" | ||
8 | #include "../../Common/DynamicBuffer.h" | ||
9 | #include "../../Common/IntToString.h" | ||
10 | #include "../../Common/MyVector.h" | ||
11 | |||
12 | #include "../../Windows/PropVariant.h" | ||
13 | |||
14 | #include "../Common/ProgressUtils.h" | ||
15 | #include "../Common/RegisterArc.h" | ||
16 | #include "../Common/StreamUtils.h" | ||
17 | #include "../Common/InBuffer.h" | ||
18 | |||
19 | namespace NArchive { | ||
20 | namespace NIhex { | ||
21 | |||
22 | /* We still don't support files with custom record types: 20, 22: used by Samsung */ | ||
23 | |||
24 | struct CBlock | ||
25 | { | ||
26 | CByteDynamicBuffer Data; | ||
27 | UInt32 Offset; | ||
28 | }; | ||
29 | |||
30 | class CHandler: | ||
31 | public IInArchive, | ||
32 | public CMyUnknownImp | ||
33 | { | ||
34 | bool _isArc; | ||
35 | bool _needMoreInput; | ||
36 | bool _dataError; | ||
37 | |||
38 | UInt64 _phySize; | ||
39 | |||
40 | CObjectVector<CBlock> _blocks; | ||
41 | public: | ||
42 | MY_UNKNOWN_IMP1(IInArchive) | ||
43 | INTERFACE_IInArchive(;) | ||
44 | }; | ||
45 | |||
46 | static const Byte kProps[] = | ||
47 | { | ||
48 | kpidPath, | ||
49 | kpidSize, | ||
50 | kpidVa | ||
51 | }; | ||
52 | |||
53 | IMP_IInArchive_Props | ||
54 | IMP_IInArchive_ArcProps_NO_Table | ||
55 | |||
56 | STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
57 | { | ||
58 | *numItems = _blocks.Size(); | ||
59 | return S_OK; | ||
60 | } | ||
61 | |||
62 | STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) | ||
63 | { | ||
64 | NWindows::NCOM::CPropVariant prop; | ||
65 | switch (propID) | ||
66 | { | ||
67 | case kpidPhySize: if (_phySize != 0) prop = _phySize; break; | ||
68 | case kpidErrorFlags: | ||
69 | { | ||
70 | UInt32 v = 0; | ||
71 | if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; | ||
72 | if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; | ||
73 | if (_dataError) v |= kpv_ErrorFlags_DataError; | ||
74 | prop = v; | ||
75 | } | ||
76 | } | ||
77 | prop.Detach(value); | ||
78 | return S_OK; | ||
79 | } | ||
80 | |||
81 | STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) | ||
82 | { | ||
83 | COM_TRY_BEGIN | ||
84 | NWindows::NCOM::CPropVariant prop; | ||
85 | const CBlock &block = _blocks[index]; | ||
86 | switch (propID) | ||
87 | { | ||
88 | case kpidSize: prop = (UInt64)block.Data.GetPos(); break; | ||
89 | case kpidVa: prop = block.Offset; break; | ||
90 | case kpidPath: | ||
91 | { | ||
92 | if (_blocks.Size() != 1) | ||
93 | { | ||
94 | char s[16]; | ||
95 | ConvertUInt32ToString(index, s); | ||
96 | prop = s; | ||
97 | } | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | prop.Detach(value); | ||
102 | return S_OK; | ||
103 | COM_TRY_END | ||
104 | } | ||
105 | |||
106 | static inline int HexToByte(unsigned c) | ||
107 | { | ||
108 | if (c >= '0' && c <= '9') return c - '0'; | ||
109 | if (c >= 'A' && c <= 'F') return c - 'A' + 10; | ||
110 | if (c >= 'a' && c <= 'f') return c - 'a' + 10; | ||
111 | return -1; | ||
112 | } | ||
113 | |||
114 | static int Parse(const Byte *p) | ||
115 | { | ||
116 | int c1 = HexToByte(p[0]); if (c1 < 0) return -1; | ||
117 | int c2 = HexToByte(p[1]); if (c2 < 0) return -1; | ||
118 | return (c1 << 4) | c2; | ||
119 | } | ||
120 | |||
121 | #define kType_Data 0 | ||
122 | #define kType_Eof 1 | ||
123 | #define kType_Seg 2 | ||
124 | // #define kType_CsIp 3 | ||
125 | #define kType_High 4 | ||
126 | // #define kType_Ip32 5 | ||
127 | |||
128 | #define kType_MAX 5 | ||
129 | |||
130 | #define IS_LINE_DELIMITER(c) ((c) == 0 || (c) == 10 || (c) == 13) | ||
131 | |||
132 | API_FUNC_static_IsArc IsArc_Ihex(const Byte *p, size_t size) | ||
133 | { | ||
134 | if (size < 1) | ||
135 | return k_IsArc_Res_NEED_MORE; | ||
136 | if (p[0] != ':') | ||
137 | return k_IsArc_Res_NO; | ||
138 | p++; | ||
139 | size--; | ||
140 | |||
141 | const unsigned kNumLinesToCheck = 3; // 1 line is OK also, but we don't want false detection | ||
142 | |||
143 | for (unsigned j = 0; j < kNumLinesToCheck; j++) | ||
144 | { | ||
145 | if (size < 4 * 2) | ||
146 | return k_IsArc_Res_NEED_MORE; | ||
147 | |||
148 | int num = Parse(p); | ||
149 | if (num < 0) | ||
150 | return k_IsArc_Res_NO; | ||
151 | |||
152 | int type = Parse(p + 6); | ||
153 | if (type < 0 || type > kType_MAX) | ||
154 | return k_IsArc_Res_NO; | ||
155 | |||
156 | unsigned numChars = ((unsigned)num + 5) * 2; | ||
157 | unsigned sum = 0; | ||
158 | |||
159 | for (unsigned i = 0; i < numChars; i += 2) | ||
160 | { | ||
161 | if (i + 2 > size) | ||
162 | return k_IsArc_Res_NEED_MORE; | ||
163 | int v = Parse(p + i); | ||
164 | if (v < 0) | ||
165 | return k_IsArc_Res_NO; | ||
166 | sum += (unsigned)v; | ||
167 | } | ||
168 | |||
169 | if ((sum & 0xFF) != 0) | ||
170 | return k_IsArc_Res_NO; | ||
171 | |||
172 | if (type == kType_Data) | ||
173 | { | ||
174 | // we don't want to open :0000000000 files | ||
175 | if (num == 0) | ||
176 | return k_IsArc_Res_NO; | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | if (type == kType_Eof) | ||
181 | { | ||
182 | if (num != 0) | ||
183 | return k_IsArc_Res_NO; | ||
184 | return k_IsArc_Res_YES; | ||
185 | } | ||
186 | if (p[2] != 0 || | ||
187 | p[3] != 0 || | ||
188 | p[4] != 0 || | ||
189 | p[5] != 0) | ||
190 | return k_IsArc_Res_NO; | ||
191 | if (type == kType_Seg || type == kType_High) | ||
192 | { | ||
193 | if (num != 2) | ||
194 | return k_IsArc_Res_NO; | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | if (num != 4) | ||
199 | return k_IsArc_Res_NO; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | p += numChars; | ||
204 | size -= numChars; | ||
205 | |||
206 | for (;;) | ||
207 | { | ||
208 | if (size == 0) | ||
209 | return k_IsArc_Res_NEED_MORE; | ||
210 | char b = *p++; | ||
211 | size--; | ||
212 | if (IS_LINE_DELIMITER(b)) | ||
213 | continue; | ||
214 | if (b == ':') | ||
215 | break; | ||
216 | return k_IsArc_Res_NO; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return k_IsArc_Res_YES; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) | ||
225 | { | ||
226 | COM_TRY_BEGIN | ||
227 | { | ||
228 | Close(); | ||
229 | try | ||
230 | { | ||
231 | const unsigned kStartSize = (2 + (256 + 5) + 2) * 2; | ||
232 | Byte temp[kStartSize]; | ||
233 | { | ||
234 | size_t size = kStartSize; | ||
235 | RINOK(ReadStream(stream, temp, &size)); | ||
236 | UInt32 isArcRes = IsArc_Ihex(temp, size); | ||
237 | if (isArcRes == k_IsArc_Res_NO) | ||
238 | return S_FALSE; | ||
239 | if (isArcRes == k_IsArc_Res_NEED_MORE && size != kStartSize) | ||
240 | return S_FALSE; | ||
241 | } | ||
242 | _isArc = true; | ||
243 | |||
244 | RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); | ||
245 | CInBuffer s; | ||
246 | if (!s.Create(1 << 15)) | ||
247 | return E_OUTOFMEMORY; | ||
248 | s.SetStream(stream); | ||
249 | s.Init(); | ||
250 | |||
251 | { | ||
252 | Byte b; | ||
253 | if (!s.ReadByte(b)) | ||
254 | { | ||
255 | _needMoreInput = true; | ||
256 | return S_FALSE; | ||
257 | } | ||
258 | if (b != ':') | ||
259 | { | ||
260 | _dataError = true; | ||
261 | return S_FALSE; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | UInt32 globalOffset = 0; | ||
266 | |||
267 | for (;;) | ||
268 | { | ||
269 | if (s.ReadBytes(temp, 2) != 2) | ||
270 | { | ||
271 | _needMoreInput = true; | ||
272 | return S_FALSE; | ||
273 | } | ||
274 | int num = Parse(temp); | ||
275 | if (num < 0) | ||
276 | { | ||
277 | _dataError = true; | ||
278 | return S_FALSE; | ||
279 | } | ||
280 | |||
281 | { | ||
282 | size_t numPairs = ((unsigned)num + 4); | ||
283 | size_t numBytes = numPairs * 2; | ||
284 | if (s.ReadBytes(temp, numBytes) != numBytes) | ||
285 | { | ||
286 | _needMoreInput = true; | ||
287 | return S_FALSE; | ||
288 | } | ||
289 | |||
290 | unsigned sum = num; | ||
291 | for (size_t i = 0; i < numPairs; i++) | ||
292 | { | ||
293 | int a = Parse(temp + i * 2); | ||
294 | if (a < 0) | ||
295 | { | ||
296 | _dataError = true; | ||
297 | return S_FALSE; | ||
298 | } | ||
299 | temp[i] = (Byte)a; | ||
300 | sum += a; | ||
301 | } | ||
302 | if ((sum & 0xFF) != 0) | ||
303 | { | ||
304 | _dataError = true; | ||
305 | return S_FALSE; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | unsigned type = temp[2]; | ||
310 | if (type > kType_MAX) | ||
311 | { | ||
312 | _dataError = true; | ||
313 | return S_FALSE; | ||
314 | } | ||
315 | |||
316 | UInt32 a = GetBe16(temp); | ||
317 | |||
318 | if (type == kType_Data) | ||
319 | { | ||
320 | if (num == 0) | ||
321 | { | ||
322 | // we don't want to open :0000000000 files | ||
323 | // maybe it can mean EOF in old-style files? | ||
324 | _dataError = true; | ||
325 | return S_FALSE; | ||
326 | } | ||
327 | // if (num != 0) | ||
328 | { | ||
329 | UInt32 offs = globalOffset + a; | ||
330 | CBlock *block = NULL; | ||
331 | if (!_blocks.IsEmpty()) | ||
332 | { | ||
333 | block = &_blocks.Back(); | ||
334 | if (block->Offset + block->Data.GetPos() != offs) | ||
335 | block = NULL; | ||
336 | } | ||
337 | if (!block) | ||
338 | { | ||
339 | block = &_blocks.AddNew(); | ||
340 | block->Offset = offs; | ||
341 | } | ||
342 | block->Data.AddData(temp + 3, (unsigned)num); | ||
343 | } | ||
344 | } | ||
345 | else if (type == kType_Eof) | ||
346 | { | ||
347 | _phySize = s.GetProcessedSize(); | ||
348 | { | ||
349 | Byte b; | ||
350 | if (s.ReadByte(b)) | ||
351 | { | ||
352 | if (b == 10) | ||
353 | _phySize++; | ||
354 | else if (b == 13) | ||
355 | { | ||
356 | _phySize++; | ||
357 | if (s.ReadByte(b)) | ||
358 | { | ||
359 | if (b == 10) | ||
360 | _phySize++; | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | return S_OK; | ||
366 | } | ||
367 | else | ||
368 | { | ||
369 | if (a != 0) | ||
370 | { | ||
371 | _dataError = true; | ||
372 | return S_FALSE; | ||
373 | } | ||
374 | if (type == kType_Seg || type == kType_High) | ||
375 | { | ||
376 | if (num != 2) | ||
377 | { | ||
378 | _dataError = true; | ||
379 | return S_FALSE; | ||
380 | } | ||
381 | UInt32 d = GetBe16(temp + 3); | ||
382 | globalOffset = d << (type == kType_Seg ? 4 : 16); | ||
383 | } | ||
384 | else | ||
385 | { | ||
386 | if (num != 4) | ||
387 | { | ||
388 | _dataError = true; | ||
389 | return S_FALSE; | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | for (;;) | ||
395 | { | ||
396 | Byte b; | ||
397 | if (!s.ReadByte(b)) | ||
398 | { | ||
399 | _needMoreInput = true; | ||
400 | return S_FALSE; | ||
401 | } | ||
402 | if (IS_LINE_DELIMITER(b)) | ||
403 | continue; | ||
404 | if (b == ':') | ||
405 | break; | ||
406 | _dataError = true; | ||
407 | return S_FALSE; | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | catch(const CInBufferException &e) { return e.ErrorCode; } | ||
412 | } | ||
413 | COM_TRY_END | ||
414 | } | ||
415 | |||
416 | STDMETHODIMP CHandler::Close() | ||
417 | { | ||
418 | _phySize = 0; | ||
419 | |||
420 | _isArc = false; | ||
421 | _needMoreInput = false; | ||
422 | _dataError = false; | ||
423 | |||
424 | _blocks.Clear(); | ||
425 | return S_OK; | ||
426 | } | ||
427 | |||
428 | |||
429 | STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, | ||
430 | Int32 testMode, IArchiveExtractCallback *extractCallback) | ||
431 | { | ||
432 | COM_TRY_BEGIN | ||
433 | bool allFilesMode = (numItems == (UInt32)(Int32)-1); | ||
434 | if (allFilesMode) | ||
435 | numItems = _blocks.Size(); | ||
436 | if (numItems == 0) | ||
437 | return S_OK; | ||
438 | |||
439 | UInt64 totalSize = 0; | ||
440 | UInt32 i; | ||
441 | for (i = 0; i < numItems; i++) | ||
442 | totalSize += _blocks[allFilesMode ? i : indices[i]].Data.GetPos(); | ||
443 | extractCallback->SetTotal(totalSize); | ||
444 | |||
445 | UInt64 currentTotalSize = 0; | ||
446 | UInt64 currentItemSize; | ||
447 | |||
448 | CLocalProgress *lps = new CLocalProgress; | ||
449 | CMyComPtr<ICompressProgressInfo> progress = lps; | ||
450 | lps->Init(extractCallback, false); | ||
451 | |||
452 | for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) | ||
453 | { | ||
454 | currentItemSize = 0; | ||
455 | lps->InSize = lps->OutSize = currentTotalSize; | ||
456 | RINOK(lps->SetCur()); | ||
457 | |||
458 | UInt32 index = allFilesMode ? i : indices[i]; | ||
459 | const CByteDynamicBuffer &data = _blocks[index].Data; | ||
460 | currentItemSize = data.GetPos(); | ||
461 | |||
462 | CMyComPtr<ISequentialOutStream> realOutStream; | ||
463 | Int32 askMode = testMode ? | ||
464 | NExtract::NAskMode::kTest : | ||
465 | NExtract::NAskMode::kExtract; | ||
466 | |||
467 | RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); | ||
468 | |||
469 | if (!testMode && !realOutStream) | ||
470 | continue; | ||
471 | |||
472 | extractCallback->PrepareOperation(askMode); | ||
473 | |||
474 | if (realOutStream) | ||
475 | { | ||
476 | RINOK(WriteStream(realOutStream, (const Byte *)data, data.GetPos())); | ||
477 | } | ||
478 | |||
479 | realOutStream.Release(); | ||
480 | RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); | ||
481 | } | ||
482 | |||
483 | lps->InSize = lps->OutSize = currentTotalSize; | ||
484 | return lps->SetCur(); | ||
485 | |||
486 | COM_TRY_END | ||
487 | } | ||
488 | |||
489 | // k_Signature: { ':', '1' } | ||
490 | |||
491 | REGISTER_ARC_I_NO_SIG( | ||
492 | "IHex", "ihex", 0, 0xCD, | ||
493 | 0, | ||
494 | NArcInfoFlags::kStartOpen, | ||
495 | IsArc_Ihex) | ||
496 | |||
497 | }} | ||