498 lines
10 KiB
C++
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)
|
|
|
|
}}
|