p7zip/CPP/7zip/Archive/IhexHandler.cpp
2017-10-11 12:35:36 +02:00

498 lines
10 KiB
C++

// 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 = (UInt64)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(unsigned 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 unsigned kNumLinesToCheck = 3; // 1 line is OK also, but we don't want false detection
for (unsigned 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 = ((unsigned)num + 4);
size_t numBytes = numPairs * 2;
if (s.ReadBytes(temp, numBytes) != numBytes)
{
_needMoreInput = true;
return S_FALSE;
}
unsigned 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;
}
block->Data.AddData(temp + 3, (unsigned)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
}
// k_Signature: { ':', '1' }
REGISTER_ARC_I_NO_SIG(
"IHex", "ihex", 0, 0xCD,
0,
NArcInfoFlags::kStartOpen,
IsArc_Ihex)
}}