// CabBlockInStream.cpp #include "StdAfx.h" #include "../../../../C/Alloc.h" #include "Common/Defs.h" #include "../../Common/StreamUtils.h" #include "CabBlockInStream.h" namespace NArchive { namespace NCab { static const UInt32 kBlockSize = (1 << 16); bool CCabBlockInStream::Create() { if (!_buffer) _buffer = (Byte *)::MyAlloc(kBlockSize); return (_buffer != 0); } CCabBlockInStream::~CCabBlockInStream() { MyFree(_buffer); } class CCheckSum2 { UInt32 m_Value; int m_Pos; Byte m_Hist[4]; public: CCheckSum2(): m_Value(0){}; void Init() { m_Value = 0; m_Pos = 0; } void Update(const void *data, UInt32 size); void FinishDataUpdate() { for (int i = 0; i < m_Pos; i++) m_Value ^= ((UInt32)(m_Hist[i])) << (8 * (m_Pos - i - 1)); } void UpdateUInt32(UInt32 v) { m_Value ^= v; } UInt32 GetResult() const { return m_Value; } }; void CCheckSum2::Update(const void *data, UInt32 size) { UInt32 checkSum = m_Value; const Byte *dataPointer = (const Byte *)data; while (size != 0 && m_Pos != 0) { m_Hist[m_Pos] = *dataPointer++; m_Pos = (m_Pos + 1) & 3; size--; if (m_Pos == 0) for (int i = 0; i < 4; i++) checkSum ^= ((UInt32)m_Hist[i]) << (8 * i); } int numWords = size / 4; while (numWords-- != 0) { UInt32 temp = *dataPointer++; temp |= ((UInt32)(*dataPointer++)) << 8; temp |= ((UInt32)(*dataPointer++)) << 16; temp |= ((UInt32)(*dataPointer++)) << 24; checkSum ^= temp; } m_Value = checkSum; size &= 3; while (size != 0) { m_Hist[m_Pos] = *dataPointer++; m_Pos = (m_Pos + 1) & 3; size--; } } static const UInt32 kDataBlockHeaderSize = 8; class CTempCabInBuffer2 { public: Byte Buffer[kDataBlockHeaderSize]; UInt32 Pos; Byte ReadByte() { return Buffer[Pos++]; } UInt32 ReadUInt32() { UInt32 value = 0; for (int i = 0; i < 4; i++) value |= (((UInt32)ReadByte()) << (8 * i)); return value; } UInt16 ReadUInt16() { UInt16 value = 0; for (int i = 0; i < 2; i++) value |= (((UInt16)ReadByte()) << (8 * i)); return value; } }; HRESULT CCabBlockInStream::PreRead(UInt32 &packSize, UInt32 &unpackSize) { CTempCabInBuffer2 inBuffer; inBuffer.Pos = 0; RINOK(ReadStream_FALSE(_stream, inBuffer.Buffer, kDataBlockHeaderSize)) UInt32 checkSum = inBuffer.ReadUInt32(); packSize = inBuffer.ReadUInt16(); unpackSize = inBuffer.ReadUInt16(); if (ReservedSize != 0) { RINOK(ReadStream_FALSE(_stream, _buffer, ReservedSize)); } _pos = 0; CCheckSum2 checkSumCalc; checkSumCalc.Init(); UInt32 packSize2 = packSize; if (MsZip && _size == 0) { if (packSize < 2) return S_FALSE; // bad block; Byte sig[2]; RINOK(ReadStream_FALSE(_stream, sig, 2)); if (sig[0] != 0x43 || sig[1] != 0x4B) return S_FALSE; packSize2 -= 2; checkSumCalc.Update(sig, 2); } if (kBlockSize - _size < packSize2) return S_FALSE; UInt32 curSize = packSize2; if (curSize != 0) { size_t processedSizeLoc = curSize; RINOK(ReadStream(_stream, _buffer + _size, &processedSizeLoc)); checkSumCalc.Update(_buffer + _size, (UInt32)processedSizeLoc); _size += (UInt32)processedSizeLoc; if (processedSizeLoc != curSize) return S_FALSE; } TotalPackSize = _size; checkSumCalc.FinishDataUpdate(); bool dataError; if (checkSum == 0) dataError = false; else { checkSumCalc.UpdateUInt32(packSize | (((UInt32)unpackSize) << 16)); dataError = (checkSumCalc.GetResult() != checkSum); } DataError |= dataError; return dataError ? S_FALSE : S_OK; } STDMETHODIMP CCabBlockInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != 0) *processedSize = 0; if (size == 0) return S_OK; if (_size != 0) { size = MyMin(_size, size); memmove(data, _buffer + _pos, size); _pos += size; _size -= size; if (processedSize != 0) *processedSize = size; return S_OK; } return S_OK; // no blocks data } }}