xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/SparseHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1*f6dc9357SAndroid Build Coastguard Worker // SparseHandler.cpp
2*f6dc9357SAndroid Build Coastguard Worker 
3*f6dc9357SAndroid Build Coastguard Worker #include "StdAfx.h"
4*f6dc9357SAndroid Build Coastguard Worker 
5*f6dc9357SAndroid Build Coastguard Worker #include "../../../C/CpuArch.h"
6*f6dc9357SAndroid Build Coastguard Worker 
7*f6dc9357SAndroid Build Coastguard Worker #include "../../Common/ComTry.h"
8*f6dc9357SAndroid Build Coastguard Worker 
9*f6dc9357SAndroid Build Coastguard Worker #include "../../Windows/PropVariantUtils.h"
10*f6dc9357SAndroid Build Coastguard Worker 
11*f6dc9357SAndroid Build Coastguard Worker #include "../Common/RegisterArc.h"
12*f6dc9357SAndroid Build Coastguard Worker #include "../Common/StreamUtils.h"
13*f6dc9357SAndroid Build Coastguard Worker 
14*f6dc9357SAndroid Build Coastguard Worker #include "HandlerCont.h"
15*f6dc9357SAndroid Build Coastguard Worker 
16*f6dc9357SAndroid Build Coastguard Worker #define Get16(p) GetUi16(p)
17*f6dc9357SAndroid Build Coastguard Worker #define Get32(p) GetUi32(p)
18*f6dc9357SAndroid Build Coastguard Worker 
19*f6dc9357SAndroid Build Coastguard Worker #define G16(_offs_, dest) dest = Get16(p + (_offs_));
20*f6dc9357SAndroid Build Coastguard Worker #define G32(_offs_, dest) dest = Get32(p + (_offs_));
21*f6dc9357SAndroid Build Coastguard Worker 
22*f6dc9357SAndroid Build Coastguard Worker using namespace NWindows;
23*f6dc9357SAndroid Build Coastguard Worker 
24*f6dc9357SAndroid Build Coastguard Worker namespace NArchive {
25*f6dc9357SAndroid Build Coastguard Worker namespace NSparse {
26*f6dc9357SAndroid Build Coastguard Worker 
27*f6dc9357SAndroid Build Coastguard Worker // libsparse and simg2img
28*f6dc9357SAndroid Build Coastguard Worker 
29*f6dc9357SAndroid Build Coastguard Worker struct CHeader
30*f6dc9357SAndroid Build Coastguard Worker {
31*f6dc9357SAndroid Build Coastguard Worker   // UInt32 magic;          /* 0xed26ff3a */
32*f6dc9357SAndroid Build Coastguard Worker   // UInt16 major_version;  /* (0x1) - reject images with higher major versions */
33*f6dc9357SAndroid Build Coastguard Worker   // UInt16 minor_version;  /* (0x0) - allow images with higer minor versions */
34*f6dc9357SAndroid Build Coastguard Worker   UInt16 file_hdr_sz;    /* 28 bytes for first revision of the file format */
35*f6dc9357SAndroid Build Coastguard Worker   UInt16 chunk_hdr_sz;   /* 12 bytes for first revision of the file format */
36*f6dc9357SAndroid Build Coastguard Worker   UInt32 BlockSize;      /* block size in bytes, must be a multiple of 4 (4096) */
37*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumBlocks;      /* total blocks in the non-sparse output image */
38*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumChunks;      /* total chunks in the sparse input image */
39*f6dc9357SAndroid Build Coastguard Worker   // UInt32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" as 0. */
40*f6dc9357SAndroid Build Coastguard Worker 
ParseNArchive::NSparse::CHeader41*f6dc9357SAndroid Build Coastguard Worker   void Parse(const Byte *p)
42*f6dc9357SAndroid Build Coastguard Worker   {
43*f6dc9357SAndroid Build Coastguard Worker     // G16 (4, major_version);
44*f6dc9357SAndroid Build Coastguard Worker     // G16 (6, minor_version);
45*f6dc9357SAndroid Build Coastguard Worker     G16 (8, file_hdr_sz)
46*f6dc9357SAndroid Build Coastguard Worker     G16 (10, chunk_hdr_sz)
47*f6dc9357SAndroid Build Coastguard Worker     G32 (12, BlockSize)
48*f6dc9357SAndroid Build Coastguard Worker     G32 (16, NumBlocks)
49*f6dc9357SAndroid Build Coastguard Worker     G32 (20, NumChunks)
50*f6dc9357SAndroid Build Coastguard Worker     // G32 (24, image_checksum);
51*f6dc9357SAndroid Build Coastguard Worker   }
52*f6dc9357SAndroid Build Coastguard Worker };
53*f6dc9357SAndroid Build Coastguard Worker 
54*f6dc9357SAndroid Build Coastguard Worker // #define SPARSE_HEADER_MAGIC 0xed26ff3a
55*f6dc9357SAndroid Build Coastguard Worker 
56*f6dc9357SAndroid Build Coastguard Worker #define CHUNK_TYPE_RAW        0xCAC1
57*f6dc9357SAndroid Build Coastguard Worker #define CHUNK_TYPE_FILL       0xCAC2
58*f6dc9357SAndroid Build Coastguard Worker #define CHUNK_TYPE_DONT_CARE  0xCAC3
59*f6dc9357SAndroid Build Coastguard Worker #define CHUNK_TYPE_CRC32      0xCAC4
60*f6dc9357SAndroid Build Coastguard Worker 
61*f6dc9357SAndroid Build Coastguard Worker #define MY_CHUNK_TYPE_FILL       0
62*f6dc9357SAndroid Build Coastguard Worker #define MY_CHUNK_TYPE_DONT_CARE  1
63*f6dc9357SAndroid Build Coastguard Worker #define MY_CHUNK_TYPE_RAW_START 2
64*f6dc9357SAndroid Build Coastguard Worker 
65*f6dc9357SAndroid Build Coastguard Worker static const char * const g_Methods[] =
66*f6dc9357SAndroid Build Coastguard Worker {
67*f6dc9357SAndroid Build Coastguard Worker     "RAW"
68*f6dc9357SAndroid Build Coastguard Worker   , "FILL"
69*f6dc9357SAndroid Build Coastguard Worker   , "SPARSE" // "DONT_CARE"
70*f6dc9357SAndroid Build Coastguard Worker   , "CRC32"
71*f6dc9357SAndroid Build Coastguard Worker };
72*f6dc9357SAndroid Build Coastguard Worker 
73*f6dc9357SAndroid Build Coastguard Worker static const unsigned kFillSize = 4;
74*f6dc9357SAndroid Build Coastguard Worker 
75*f6dc9357SAndroid Build Coastguard Worker struct CChunk
76*f6dc9357SAndroid Build Coastguard Worker {
77*f6dc9357SAndroid Build Coastguard Worker   UInt32 VirtBlock;
78*f6dc9357SAndroid Build Coastguard Worker   Byte Fill [kFillSize];
79*f6dc9357SAndroid Build Coastguard Worker   UInt64 PhyOffset;
80*f6dc9357SAndroid Build Coastguard Worker 
CChunkNArchive::NSparse::CChunk81*f6dc9357SAndroid Build Coastguard Worker   CChunk()
82*f6dc9357SAndroid Build Coastguard Worker   {
83*f6dc9357SAndroid Build Coastguard Worker     Fill[0] =
84*f6dc9357SAndroid Build Coastguard Worker     Fill[1] =
85*f6dc9357SAndroid Build Coastguard Worker     Fill[2] =
86*f6dc9357SAndroid Build Coastguard Worker     Fill[3] =
87*f6dc9357SAndroid Build Coastguard Worker       0;
88*f6dc9357SAndroid Build Coastguard Worker   }
89*f6dc9357SAndroid Build Coastguard Worker };
90*f6dc9357SAndroid Build Coastguard Worker 
91*f6dc9357SAndroid Build Coastguard Worker static const Byte k_Signature[] = { 0x3a, 0xff, 0x26, 0xed, 1, 0 };
92*f6dc9357SAndroid Build Coastguard Worker 
93*f6dc9357SAndroid Build Coastguard Worker 
94*f6dc9357SAndroid Build Coastguard Worker Z7_class_CHandler_final: public CHandlerImg
95*f6dc9357SAndroid Build Coastguard Worker {
96*f6dc9357SAndroid Build Coastguard Worker   Z7_IFACE_COM7_IMP(IInArchive_Img)
97*f6dc9357SAndroid Build Coastguard Worker 
98*f6dc9357SAndroid Build Coastguard Worker   Z7_IFACE_COM7_IMP(IInArchiveGetStream)
99*f6dc9357SAndroid Build Coastguard Worker   Z7_IFACE_COM7_IMP(ISequentialInStream)
100*f6dc9357SAndroid Build Coastguard Worker 
101*f6dc9357SAndroid Build Coastguard Worker   CRecordVector<CChunk> Chunks;
102*f6dc9357SAndroid Build Coastguard Worker   UInt64 _virtSize_fromChunks;
103*f6dc9357SAndroid Build Coastguard Worker   unsigned _blockSizeLog;
104*f6dc9357SAndroid Build Coastguard Worker   UInt32 _chunkIndexPrev;
105*f6dc9357SAndroid Build Coastguard Worker 
106*f6dc9357SAndroid Build Coastguard Worker   UInt64 _packSizeProcessed;
107*f6dc9357SAndroid Build Coastguard Worker   UInt64 _phySize;
108*f6dc9357SAndroid Build Coastguard Worker   UInt32 _methodFlags;
109*f6dc9357SAndroid Build Coastguard Worker   bool _isArc;
110*f6dc9357SAndroid Build Coastguard Worker   bool _headersError;
111*f6dc9357SAndroid Build Coastguard Worker   bool _unexpectedEnd;
112*f6dc9357SAndroid Build Coastguard Worker   // bool _unsupported;
113*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumChunks; // from header
114*f6dc9357SAndroid Build Coastguard Worker 
115*f6dc9357SAndroid Build Coastguard Worker   HRESULT Seek2(UInt64 offset)
116*f6dc9357SAndroid Build Coastguard Worker   {
117*f6dc9357SAndroid Build Coastguard Worker     _posInArc = offset;
118*f6dc9357SAndroid Build Coastguard Worker     return InStream_SeekSet(Stream, offset);
119*f6dc9357SAndroid Build Coastguard Worker   }
120*f6dc9357SAndroid Build Coastguard Worker 
121*f6dc9357SAndroid Build Coastguard Worker   void InitSeekPositions()
122*f6dc9357SAndroid Build Coastguard Worker   {
123*f6dc9357SAndroid Build Coastguard Worker     /* (_virtPos) and (_posInArc) is used only in Read() (that calls ReadPhy()).
124*f6dc9357SAndroid Build Coastguard Worker        So we must reset these variables before first call of Read() */
125*f6dc9357SAndroid Build Coastguard Worker     Reset_VirtPos();
126*f6dc9357SAndroid Build Coastguard Worker     Reset_PosInArc();
127*f6dc9357SAndroid Build Coastguard Worker     _chunkIndexPrev = 0;
128*f6dc9357SAndroid Build Coastguard Worker     _packSizeProcessed = 0;
129*f6dc9357SAndroid Build Coastguard Worker   }
130*f6dc9357SAndroid Build Coastguard Worker 
131*f6dc9357SAndroid Build Coastguard Worker   // virtual functions
132*f6dc9357SAndroid Build Coastguard Worker   bool Init_PackSizeProcessed() Z7_override
133*f6dc9357SAndroid Build Coastguard Worker   {
134*f6dc9357SAndroid Build Coastguard Worker     _packSizeProcessed = 0;
135*f6dc9357SAndroid Build Coastguard Worker     return true;
136*f6dc9357SAndroid Build Coastguard Worker   }
137*f6dc9357SAndroid Build Coastguard Worker   bool Get_PackSizeProcessed(UInt64 &size) Z7_override
138*f6dc9357SAndroid Build Coastguard Worker   {
139*f6dc9357SAndroid Build Coastguard Worker     size = _packSizeProcessed;
140*f6dc9357SAndroid Build Coastguard Worker     return true;
141*f6dc9357SAndroid Build Coastguard Worker   }
142*f6dc9357SAndroid Build Coastguard Worker 
143*f6dc9357SAndroid Build Coastguard Worker   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback) Z7_override;
144*f6dc9357SAndroid Build Coastguard Worker   HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size, UInt32 &processed);
145*f6dc9357SAndroid Build Coastguard Worker };
146*f6dc9357SAndroid Build Coastguard Worker 
147*f6dc9357SAndroid Build Coastguard Worker 
148*f6dc9357SAndroid Build Coastguard Worker 
149*f6dc9357SAndroid Build Coastguard Worker static const Byte kProps[] =
150*f6dc9357SAndroid Build Coastguard Worker {
151*f6dc9357SAndroid Build Coastguard Worker   kpidSize,
152*f6dc9357SAndroid Build Coastguard Worker   kpidPackSize
153*f6dc9357SAndroid Build Coastguard Worker };
154*f6dc9357SAndroid Build Coastguard Worker 
155*f6dc9357SAndroid Build Coastguard Worker static const Byte kArcProps[] =
156*f6dc9357SAndroid Build Coastguard Worker {
157*f6dc9357SAndroid Build Coastguard Worker   kpidClusterSize,
158*f6dc9357SAndroid Build Coastguard Worker   kpidNumBlocks,
159*f6dc9357SAndroid Build Coastguard Worker   kpidMethod
160*f6dc9357SAndroid Build Coastguard Worker };
161*f6dc9357SAndroid Build Coastguard Worker 
162*f6dc9357SAndroid Build Coastguard Worker IMP_IInArchive_Props
163*f6dc9357SAndroid Build Coastguard Worker IMP_IInArchive_ArcProps
164*f6dc9357SAndroid Build Coastguard Worker 
165*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
166*f6dc9357SAndroid Build Coastguard Worker {
167*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
168*f6dc9357SAndroid Build Coastguard Worker   NCOM::CPropVariant prop;
169*f6dc9357SAndroid Build Coastguard Worker 
170*f6dc9357SAndroid Build Coastguard Worker   switch (propID)
171*f6dc9357SAndroid Build Coastguard Worker   {
172*f6dc9357SAndroid Build Coastguard Worker     case kpidMainSubfile: prop = (UInt32)0; break;
173*f6dc9357SAndroid Build Coastguard Worker     case kpidClusterSize: prop = (UInt32)((UInt32)1 << _blockSizeLog); break;
174*f6dc9357SAndroid Build Coastguard Worker     case kpidNumBlocks: prop = (UInt32)NumChunks; break;
175*f6dc9357SAndroid Build Coastguard Worker     case kpidPhySize: if (_phySize != 0) prop = _phySize; break;
176*f6dc9357SAndroid Build Coastguard Worker 
177*f6dc9357SAndroid Build Coastguard Worker     case kpidMethod:
178*f6dc9357SAndroid Build Coastguard Worker     {
179*f6dc9357SAndroid Build Coastguard Worker       FLAGS_TO_PROP(g_Methods, _methodFlags, prop);
180*f6dc9357SAndroid Build Coastguard Worker       break;
181*f6dc9357SAndroid Build Coastguard Worker     }
182*f6dc9357SAndroid Build Coastguard Worker 
183*f6dc9357SAndroid Build Coastguard Worker     case kpidErrorFlags:
184*f6dc9357SAndroid Build Coastguard Worker     {
185*f6dc9357SAndroid Build Coastguard Worker       UInt32 v = 0;
186*f6dc9357SAndroid Build Coastguard Worker       if (!_isArc)        v |= kpv_ErrorFlags_IsNotArc;
187*f6dc9357SAndroid Build Coastguard Worker       if (_headersError)  v |= kpv_ErrorFlags_HeadersError;
188*f6dc9357SAndroid Build Coastguard Worker       if (_unexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
189*f6dc9357SAndroid Build Coastguard Worker       // if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
190*f6dc9357SAndroid Build Coastguard Worker       if (!Stream && v == 0 && _isArc)
191*f6dc9357SAndroid Build Coastguard Worker         v = kpv_ErrorFlags_HeadersError;
192*f6dc9357SAndroid Build Coastguard Worker       if (v != 0)
193*f6dc9357SAndroid Build Coastguard Worker         prop = v;
194*f6dc9357SAndroid Build Coastguard Worker       break;
195*f6dc9357SAndroid Build Coastguard Worker     }
196*f6dc9357SAndroid Build Coastguard Worker   }
197*f6dc9357SAndroid Build Coastguard Worker 
198*f6dc9357SAndroid Build Coastguard Worker   prop.Detach(value);
199*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
200*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
201*f6dc9357SAndroid Build Coastguard Worker }
202*f6dc9357SAndroid Build Coastguard Worker 
203*f6dc9357SAndroid Build Coastguard Worker 
204*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
205*f6dc9357SAndroid Build Coastguard Worker {
206*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
207*f6dc9357SAndroid Build Coastguard Worker   NCOM::CPropVariant prop;
208*f6dc9357SAndroid Build Coastguard Worker 
209*f6dc9357SAndroid Build Coastguard Worker   switch (propID)
210*f6dc9357SAndroid Build Coastguard Worker   {
211*f6dc9357SAndroid Build Coastguard Worker     case kpidSize: prop = _size; break;
212*f6dc9357SAndroid Build Coastguard Worker     case kpidPackSize: prop = _phySize; break;
213*f6dc9357SAndroid Build Coastguard Worker     case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
214*f6dc9357SAndroid Build Coastguard Worker   }
215*f6dc9357SAndroid Build Coastguard Worker 
216*f6dc9357SAndroid Build Coastguard Worker   prop.Detach(value);
217*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
218*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
219*f6dc9357SAndroid Build Coastguard Worker }
220*f6dc9357SAndroid Build Coastguard Worker 
221*f6dc9357SAndroid Build Coastguard Worker 
222*f6dc9357SAndroid Build Coastguard Worker static unsigned GetLogSize(UInt32 size)
223*f6dc9357SAndroid Build Coastguard Worker {
224*f6dc9357SAndroid Build Coastguard Worker   unsigned k;
225*f6dc9357SAndroid Build Coastguard Worker   for (k = 0; k < 32; k++)
226*f6dc9357SAndroid Build Coastguard Worker     if (((UInt32)1 << k) == size)
227*f6dc9357SAndroid Build Coastguard Worker       return k;
228*f6dc9357SAndroid Build Coastguard Worker   return k;
229*f6dc9357SAndroid Build Coastguard Worker }
230*f6dc9357SAndroid Build Coastguard Worker 
231*f6dc9357SAndroid Build Coastguard Worker 
232*f6dc9357SAndroid Build Coastguard Worker HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback)
233*f6dc9357SAndroid Build Coastguard Worker {
234*f6dc9357SAndroid Build Coastguard Worker   const unsigned kHeaderSize = 28;
235*f6dc9357SAndroid Build Coastguard Worker   const unsigned kChunkHeaderSize = 12;
236*f6dc9357SAndroid Build Coastguard Worker   CHeader h;
237*f6dc9357SAndroid Build Coastguard Worker   {
238*f6dc9357SAndroid Build Coastguard Worker     Byte buf[kHeaderSize];
239*f6dc9357SAndroid Build Coastguard Worker     RINOK(ReadStream_FALSE(stream, buf, kHeaderSize))
240*f6dc9357SAndroid Build Coastguard Worker     if (memcmp(buf, k_Signature, 6) != 0)
241*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
242*f6dc9357SAndroid Build Coastguard Worker     h.Parse(buf);
243*f6dc9357SAndroid Build Coastguard Worker   }
244*f6dc9357SAndroid Build Coastguard Worker 
245*f6dc9357SAndroid Build Coastguard Worker   if (h.file_hdr_sz != kHeaderSize ||
246*f6dc9357SAndroid Build Coastguard Worker       h.chunk_hdr_sz != kChunkHeaderSize)
247*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
248*f6dc9357SAndroid Build Coastguard Worker 
249*f6dc9357SAndroid Build Coastguard Worker   NumChunks = h.NumChunks;
250*f6dc9357SAndroid Build Coastguard Worker 
251*f6dc9357SAndroid Build Coastguard Worker   const unsigned logSize = GetLogSize(h.BlockSize);
252*f6dc9357SAndroid Build Coastguard Worker   if (logSize < 2 || logSize >= 32)
253*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
254*f6dc9357SAndroid Build Coastguard Worker   _blockSizeLog = logSize;
255*f6dc9357SAndroid Build Coastguard Worker 
256*f6dc9357SAndroid Build Coastguard Worker   _size = (UInt64)h.NumBlocks << logSize;
257*f6dc9357SAndroid Build Coastguard Worker 
258*f6dc9357SAndroid Build Coastguard Worker   if (h.NumChunks >= (UInt32)(Int32)-2) // it's our limit
259*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
260*f6dc9357SAndroid Build Coastguard Worker 
261*f6dc9357SAndroid Build Coastguard Worker   _isArc = true;
262*f6dc9357SAndroid Build Coastguard Worker   Chunks.Reserve(h.NumChunks + 1);
263*f6dc9357SAndroid Build Coastguard Worker   UInt64 offset = kHeaderSize;
264*f6dc9357SAndroid Build Coastguard Worker   UInt32 virtBlock = 0;
265*f6dc9357SAndroid Build Coastguard Worker   UInt32 i;
266*f6dc9357SAndroid Build Coastguard Worker 
267*f6dc9357SAndroid Build Coastguard Worker   for (i = 0; i < h.NumChunks; i++)
268*f6dc9357SAndroid Build Coastguard Worker   {
269*f6dc9357SAndroid Build Coastguard Worker     {
270*f6dc9357SAndroid Build Coastguard Worker       const UInt32 mask = ((UInt32)1 << 16) - 1;
271*f6dc9357SAndroid Build Coastguard Worker       if ((i & mask) == mask && openCallback)
272*f6dc9357SAndroid Build Coastguard Worker       {
273*f6dc9357SAndroid Build Coastguard Worker         RINOK(openCallback->SetCompleted(NULL, &offset))
274*f6dc9357SAndroid Build Coastguard Worker       }
275*f6dc9357SAndroid Build Coastguard Worker     }
276*f6dc9357SAndroid Build Coastguard Worker     Byte buf[kChunkHeaderSize];
277*f6dc9357SAndroid Build Coastguard Worker     {
278*f6dc9357SAndroid Build Coastguard Worker       size_t processed = kChunkHeaderSize;
279*f6dc9357SAndroid Build Coastguard Worker       RINOK(ReadStream(stream, buf, &processed))
280*f6dc9357SAndroid Build Coastguard Worker       if (kChunkHeaderSize != processed)
281*f6dc9357SAndroid Build Coastguard Worker       {
282*f6dc9357SAndroid Build Coastguard Worker         offset += kChunkHeaderSize;
283*f6dc9357SAndroid Build Coastguard Worker         break;
284*f6dc9357SAndroid Build Coastguard Worker       }
285*f6dc9357SAndroid Build Coastguard Worker     }
286*f6dc9357SAndroid Build Coastguard Worker     const UInt32 type = Get32(&buf[0]);
287*f6dc9357SAndroid Build Coastguard Worker     const UInt32 numBlocks = Get32(&buf[4]);
288*f6dc9357SAndroid Build Coastguard Worker     UInt32 size = Get32(&buf[8]);
289*f6dc9357SAndroid Build Coastguard Worker 
290*f6dc9357SAndroid Build Coastguard Worker     if (type < CHUNK_TYPE_RAW ||
291*f6dc9357SAndroid Build Coastguard Worker         type > CHUNK_TYPE_CRC32)
292*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
293*f6dc9357SAndroid Build Coastguard Worker     if (size < kChunkHeaderSize)
294*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
295*f6dc9357SAndroid Build Coastguard Worker     CChunk c;
296*f6dc9357SAndroid Build Coastguard Worker     c.PhyOffset = offset + kChunkHeaderSize;
297*f6dc9357SAndroid Build Coastguard Worker     c.VirtBlock = virtBlock;
298*f6dc9357SAndroid Build Coastguard Worker     offset += size;
299*f6dc9357SAndroid Build Coastguard Worker     size -= kChunkHeaderSize;
300*f6dc9357SAndroid Build Coastguard Worker     _methodFlags |= ((UInt32)1 << (type - CHUNK_TYPE_RAW));
301*f6dc9357SAndroid Build Coastguard Worker 
302*f6dc9357SAndroid Build Coastguard Worker     if (numBlocks > h.NumBlocks - virtBlock)
303*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
304*f6dc9357SAndroid Build Coastguard Worker 
305*f6dc9357SAndroid Build Coastguard Worker     if (type == CHUNK_TYPE_CRC32)
306*f6dc9357SAndroid Build Coastguard Worker     {
307*f6dc9357SAndroid Build Coastguard Worker       // crc chunk must be last chunk (i == h.NumChunks -1);
308*f6dc9357SAndroid Build Coastguard Worker       if (size != kFillSize || numBlocks != 0)
309*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
310*f6dc9357SAndroid Build Coastguard Worker       {
311*f6dc9357SAndroid Build Coastguard Worker         size_t processed = kFillSize;
312*f6dc9357SAndroid Build Coastguard Worker         RINOK(ReadStream(stream, c.Fill, &processed))
313*f6dc9357SAndroid Build Coastguard Worker         if (kFillSize != processed)
314*f6dc9357SAndroid Build Coastguard Worker           break;
315*f6dc9357SAndroid Build Coastguard Worker       }
316*f6dc9357SAndroid Build Coastguard Worker       continue;
317*f6dc9357SAndroid Build Coastguard Worker     }
318*f6dc9357SAndroid Build Coastguard Worker     // else
319*f6dc9357SAndroid Build Coastguard Worker     {
320*f6dc9357SAndroid Build Coastguard Worker       if (numBlocks == 0)
321*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
322*f6dc9357SAndroid Build Coastguard Worker 
323*f6dc9357SAndroid Build Coastguard Worker       if (type == CHUNK_TYPE_DONT_CARE)
324*f6dc9357SAndroid Build Coastguard Worker       {
325*f6dc9357SAndroid Build Coastguard Worker         if (size != 0)
326*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
327*f6dc9357SAndroid Build Coastguard Worker         c.PhyOffset = MY_CHUNK_TYPE_DONT_CARE;
328*f6dc9357SAndroid Build Coastguard Worker       }
329*f6dc9357SAndroid Build Coastguard Worker       else if (type == CHUNK_TYPE_FILL)
330*f6dc9357SAndroid Build Coastguard Worker       {
331*f6dc9357SAndroid Build Coastguard Worker         if (size != kFillSize)
332*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
333*f6dc9357SAndroid Build Coastguard Worker         c.PhyOffset = MY_CHUNK_TYPE_FILL;
334*f6dc9357SAndroid Build Coastguard Worker         size_t processed = kFillSize;
335*f6dc9357SAndroid Build Coastguard Worker         RINOK(ReadStream(stream, c.Fill, &processed))
336*f6dc9357SAndroid Build Coastguard Worker         if (kFillSize != processed)
337*f6dc9357SAndroid Build Coastguard Worker           break;
338*f6dc9357SAndroid Build Coastguard Worker       }
339*f6dc9357SAndroid Build Coastguard Worker       else if (type == CHUNK_TYPE_RAW)
340*f6dc9357SAndroid Build Coastguard Worker       {
341*f6dc9357SAndroid Build Coastguard Worker         /* Here we require (size == virtSize).
342*f6dc9357SAndroid Build Coastguard Worker            Probably original decoder also requires it.
343*f6dc9357SAndroid Build Coastguard Worker            But maybe size of last chunk can be non-aligned with blockSize ? */
344*f6dc9357SAndroid Build Coastguard Worker         const UInt32 virtSize = (numBlocks << _blockSizeLog);
345*f6dc9357SAndroid Build Coastguard Worker         if (size != virtSize || numBlocks != (virtSize >> _blockSizeLog))
346*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
347*f6dc9357SAndroid Build Coastguard Worker       }
348*f6dc9357SAndroid Build Coastguard Worker       else
349*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
350*f6dc9357SAndroid Build Coastguard Worker 
351*f6dc9357SAndroid Build Coastguard Worker       virtBlock += numBlocks;
352*f6dc9357SAndroid Build Coastguard Worker       Chunks.AddInReserved(c);
353*f6dc9357SAndroid Build Coastguard Worker       if (type == CHUNK_TYPE_RAW)
354*f6dc9357SAndroid Build Coastguard Worker         RINOK(InStream_SeekSet(stream, offset))
355*f6dc9357SAndroid Build Coastguard Worker     }
356*f6dc9357SAndroid Build Coastguard Worker   }
357*f6dc9357SAndroid Build Coastguard Worker 
358*f6dc9357SAndroid Build Coastguard Worker   if (i != h.NumChunks)
359*f6dc9357SAndroid Build Coastguard Worker     _unexpectedEnd = true;
360*f6dc9357SAndroid Build Coastguard Worker   else if (virtBlock != h.NumBlocks)
361*f6dc9357SAndroid Build Coastguard Worker     _headersError = true;
362*f6dc9357SAndroid Build Coastguard Worker 
363*f6dc9357SAndroid Build Coastguard Worker   _phySize = offset;
364*f6dc9357SAndroid Build Coastguard Worker 
365*f6dc9357SAndroid Build Coastguard Worker   {
366*f6dc9357SAndroid Build Coastguard Worker     CChunk c;
367*f6dc9357SAndroid Build Coastguard Worker     c.VirtBlock = virtBlock;
368*f6dc9357SAndroid Build Coastguard Worker     c.PhyOffset = offset;
369*f6dc9357SAndroid Build Coastguard Worker     Chunks.AddInReserved(c);
370*f6dc9357SAndroid Build Coastguard Worker   }
371*f6dc9357SAndroid Build Coastguard Worker   _virtSize_fromChunks = (UInt64)virtBlock << _blockSizeLog;
372*f6dc9357SAndroid Build Coastguard Worker 
373*f6dc9357SAndroid Build Coastguard Worker   Stream = stream;
374*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
375*f6dc9357SAndroid Build Coastguard Worker }
376*f6dc9357SAndroid Build Coastguard Worker 
377*f6dc9357SAndroid Build Coastguard Worker 
378*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::Close())
379*f6dc9357SAndroid Build Coastguard Worker {
380*f6dc9357SAndroid Build Coastguard Worker   Chunks.Clear();
381*f6dc9357SAndroid Build Coastguard Worker   _isArc = false;
382*f6dc9357SAndroid Build Coastguard Worker   _virtSize_fromChunks = 0;
383*f6dc9357SAndroid Build Coastguard Worker   // _unsupported = false;
384*f6dc9357SAndroid Build Coastguard Worker   _headersError = false;
385*f6dc9357SAndroid Build Coastguard Worker   _unexpectedEnd = false;
386*f6dc9357SAndroid Build Coastguard Worker   _phySize = 0;
387*f6dc9357SAndroid Build Coastguard Worker   _methodFlags = 0;
388*f6dc9357SAndroid Build Coastguard Worker 
389*f6dc9357SAndroid Build Coastguard Worker   _chunkIndexPrev = 0;
390*f6dc9357SAndroid Build Coastguard Worker   _packSizeProcessed = 0;
391*f6dc9357SAndroid Build Coastguard Worker 
392*f6dc9357SAndroid Build Coastguard Worker   // CHandlerImg:
393*f6dc9357SAndroid Build Coastguard Worker   Clear_HandlerImg_Vars();
394*f6dc9357SAndroid Build Coastguard Worker   Stream.Release();
395*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
396*f6dc9357SAndroid Build Coastguard Worker }
397*f6dc9357SAndroid Build Coastguard Worker 
398*f6dc9357SAndroid Build Coastguard Worker 
399*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream))
400*f6dc9357SAndroid Build Coastguard Worker {
401*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
402*f6dc9357SAndroid Build Coastguard Worker   *stream = NULL;
403*f6dc9357SAndroid Build Coastguard Worker   if (Chunks.Size() < 1)
404*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
405*f6dc9357SAndroid Build Coastguard Worker   if (Chunks.Size() < 2 && _virtSize_fromChunks != 0)
406*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
407*f6dc9357SAndroid Build Coastguard Worker   // if (_unsupported) return S_FALSE;
408*f6dc9357SAndroid Build Coastguard Worker   InitSeekPositions();
409*f6dc9357SAndroid Build Coastguard Worker   CMyComPtr<ISequentialInStream> streamTemp = this;
410*f6dc9357SAndroid Build Coastguard Worker   *stream = streamTemp.Detach();
411*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
412*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
413*f6dc9357SAndroid Build Coastguard Worker }
414*f6dc9357SAndroid Build Coastguard Worker 
415*f6dc9357SAndroid Build Coastguard Worker 
416*f6dc9357SAndroid Build Coastguard Worker 
417*f6dc9357SAndroid Build Coastguard Worker HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size, UInt32 &processed)
418*f6dc9357SAndroid Build Coastguard Worker {
419*f6dc9357SAndroid Build Coastguard Worker   processed = 0;
420*f6dc9357SAndroid Build Coastguard Worker   if (offset > _phySize || offset + size > _phySize)
421*f6dc9357SAndroid Build Coastguard Worker   {
422*f6dc9357SAndroid Build Coastguard Worker     // we don't expect these cases, if (_phySize) was set correctly.
423*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
424*f6dc9357SAndroid Build Coastguard Worker   }
425*f6dc9357SAndroid Build Coastguard Worker   if (offset != _posInArc)
426*f6dc9357SAndroid Build Coastguard Worker   {
427*f6dc9357SAndroid Build Coastguard Worker     const HRESULT res = Seek2(offset);
428*f6dc9357SAndroid Build Coastguard Worker     if (res != S_OK)
429*f6dc9357SAndroid Build Coastguard Worker     {
430*f6dc9357SAndroid Build Coastguard Worker       Reset_PosInArc(); // we don't trust seek_pos in case of error
431*f6dc9357SAndroid Build Coastguard Worker       return res;
432*f6dc9357SAndroid Build Coastguard Worker     }
433*f6dc9357SAndroid Build Coastguard Worker   }
434*f6dc9357SAndroid Build Coastguard Worker   {
435*f6dc9357SAndroid Build Coastguard Worker     size_t size2 = size;
436*f6dc9357SAndroid Build Coastguard Worker     const HRESULT res = ReadStream(Stream, data, &size2);
437*f6dc9357SAndroid Build Coastguard Worker     processed = (UInt32)size2;
438*f6dc9357SAndroid Build Coastguard Worker     _packSizeProcessed += size2;
439*f6dc9357SAndroid Build Coastguard Worker     _posInArc += size2;
440*f6dc9357SAndroid Build Coastguard Worker     if (res != S_OK)
441*f6dc9357SAndroid Build Coastguard Worker       Reset_PosInArc(); // we don't trust seek_pos in case of reading error
442*f6dc9357SAndroid Build Coastguard Worker     return res;
443*f6dc9357SAndroid Build Coastguard Worker   }
444*f6dc9357SAndroid Build Coastguard Worker }
445*f6dc9357SAndroid Build Coastguard Worker 
446*f6dc9357SAndroid Build Coastguard Worker 
447*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::Read(void *data, UInt32 size, UInt32 *processedSize))
448*f6dc9357SAndroid Build Coastguard Worker {
449*f6dc9357SAndroid Build Coastguard Worker   if (processedSize)
450*f6dc9357SAndroid Build Coastguard Worker     *processedSize = 0;
451*f6dc9357SAndroid Build Coastguard Worker   // const unsigned kLimit = (1 << 16) + 1; if (size > kLimit) size = kLimit; // for debug
452*f6dc9357SAndroid Build Coastguard Worker   if (_virtPos >= _virtSize_fromChunks)
453*f6dc9357SAndroid Build Coastguard Worker     return S_OK;
454*f6dc9357SAndroid Build Coastguard Worker   {
455*f6dc9357SAndroid Build Coastguard Worker     const UInt64 rem = _virtSize_fromChunks - _virtPos;
456*f6dc9357SAndroid Build Coastguard Worker     if (size > rem)
457*f6dc9357SAndroid Build Coastguard Worker       size = (UInt32)rem;
458*f6dc9357SAndroid Build Coastguard Worker     if (size == 0)
459*f6dc9357SAndroid Build Coastguard Worker       return S_OK;
460*f6dc9357SAndroid Build Coastguard Worker   }
461*f6dc9357SAndroid Build Coastguard Worker 
462*f6dc9357SAndroid Build Coastguard Worker   UInt32 chunkIndex = _chunkIndexPrev;
463*f6dc9357SAndroid Build Coastguard Worker   if (chunkIndex + 1 >= Chunks.Size())
464*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
465*f6dc9357SAndroid Build Coastguard Worker   {
466*f6dc9357SAndroid Build Coastguard Worker     const UInt32 blockIndex = (UInt32)(_virtPos >> _blockSizeLog);
467*f6dc9357SAndroid Build Coastguard Worker     if (blockIndex <  Chunks[chunkIndex    ].VirtBlock ||
468*f6dc9357SAndroid Build Coastguard Worker         blockIndex >= Chunks[chunkIndex + 1].VirtBlock)
469*f6dc9357SAndroid Build Coastguard Worker     {
470*f6dc9357SAndroid Build Coastguard Worker       unsigned left = 0, right = Chunks.Size() - 1;
471*f6dc9357SAndroid Build Coastguard Worker       for (;;)
472*f6dc9357SAndroid Build Coastguard Worker       {
473*f6dc9357SAndroid Build Coastguard Worker         const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
474*f6dc9357SAndroid Build Coastguard Worker         if (mid == left)
475*f6dc9357SAndroid Build Coastguard Worker           break;
476*f6dc9357SAndroid Build Coastguard Worker         if (blockIndex < Chunks[mid].VirtBlock)
477*f6dc9357SAndroid Build Coastguard Worker           right = mid;
478*f6dc9357SAndroid Build Coastguard Worker         else
479*f6dc9357SAndroid Build Coastguard Worker           left = mid;
480*f6dc9357SAndroid Build Coastguard Worker       }
481*f6dc9357SAndroid Build Coastguard Worker       chunkIndex = left;
482*f6dc9357SAndroid Build Coastguard Worker       _chunkIndexPrev = chunkIndex;
483*f6dc9357SAndroid Build Coastguard Worker     }
484*f6dc9357SAndroid Build Coastguard Worker   }
485*f6dc9357SAndroid Build Coastguard Worker 
486*f6dc9357SAndroid Build Coastguard Worker   const CChunk &c = Chunks[chunkIndex];
487*f6dc9357SAndroid Build Coastguard Worker   const UInt64 offset = _virtPos - ((UInt64)c.VirtBlock << _blockSizeLog);
488*f6dc9357SAndroid Build Coastguard Worker   {
489*f6dc9357SAndroid Build Coastguard Worker     const UInt32 numBlocks = Chunks[chunkIndex + 1].VirtBlock - c.VirtBlock;
490*f6dc9357SAndroid Build Coastguard Worker     const UInt64 rem = ((UInt64)numBlocks << _blockSizeLog) - offset;
491*f6dc9357SAndroid Build Coastguard Worker     if (size > rem)
492*f6dc9357SAndroid Build Coastguard Worker       size = (UInt32)rem;
493*f6dc9357SAndroid Build Coastguard Worker   }
494*f6dc9357SAndroid Build Coastguard Worker 
495*f6dc9357SAndroid Build Coastguard Worker   const UInt64 phyOffset = c.PhyOffset;
496*f6dc9357SAndroid Build Coastguard Worker 
497*f6dc9357SAndroid Build Coastguard Worker   if (phyOffset >= MY_CHUNK_TYPE_RAW_START)
498*f6dc9357SAndroid Build Coastguard Worker   {
499*f6dc9357SAndroid Build Coastguard Worker     UInt32 processed = 0;
500*f6dc9357SAndroid Build Coastguard Worker     const HRESULT res = ReadPhy(phyOffset + offset, data, size, processed);
501*f6dc9357SAndroid Build Coastguard Worker     if (processedSize)
502*f6dc9357SAndroid Build Coastguard Worker       *processedSize = processed;
503*f6dc9357SAndroid Build Coastguard Worker     _virtPos += processed;
504*f6dc9357SAndroid Build Coastguard Worker     return res;
505*f6dc9357SAndroid Build Coastguard Worker   }
506*f6dc9357SAndroid Build Coastguard Worker 
507*f6dc9357SAndroid Build Coastguard Worker   Byte b = 0;
508*f6dc9357SAndroid Build Coastguard Worker 
509*f6dc9357SAndroid Build Coastguard Worker   if (phyOffset == MY_CHUNK_TYPE_FILL)
510*f6dc9357SAndroid Build Coastguard Worker   {
511*f6dc9357SAndroid Build Coastguard Worker     const Byte b0 = c.Fill [0];
512*f6dc9357SAndroid Build Coastguard Worker     const Byte b1 = c.Fill [1];
513*f6dc9357SAndroid Build Coastguard Worker     const Byte b2 = c.Fill [2];
514*f6dc9357SAndroid Build Coastguard Worker     const Byte b3 = c.Fill [3];
515*f6dc9357SAndroid Build Coastguard Worker     if (b0 != b1 ||
516*f6dc9357SAndroid Build Coastguard Worker         b0 != b2 ||
517*f6dc9357SAndroid Build Coastguard Worker         b0 != b3)
518*f6dc9357SAndroid Build Coastguard Worker     {
519*f6dc9357SAndroid Build Coastguard Worker       if (processedSize)
520*f6dc9357SAndroid Build Coastguard Worker         *processedSize = size;
521*f6dc9357SAndroid Build Coastguard Worker       _virtPos += size;
522*f6dc9357SAndroid Build Coastguard Worker       Byte *dest = (Byte *)data;
523*f6dc9357SAndroid Build Coastguard Worker       while (size >= 4)
524*f6dc9357SAndroid Build Coastguard Worker       {
525*f6dc9357SAndroid Build Coastguard Worker         dest[0] = b0;
526*f6dc9357SAndroid Build Coastguard Worker         dest[1] = b1;
527*f6dc9357SAndroid Build Coastguard Worker         dest[2] = b2;
528*f6dc9357SAndroid Build Coastguard Worker         dest[3] = b3;
529*f6dc9357SAndroid Build Coastguard Worker         dest += 4;
530*f6dc9357SAndroid Build Coastguard Worker         size -= 4;
531*f6dc9357SAndroid Build Coastguard Worker       }
532*f6dc9357SAndroid Build Coastguard Worker       if (size > 0) dest[0] = b0;
533*f6dc9357SAndroid Build Coastguard Worker       if (size > 1) dest[1] = b1;
534*f6dc9357SAndroid Build Coastguard Worker       if (size > 2) dest[2] = b2;
535*f6dc9357SAndroid Build Coastguard Worker       return S_OK;
536*f6dc9357SAndroid Build Coastguard Worker     }
537*f6dc9357SAndroid Build Coastguard Worker     b = b0;
538*f6dc9357SAndroid Build Coastguard Worker   }
539*f6dc9357SAndroid Build Coastguard Worker   else if (phyOffset != MY_CHUNK_TYPE_DONT_CARE)
540*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
541*f6dc9357SAndroid Build Coastguard Worker 
542*f6dc9357SAndroid Build Coastguard Worker   memset(data, b, size);
543*f6dc9357SAndroid Build Coastguard Worker   _virtPos += size;
544*f6dc9357SAndroid Build Coastguard Worker   if (processedSize)
545*f6dc9357SAndroid Build Coastguard Worker     *processedSize = size;
546*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
547*f6dc9357SAndroid Build Coastguard Worker }
548*f6dc9357SAndroid Build Coastguard Worker 
549*f6dc9357SAndroid Build Coastguard Worker REGISTER_ARC_I(
550*f6dc9357SAndroid Build Coastguard Worker   "Sparse", "simg img", NULL, 0xc2,
551*f6dc9357SAndroid Build Coastguard Worker   k_Signature,
552*f6dc9357SAndroid Build Coastguard Worker   0,
553*f6dc9357SAndroid Build Coastguard Worker   0,
554*f6dc9357SAndroid Build Coastguard Worker   NULL)
555*f6dc9357SAndroid Build Coastguard Worker 
556*f6dc9357SAndroid Build Coastguard Worker }}
557