1 #include "IszImageStream.h"
2 #include <stdexcept>
3 #include <algorithm>
4 #include <string.h>
5 #include <assert.h>
6 #include "bzlib.h"
7 #include "zlib.h"
8 #include "StdStream.h"
9 
CIszImageStream(CStream * baseStream)10 CIszImageStream::CIszImageStream(CStream* baseStream)
11     : m_baseStream(baseStream)
12 {
13 	if(baseStream == nullptr)
14 	{
15 		throw std::runtime_error("Null base stream supplied.");
16 	}
17 
18 	baseStream->Read(&m_header, sizeof(HEADER));
19 
20 	assert(m_header.hasPassword == 0);
21 	assert(m_header.segmentPtrOffset == 0);
22 
23 	if(m_header.blockPtrOffset == 0)
24 	{
25 		throw std::runtime_error("Block Descriptor Table not present.");
26 	}
27 	if(m_header.blockPtrLength != 3)
28 	{
29 		throw std::runtime_error("Unsupported block descriptor size.");
30 	}
31 
32 	ReadBlockDescriptorTable();
33 	m_cachedBlock = new uint8[m_header.blockSize];
34 	m_readBuffer = new uint8[m_header.blockSize];
35 }
36 
~CIszImageStream()37 CIszImageStream::~CIszImageStream()
38 {
39 	delete[] m_cachedBlock;
40 	delete[] m_readBuffer;
41 	delete[] m_blockDescriptorTable;
42 	delete m_baseStream;
43 }
44 
Seek(int64 position,Framework::STREAM_SEEK_DIRECTION origin)45 void CIszImageStream::Seek(int64 position, Framework::STREAM_SEEK_DIRECTION origin)
46 {
47 	switch(origin)
48 	{
49 	case Framework::STREAM_SEEK_CUR:
50 		m_position += position;
51 		break;
52 	case Framework::STREAM_SEEK_SET:
53 		m_position = position;
54 		break;
55 	case Framework::STREAM_SEEK_END:
56 		m_position = GetTotalSize();
57 		break;
58 	}
59 }
60 
Tell()61 uint64 CIszImageStream::Tell()
62 {
63 	return m_position;
64 }
65 
Read(void * buffer,uint64 size)66 uint64 CIszImageStream::Read(void* buffer, uint64 size)
67 {
68 	uint64 bytesRead = 0;
69 	uint8* inputBuffer = reinterpret_cast<uint8*>(buffer);
70 	while(size != 0)
71 	{
72 		if(IsEOF())
73 		{
74 			break;
75 		}
76 		SyncCache();
77 		uint64 blockPosition = (m_position % m_header.blockSize);
78 		uint64 sizeLeft = m_header.blockSize - blockPosition;
79 		uint64 sizeToRead = std::min<uint64>(size, sizeLeft);
80 		memcpy(inputBuffer, m_cachedBlock + blockPosition, static_cast<size_t>(sizeToRead));
81 		m_position += sizeToRead;
82 		size -= sizeToRead;
83 		inputBuffer += sizeToRead;
84 		bytesRead += sizeToRead;
85 	}
86 	return bytesRead;
87 }
88 
Write(const void * buffer,uint64 size)89 uint64 CIszImageStream::Write(const void* buffer, uint64 size)
90 {
91 	throw std::exception();
92 }
93 
IsEOF()94 bool CIszImageStream::IsEOF()
95 {
96 	return (m_position >= GetTotalSize());
97 }
98 
ReadBlockDescriptorTable()99 void CIszImageStream::ReadBlockDescriptorTable()
100 {
101 	const char* key = "IsZ!";
102 	unsigned int cryptedTableLength = m_header.blockNumber * m_header.blockPtrLength;
103 	uint8* cryptedTable = new uint8[cryptedTableLength];
104 	m_baseStream->Seek(m_header.blockPtrOffset, Framework::STREAM_SEEK_SET);
105 	m_baseStream->Read(cryptedTable, cryptedTableLength);
106 	for(unsigned int i = 0; i < cryptedTableLength; i++)
107 	{
108 		cryptedTable[i] ^= ~key[i & 3];
109 	}
110 
111 	m_blockDescriptorTable = new BLOCKDESCRIPTOR[m_header.blockNumber];
112 	for(unsigned int i = 0; i < m_header.blockNumber; i++)
113 	{
114 		uint32 value = *reinterpret_cast<uint32*>(&cryptedTable[i * m_header.blockPtrLength]);
115 		value &= 0xFFFFFF;
116 		m_blockDescriptorTable[i].size = value & 0x3FFFFF;
117 		m_blockDescriptorTable[i].storageType = static_cast<uint8>(value >> 22);
118 	}
119 
120 	delete[] cryptedTable;
121 }
122 
GetTotalSize() const123 uint64 CIszImageStream::GetTotalSize() const
124 {
125 	return static_cast<uint64>(m_header.totalSectors) * static_cast<uint64>(m_header.sectorSize);
126 }
127 
SeekToBlock(uint64 blockNumber)128 const CIszImageStream::BLOCKDESCRIPTOR& CIszImageStream::SeekToBlock(uint64 blockNumber)
129 {
130 	assert(blockNumber < m_header.blockNumber);
131 	uint64 seekPosition = m_header.dataOffset;
132 	for(unsigned int i = 0; i < blockNumber; i++)
133 	{
134 		const BLOCKDESCRIPTOR& blockDescriptor = m_blockDescriptorTable[i];
135 		if(blockDescriptor.storageType != ADI_ZERO)
136 		{
137 			seekPosition += blockDescriptor.size;
138 		}
139 	}
140 	m_baseStream->Seek(seekPosition, Framework::STREAM_SEEK_SET);
141 	return m_blockDescriptorTable[blockNumber];
142 }
143 
SyncCache()144 void CIszImageStream::SyncCache()
145 {
146 	uint64 currentSector = (m_position / m_header.sectorSize);
147 	uint64 neededBlock = (currentSector * m_header.sectorSize) / m_header.blockSize;
148 	if(neededBlock == m_cachedBlockNumber)
149 	{
150 		return;
151 	}
152 	if(neededBlock >= m_header.blockNumber)
153 	{
154 		throw std::runtime_error("Trying to read past eof.");
155 	}
156 
157 	const BLOCKDESCRIPTOR& blockDescriptor = SeekToBlock(neededBlock);
158 	memset(m_cachedBlock, 0, m_header.blockSize);
159 	switch(blockDescriptor.storageType)
160 	{
161 	case ADI_ZERO:
162 		ReadZeroBlock(blockDescriptor.size);
163 		break;
164 	case ADI_DATA:
165 		ReadDataBlock(blockDescriptor.size);
166 		break;
167 	case ADI_ZLIB:
168 		ReadGzipBlock(blockDescriptor.size);
169 		break;
170 	case ADI_BZ2:
171 		ReadBz2Block(blockDescriptor.size);
172 		break;
173 	default:
174 		throw std::runtime_error("Unsupported block storage mode.");
175 		break;
176 	}
177 	m_cachedBlockNumber = neededBlock;
178 }
179 
ReadZeroBlock(uint32 compressedBlockSize)180 void CIszImageStream::ReadZeroBlock(uint32 compressedBlockSize)
181 {
182 	if(compressedBlockSize != m_header.blockSize)
183 	{
184 		throw std::runtime_error("Invalid zero block.");
185 	}
186 }
187 
ReadDataBlock(uint32 compressedBlockSize)188 void CIszImageStream::ReadDataBlock(uint32 compressedBlockSize)
189 {
190 	if(compressedBlockSize != m_header.blockSize)
191 	{
192 		throw std::runtime_error("Invalid data block.");
193 	}
194 	m_baseStream->Read(m_cachedBlock, compressedBlockSize);
195 }
196 
ReadGzipBlock(uint32 compressedBlockSize)197 void CIszImageStream::ReadGzipBlock(uint32 compressedBlockSize)
198 {
199 	m_baseStream->Read(m_readBuffer, compressedBlockSize);
200 	uLongf destLength = m_header.blockSize;
201 	if(uncompress(
202 	       reinterpret_cast<Bytef*>(m_cachedBlock), &destLength,
203 	       reinterpret_cast<Bytef*>(m_readBuffer), compressedBlockSize) != Z_OK)
204 	{
205 		throw std::runtime_error("Error decompressing zlib block.");
206 	}
207 }
208 
ReadBz2Block(uint32 compressedBlockSize)209 void CIszImageStream::ReadBz2Block(uint32 compressedBlockSize)
210 {
211 	m_baseStream->Read(m_readBuffer, compressedBlockSize);
212 	//Force BZ2 header
213 	m_readBuffer[0] = 'B';
214 	m_readBuffer[1] = 'Z';
215 	m_readBuffer[2] = 'h';
216 	unsigned int destLength = m_header.blockSize;
217 	if(BZ2_bzBuffToBuffDecompress(
218 	       reinterpret_cast<char*>(m_cachedBlock), &destLength,
219 	       reinterpret_cast<char*>(m_readBuffer), compressedBlockSize, 0, 0) != BZ_OK)
220 	{
221 		throw std::runtime_error("Error decompressing bz2 block.");
222 	}
223 }
224