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
GetNumberOfItems(UInt32 * numItems)56 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
57 {
58 *numItems = _blocks.Size();
59 return S_OK;
60 }
61
GetArchiveProperty(PROPID propID,PROPVARIANT * value)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
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)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
HexToByte(unsigned c)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
Parse(const Byte * p)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
IsArc_Ihex(const Byte * p,size_t size)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
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)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
Close()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
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)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 }}
498