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