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