// Archive/arj/InEngine.cpp #include "StdAfx.h" #include "Common/StringConvert.h" #include "Common/Buffer.h" #include "../../Common/StreamUtils.h" #include "ArjIn.h" extern "C" { #include "../../../../C/7zCrc.h" } namespace NArchive { namespace NArj { HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize; HRESULT result = ReadStream(_stream, data, size, &realProcessedSize); if(processedSize != NULL) *processedSize = realProcessedSize; IncreasePositionValue(realProcessedSize); return result; } static inline UInt16 GetUInt16FromMemLE(const Byte *p) { return (UInt16)(p[0] | (((UInt16)p[1]) << 8)); } static inline UInt32 GetUInt32FromMemLE(const Byte *p) { return p[0] | (((UInt32)p[1]) << 8) | (((UInt32)p[2]) << 16) | (((UInt32)p[3]) << 24); } inline bool TestMarkerCandidate(const void *testBytes, UInt32 maxSize) { if (maxSize < 2 + 2 + 4) return false; const Byte *block = ((const Byte *)(testBytes)); if (block[0] != NSignature::kSig0 || block[1] != NSignature::kSig1) return false; UInt32 blockSize = GetUInt16FromMemLE(block + 2); if (maxSize < 2 + 2 + blockSize + 4) return false; block += 4; if (blockSize == 0 || blockSize > 2600) return false; UInt32 crcFromFile = GetUInt32FromMemLE(block + blockSize); return (crcFromFile == CrcCalc(block, blockSize)); } bool CInArchive::FindAndReadMarker(const UInt64 *searchHeaderSizeLimit) { // _archiveInfo.StartPosition = 0; _position = _streamStartPosition; if(_stream->Seek(_streamStartPosition, STREAM_SEEK_SET, NULL) != S_OK) return false; const int kMarkerSizeMax = 2 + 2 + kMaxBlockSize + 4; CByteBuffer byteBuffer; static const UInt32 kSearchMarkerBufferSize = 0x10000; byteBuffer.SetCapacity(kSearchMarkerBufferSize); Byte *buffer = byteBuffer; UInt32 processedSize; ReadBytes(buffer, kMarkerSizeMax, &processedSize); if (processedSize == 0) return false; if (TestMarkerCandidate(buffer, processedSize)) { _position = _streamStartPosition; if(_stream->Seek(_position, STREAM_SEEK_SET, NULL) != S_OK) return false; return true; } UInt32 numBytesPrev = processedSize - 1; memmove(buffer, buffer + 1, numBytesPrev); UInt64 curTestPos = _streamStartPosition + 1; for (;;) { if (searchHeaderSizeLimit != NULL) if (curTestPos - _streamStartPosition > *searchHeaderSizeLimit) return false; UInt32 numReadBytes = kSearchMarkerBufferSize - numBytesPrev; ReadBytes(buffer + numBytesPrev, numReadBytes, &processedSize); UInt32 numBytesInBuffer = numBytesPrev + processedSize; if (numBytesInBuffer < 1) return false; UInt32 numTests = numBytesInBuffer; for(UInt32 pos = 0; pos < numTests; pos++, curTestPos++) { if (TestMarkerCandidate(buffer + pos, numBytesInBuffer - pos)) { // _archiveInfo.StartPosition = curTestPos; _position = curTestPos; if(_stream->Seek(_position, STREAM_SEEK_SET, NULL) != S_OK) return false; return true; } } numBytesPrev = numBytesInBuffer - numTests; memmove(buffer, buffer + numTests, numBytesPrev); } } void CInArchive::IncreasePositionValue(UInt64 addValue) { _position += addValue; } void CInArchive::IncreaseRealPosition(UInt64 addValue) { if(_stream->Seek(addValue, STREAM_SEEK_CUR, &_position) != S_OK) throw CInArchiveException(CInArchiveException::kSeekStreamError); } bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size) { UInt32 realProcessedSize; if(ReadBytes(data, size, &realProcessedSize) != S_OK) throw CInArchiveException(CInArchiveException::kReadStreamError); return (realProcessedSize == size); } void CInArchive::SafeReadBytes(void *data, UInt32 size) { if(!ReadBytesAndTestSize(data, size)) throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); } Byte CInArchive::SafeReadByte() { Byte b; SafeReadBytes(&b, 1); return b; } UInt16 CInArchive::SafeReadUInt16() { UInt16 value = 0; for (int i = 0; i < 2; i++) { Byte b = SafeReadByte(); value |= (UInt16(b) << (8 * i)); } return value; } UInt32 CInArchive::SafeReadUInt32() { UInt32 value = 0; for (int i = 0; i < 4; i++) { Byte b = SafeReadByte(); value |= (UInt32(b) << (8 * i)); } return value; } bool CInArchive::ReadBlock() { _blockPos = 0; _blockSize = SafeReadUInt16(); if (_blockSize == 0 || _blockSize > kMaxBlockSize) return false; SafeReadBytes(_block, _blockSize); UInt32 crcFromFile = SafeReadUInt32(); if (crcFromFile != CrcCalc(_block, _blockSize)) throw CInArchiveException(CInArchiveException::kCRCError); return true; } bool CInArchive::ReadBlock2() { Byte id[2]; ReadBytesAndTestSize(id, 2); if (id[0] != NSignature::kSig0 || id[1] != NSignature::kSig1) throw CInArchiveException(CInArchiveException::kIncorrectArchive); return ReadBlock(); } bool CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit) { _stream = inStream; if(_stream->Seek(0, STREAM_SEEK_CUR, &_streamStartPosition) != S_OK) return false; _position = _streamStartPosition; if (!FindAndReadMarker(searchHeaderSizeLimit)) return false; if (!ReadBlock2()) return false; for (;;) if (!ReadBlock()) break; return true; } void CInArchive::Close() { _stream.Release(); } void CInArchive::ThrowIncorrectArchiveException() { throw CInArchiveException(CInArchiveException::kIncorrectArchive); } Byte CInArchive::ReadByte() { if (_blockPos >= _blockSize) ThrowIncorrectArchiveException(); return _block[_blockPos++]; } UInt16 CInArchive::ReadUInt16() { UInt16 value = 0; for (int i = 0; i < 2; i++) { Byte b = ReadByte(); value |= (UInt16(b) << (8 * i)); } return value; } UInt32 CInArchive::ReadUInt32() { UInt32 value = 0; for (int i = 0; i < 4; i++) { Byte b = ReadByte(); value |= (UInt32(b) << (8 * i)); } return value; } HRESULT CInArchive::GetNextItem(bool &filled, CItemEx &item) { filled = false; if (!ReadBlock2()) return S_OK; Byte firstHeaderSize = ReadByte(); item.Version = ReadByte(); item.ExtractVersion = ReadByte(); item.HostOS = ReadByte(); item.Flags = ReadByte(); item.Method = ReadByte(); item.FileType = ReadByte(); ReadByte(); // Reserved item.ModifiedTime = ReadUInt32(); item.PackSize = ReadUInt32(); item.Size = ReadUInt32(); item.FileCRC = ReadUInt32(); ReadUInt16(); // FilespecPositionInFilename item.FileAccessMode = ReadUInt16(); ReadByte(); // FirstChapter ReadByte(); // LastChapter /* UInt32 extraData; if ((header.Flags & NFileHeader::NFlags::kExtFile) != 0) extraData = GetUInt32FromMemLE(_block + pos); */ _blockPos = firstHeaderSize; for (; _blockPos < _blockSize;) item.Name += (char)ReadByte(); for (;;) if (!ReadBlock()) break; item.DataPosition = _position; filled = true; return S_OK; } }}