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