Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / MubHandler.cpp
1 // MubHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/CpuArch.h"\r
6 \r
7 #include "Common/ComTry.h"\r
8 \r
9 #include "Windows/PropVariant.h"\r
10 \r
11 #include "../Common/LimitedStreams.h"\r
12 #include "../Common/ProgressUtils.h"\r
13 #include "../Common/RegisterArc.h"\r
14 #include "../Common/StreamUtils.h"\r
15 \r
16 #include "../Compress/CopyCoder.h"\r
17 \r
18 #define Get32(p) GetBe32(p)\r
19 \r
20 namespace NArchive {\r
21 namespace NMub {\r
22 \r
23 struct CItem\r
24 {\r
25   UInt32 Type;\r
26   UInt32 SubType;\r
27   UInt64 Offset;\r
28   UInt64 Size;\r
29   UInt32 Align;\r
30   bool IsTail;\r
31 };\r
32 \r
33 const UInt32 kNumFilesMax = 10;\r
34 \r
35 class CHandler:\r
36   public IInArchive,\r
37   public IInArchiveGetStream,\r
38   public CMyUnknownImp\r
39 {\r
40   UInt64 _startPos;\r
41   CMyComPtr<IInStream> _stream;\r
42   UInt32 _numItems;\r
43   CItem _items[kNumFilesMax + 1];\r
44   HRESULT Open2(IInStream *stream);\r
45 public:\r
46   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)\r
47   INTERFACE_IInArchive(;)\r
48   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);\r
49 };\r
50 \r
51 STATPROPSTG kProps[] =\r
52 {\r
53   { NULL, kpidSize, VT_UI8}\r
54 };\r
55 \r
56 IMP_IInArchive_Props\r
57 IMP_IInArchive_ArcProps_NO\r
58 \r
59 #define MACH_ARCH_ABI64  0x1000000\r
60 #define MACH_MACHINE_386   7\r
61 #define MACH_MACHINE_ARM   12\r
62 #define MACH_MACHINE_SPARC 14\r
63 #define MACH_MACHINE_PPC   18\r
64 \r
65 #define MACH_MACHINE_PPC64 (MACH_MACHINE_PPC | MACH_ARCH_ABI64)\r
66 #define MACH_MACHINE_AMD64 (MACH_MACHINE_386 | MACH_ARCH_ABI64)\r
67 \r
68 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
69 {\r
70   NWindows::NCOM::CPropVariant prop;\r
71   const CItem &item = _items[index];\r
72   switch(propID)\r
73   {\r
74     case kpidExtension:\r
75     {\r
76       const wchar_t *ext;\r
77       if (item.IsTail)\r
78         ext = L"tail";\r
79       else\r
80       {\r
81         switch(item.Type)\r
82         {\r
83           case MACH_MACHINE_386:   ext = L"86";    break;\r
84           case MACH_MACHINE_ARM:   ext = L"arm";   break;\r
85           case MACH_MACHINE_SPARC: ext = L"sparc"; break;\r
86           case MACH_MACHINE_PPC:   ext = L"ppc";   break;\r
87           case MACH_MACHINE_PPC64: ext = L"ppc64"; break;\r
88           case MACH_MACHINE_AMD64: ext = L"x64";   break;\r
89           default: ext = L"unknown"; break;\r
90         }\r
91       }\r
92       prop = ext;\r
93       break;\r
94     }\r
95     case kpidSize:\r
96     case kpidPackSize:\r
97       prop = (UInt64)item.Size;\r
98       break;\r
99   }\r
100   prop.Detach(value);\r
101   return S_OK;\r
102 }\r
103 \r
104 #define MACH_TYPE_ABI64 (1 << 24)\r
105 #define MACH_SUBTYPE_ABI64 (1 << 31)\r
106 \r
107 HRESULT CHandler::Open2(IInStream *stream)\r
108 {\r
109   RINOK(stream->Seek(0, STREAM_SEEK_SET, &_startPos));\r
110 \r
111   const UInt32 kHeaderSize = 8;\r
112   const UInt32 kRecordSize = 5 * 4;\r
113   const UInt32 kBufSize = kHeaderSize + kNumFilesMax * kRecordSize;\r
114   Byte buf[kBufSize];\r
115   size_t processed = kBufSize;\r
116   RINOK(ReadStream(stream, buf, &processed));\r
117   if (processed < kHeaderSize)\r
118     return S_FALSE;\r
119   UInt32 num = Get32(buf + 4);\r
120   if (Get32(buf) != 0xCAFEBABE || num > kNumFilesMax || processed < kHeaderSize + num * kRecordSize)\r
121     return S_FALSE;\r
122   UInt64 endPosMax = kHeaderSize;\r
123   for (UInt32 i = 0; i < num; i++)\r
124   {\r
125     const Byte *p = buf + kHeaderSize + i * kRecordSize;\r
126     CItem &sb = _items[i];\r
127     sb.IsTail = false;\r
128     sb.Type = Get32(p);\r
129     sb.SubType = Get32(p + 4);\r
130     sb.Offset = Get32(p + 8);\r
131     sb.Size = Get32(p + 12);\r
132     sb.Align = Get32(p + 16);\r
133 \r
134     if ((sb.Type & ~MACH_TYPE_ABI64) >= 0x100 ||\r
135         (sb.SubType & ~MACH_SUBTYPE_ABI64) >= 0x100 ||\r
136         sb.Align > 31)\r
137       return S_FALSE;\r
138 \r
139     UInt64 endPos = (UInt64)sb.Offset + sb.Size;\r
140     if (endPos > endPosMax)\r
141       endPosMax = endPos;\r
142   }\r
143   UInt64 fileSize;\r
144   RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));\r
145   fileSize -= _startPos;\r
146   _numItems = num;\r
147   if (fileSize > endPosMax)\r
148   {\r
149     CItem &sb = _items[_numItems++];\r
150     sb.IsTail = true;\r
151     sb.Type = 0;\r
152     sb.SubType = 0;\r
153     sb.Offset = endPosMax;\r
154     sb.Size = fileSize - endPosMax;\r
155     sb.Align = 0;\r
156   }\r
157   return S_OK;\r
158 }\r
159 \r
160 STDMETHODIMP CHandler::Open(IInStream *inStream,\r
161     const UInt64 * /* maxCheckStartPosition */,\r
162     IArchiveOpenCallback * /* openArchiveCallback */)\r
163 {\r
164   COM_TRY_BEGIN\r
165   Close();\r
166   try\r
167   {\r
168     if (Open2(inStream) != S_OK)\r
169       return S_FALSE;\r
170     _stream = inStream;\r
171   }\r
172   catch(...) { return S_FALSE; }\r
173   return S_OK;\r
174   COM_TRY_END\r
175 }\r
176 \r
177 STDMETHODIMP CHandler::Close()\r
178 {\r
179   _stream.Release();\r
180   _numItems = 0;\r
181   return S_OK;\r
182 }\r
183 \r
184 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
185 {\r
186   *numItems = _numItems;\r
187   return S_OK;\r
188 }\r
189 \r
190 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
191     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
192 {\r
193   COM_TRY_BEGIN\r
194   bool allFilesMode = (numItems == (UInt32)-1);\r
195   if (allFilesMode)\r
196     numItems = _numItems;\r
197   if (numItems == 0)\r
198     return S_OK;\r
199   UInt64 totalSize = 0;\r
200   UInt32 i;\r
201   for (i = 0; i < numItems; i++)\r
202     totalSize += _items[allFilesMode ? i : indices[i]].Size;\r
203   extractCallback->SetTotal(totalSize);\r
204 \r
205   UInt64 currentTotalSize = 0;\r
206   \r
207   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
208   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
209 \r
210   CLocalProgress *lps = new CLocalProgress;\r
211   CMyComPtr<ICompressProgressInfo> progress = lps;\r
212   lps->Init(extractCallback, false);\r
213 \r
214   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;\r
215   CMyComPtr<ISequentialInStream> inStream(streamSpec);\r
216   streamSpec->SetStream(_stream);\r
217 \r
218   for (i = 0; i < numItems; i++)\r
219   {\r
220     lps->InSize = lps->OutSize = currentTotalSize;\r
221     RINOK(lps->SetCur());\r
222     CMyComPtr<ISequentialOutStream> realOutStream;\r
223     Int32 askMode = testMode ?\r
224         NExtract::NAskMode::kTest :\r
225         NExtract::NAskMode::kExtract;\r
226     UInt32 index = allFilesMode ? i : indices[i];\r
227     const CItem &item = _items[index];\r
228     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));\r
229     currentTotalSize += item.Size;\r
230     \r
231     if (!testMode && !realOutStream)\r
232       continue;\r
233     RINOK(extractCallback->PrepareOperation(askMode));\r
234     if (testMode)\r
235     {\r
236       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));\r
237       continue;\r
238     }\r
239     RINOK(_stream->Seek(_startPos + item.Offset, STREAM_SEEK_SET, NULL));\r
240     streamSpec->Init(item.Size);\r
241     RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));\r
242     realOutStream.Release();\r
243     RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ?\r
244         NExtract::NOperationResult::kOK:\r
245         NExtract::NOperationResult::kDataError));\r
246   }\r
247   return S_OK;\r
248   COM_TRY_END\r
249 }\r
250 \r
251 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)\r
252 {\r
253   COM_TRY_BEGIN\r
254   const CItem &item = _items[index];\r
255   return CreateLimitedInStream(_stream, _startPos + item.Offset, item.Size, stream);\r
256   COM_TRY_END\r
257 }\r
258 \r
259 static IInArchive *CreateArc() { return new CHandler; }\r
260 \r
261 static CArcInfo g_ArcInfo =\r
262   { L"Mub", L"", 0, 0xE2, { 0xCA, 0xFE, 0xBA, 0xBE, 0, 0, 0 }, 7, false, CreateArc, 0 };\r
263 \r
264 REGISTER_ARC(Mub)\r
265 \r
266 }}\r