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