diff options
Diffstat (limited to 'CPP/7zip/Archive/IhexHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/IhexHandler.cpp | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/IhexHandler.cpp b/CPP/7zip/Archive/IhexHandler.cpp new file mode 100644 index 00000000..bc468401 --- /dev/null +++ b/CPP/7zip/Archive/IhexHandler.cpp @@ -0,0 +1,500 @@ +// IhexHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "../../Common/ComTry.h" +#include "../../Common/DynamicBuffer.h" +#include "../../Common/IntToString.h" +#include "../../Common/MyVector.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" +#include "../Common/InBuffer.h" + +namespace NArchive { +namespace NIhex { + +/* We still don't support files with custom record types: 20, 22: used by Samsung */ + +struct CBlock +{ + CByteDynamicBuffer Data; + UInt32 Offset; +}; + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + bool _isArc; + bool _needMoreInput; + bool _dataError; + + UInt64 _phySize; + + CObjectVector<CBlock> _blocks; +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +static const Byte kProps[] = +{ + kpidPath, + kpidSize, + kpidVa +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _blocks.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch (propID) + { + case kpidPhySize: if (_phySize != 0) prop = _phySize; break; + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; + if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; + if (_dataError) v |= kpv_ErrorFlags_DataError; + prop = v; + } + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CBlock &block = _blocks[index]; + switch (propID) + { + case kpidSize: prop = block.Data.GetPos(); break; + case kpidVa: prop = block.Offset; break; + case kpidPath: + { + if (_blocks.Size() != 1) + { + char s[16]; + ConvertUInt32ToString(index, s); + prop = s; + } + break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +static inline int HexToByte(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + +static int Parse(const Byte *p) +{ + int c1 = HexToByte(p[0]); if (c1 < 0) return -1; + int c2 = HexToByte(p[1]); if (c2 < 0) return -1; + return (c1 << 4) | c2; +} + +#define kType_Data 0 +#define kType_Eof 1 +#define kType_Seg 2 +#define kType_CsIp 3 +#define kType_High 4 +#define kType_Ip32 5 + +#define kType_MAX 5 + +#define IS_LINE_DELIMITER(c) ((c) == 0 || (c) == 10 || (c) == 13) + +API_FUNC_static_IsArc IsArc_Ihex(const Byte *p, size_t size) +{ + if (size < 1) + return k_IsArc_Res_NEED_MORE; + if (p[0] != ':') + return k_IsArc_Res_NO; + p++; + size--; + + const int kNumLinesToCheck = 3; // 1 line is OK also, but we don't want false detection + + for (int j = 0; j < kNumLinesToCheck; j++) + { + if (size < 4 * 2) + return k_IsArc_Res_NEED_MORE; + + int num = Parse(p); + if (num < 0) + return k_IsArc_Res_NO; + + int type = Parse(p + 6); + if (type < 0 || type > kType_MAX) + return k_IsArc_Res_NO; + + unsigned numChars = ((unsigned)num + 5) * 2; + unsigned sum = 0; + + for (unsigned i = 0; i < numChars; i += 2) + { + if (i + 2 > size) + return k_IsArc_Res_NEED_MORE; + int v = Parse(p + i); + if (v < 0) + return k_IsArc_Res_NO; + sum += (unsigned)v; + } + + if ((sum & 0xFF) != 0) + return k_IsArc_Res_NO; + + if (type == kType_Data) + { + // we don't want to open :0000000000 files + if (num == 0) + return k_IsArc_Res_NO; + } + else + { + if (type == kType_Eof) + { + if (num != 0) + return k_IsArc_Res_NO; + return k_IsArc_Res_YES; + } + if (p[2] != 0 || + p[3] != 0 || + p[4] != 0 || + p[5] != 0) + return k_IsArc_Res_NO; + if (type == kType_Seg || type == kType_High) + { + if (num != 2) + return k_IsArc_Res_NO; + } + else + { + if (num != 4) + return k_IsArc_Res_NO; + } + } + + p += numChars; + size -= numChars; + + for (;;) + { + if (size == 0) + return k_IsArc_Res_NEED_MORE; + char b = *p++; + size--; + if (IS_LINE_DELIMITER(b)) + continue; + if (b == ':') + break; + return k_IsArc_Res_NO; + } + } + + return k_IsArc_Res_YES; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) +{ + COM_TRY_BEGIN + { + Close(); + try + { + const unsigned kStartSize = (2 + (256 + 5) + 2) * 2; + Byte temp[kStartSize]; + { + size_t size = kStartSize; + RINOK(ReadStream(stream, temp, &size)); + UInt32 isArcRes = IsArc_Ihex(temp, size); + if (isArcRes == k_IsArc_Res_NO) + return S_FALSE; + if (isArcRes == k_IsArc_Res_NEED_MORE && size != kStartSize) + return S_FALSE; + } + _isArc = true; + + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + CInBuffer s; + if (!s.Create(1 << 15)) + return E_OUTOFMEMORY; + s.SetStream(stream); + s.Init(); + + { + Byte b; + if (!s.ReadByte(b)) + { + _needMoreInput = true; + return S_FALSE; + } + if (b != ':') + { + _dataError = true; + return S_FALSE; + } + } + + UInt32 globalOffset = 0; + + for (;;) + { + if (s.ReadBytes(temp, 2) != 2) + { + _needMoreInput = true; + return S_FALSE; + } + int num = Parse(temp); + if (num < 0) + { + _dataError = true; + return S_FALSE; + } + + { + size_t numPairs = (num + 4); + size_t numBytes = numPairs * 2; + if (s.ReadBytes(temp, numBytes) != numBytes) + { + _needMoreInput = true; + return S_FALSE; + } + + int sum = num; + for (size_t i = 0; i < numPairs; i++) + { + int a = Parse(temp + i * 2); + if (a < 0) + { + _dataError = true; + return S_FALSE; + } + temp[i] = (Byte)a; + sum += a; + } + if ((sum & 0xFF) != 0) + { + _dataError = true; + return S_FALSE; + } + } + + unsigned type = temp[2]; + if (type > kType_MAX) + { + _dataError = true; + return S_FALSE; + } + + UInt32 a = GetBe16(temp); + + if (type == kType_Data) + { + if (num == 0) + { + // we don't want to open :0000000000 files + // maybe it can mean EOF in old-style files? + _dataError = true; + return S_FALSE; + } + // if (num != 0) + { + UInt32 offs = globalOffset + a; + CBlock *block = NULL; + if (!_blocks.IsEmpty()) + { + block = &_blocks.Back(); + if (block->Offset + block->Data.GetPos() != offs) + block = NULL; + } + if (!block) + { + block = &_blocks.AddNew(); + block->Offset = offs; + } + memcpy(block->Data.GetCurPtrAndGrow(num), temp + 3, num); + } + } + else if (type == kType_Eof) + { + _phySize = s.GetProcessedSize(); + { + Byte b; + if (s.ReadByte(b)) + { + if (b == 10) + _phySize++; + else if (b == 13) + { + _phySize++; + if (s.ReadByte(b)) + { + if (b == 10) + _phySize++; + } + } + } + } + return S_OK; + } + else + { + if (a != 0) + { + _dataError = true; + return S_FALSE; + } + if (type == kType_Seg || type == kType_High) + { + if (num != 2) + { + _dataError = true; + return S_FALSE; + } + UInt32 d = GetBe16(temp + 3); + globalOffset = d << (type == kType_Seg ? 4 : 16); + } + else + { + if (num != 4) + { + _dataError = true; + return S_FALSE; + } + } + } + + for (;;) + { + Byte b; + if (!s.ReadByte(b)) + { + _needMoreInput = true; + return S_FALSE; + } + if (IS_LINE_DELIMITER(b)) + continue; + if (b == ':') + break; + _dataError = true; + return S_FALSE; + } + } + } + catch(const CInBufferException &e) { return e.ErrorCode; } + } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _phySize = 0; + + _isArc = false; + _needMoreInput = false; + _dataError = false; + + _blocks.Clear(); + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)(Int32)-1); + if (allFilesMode) + numItems = _blocks.Size(); + if (numItems == 0) + return S_OK; + + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _blocks[allFilesMode ? i : indices[i]].Data.GetPos(); + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + currentItemSize = 0; + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + + UInt32 index = allFilesMode ? i : indices[i]; + const CByteDynamicBuffer &data = _blocks[index].Data; + currentItemSize = data.GetPos(); + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (!testMode && !realOutStream) + continue; + + extractCallback->PrepareOperation(askMode); + + if (realOutStream) + { + RINOK(WriteStream(realOutStream, (const Byte *)data, data.GetPos())); + } + + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + + lps->InSize = lps->OutSize = currentTotalSize; + return lps->SetCur(); + + COM_TRY_END +} + +IMP_CreateArcIn + +static CArcInfo g_ArcInfo = + { "IHex", "ihex", 0, 0xCD, + 0, { 0 }, + // 2, { ':', '1' }, + 0, + NArcInfoFlags::kStartOpen, + CreateArc, NULL, IsArc_Ihex }; + +REGISTER_ARC(Z) + +}} |