Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / Iso / IsoHandler.cpp
1 // IsoHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "Common/ComTry.h"\r
6 #include "Common/IntToString.h"\r
7 #include "Common/StringConvert.h"\r
8 \r
9 #include "Windows/PropVariant.h"\r
10 #include "Windows/Time.h"\r
11 \r
12 #include "../../Common/LimitedStreams.h"\r
13 #include "../../Common/ProgressUtils.h"\r
14 \r
15 #include "../../Compress/CopyCoder.h"\r
16 \r
17 #include "../Common/ItemNameUtils.h"\r
18 \r
19 #include "IsoHandler.h"\r
20 \r
21 using namespace NWindows;\r
22 using namespace NTime;\r
23 \r
24 namespace NArchive {\r
25 namespace NIso {\r
26 \r
27 static const STATPROPSTG kProps[] =\r
28 {\r
29   { NULL, kpidPath, VT_BSTR},\r
30   { NULL, kpidIsDir, VT_BOOL},\r
31   { NULL, kpidSize, VT_UI8},\r
32   { NULL, kpidPackSize, VT_UI8},\r
33   { NULL, kpidMTime, VT_FILETIME}\r
34 };\r
35 \r
36 static const STATPROPSTG kArcProps[] =\r
37 {\r
38   { NULL, kpidComment, VT_BSTR},\r
39   { NULL, kpidCTime, VT_FILETIME},\r
40   { NULL, kpidMTime, VT_FILETIME}\r
41   // { NULL, kpidPhySize, VT_UI8},\r
42   // { NULL, kpidHeadersSize, VT_UI8}\r
43 };\r
44 \r
45 IMP_IInArchive_Props\r
46 IMP_IInArchive_ArcProps\r
47 \r
48 STDMETHODIMP CHandler::Open(IInStream *stream,\r
49     const UInt64 * /* maxCheckStartPosition */,\r
50     IArchiveOpenCallback * /* openArchiveCallback */)\r
51 {\r
52   COM_TRY_BEGIN\r
53   Close();\r
54   // try\r
55   {\r
56     if (_archive.Open(stream) != S_OK)\r
57       return S_FALSE;\r
58     _stream = stream;\r
59   }\r
60   // catch(...) { return S_FALSE; }\r
61   return S_OK;\r
62   COM_TRY_END\r
63 }\r
64 \r
65 STDMETHODIMP CHandler::Close()\r
66 {\r
67   _archive.Clear();\r
68   _stream.Release();\r
69   return S_OK;\r
70 }\r
71 \r
72 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
73 {\r
74   *numItems = _archive.Refs.Size() + _archive.BootEntries.Size();\r
75   return S_OK;\r
76 }\r
77 \r
78 static void AddString(AString &s, const char *name, const Byte *p, int size)\r
79 {\r
80   int i;\r
81   for (i = 0; i < size && p[i]; i++);\r
82   for (; i > 0 && p[i - 1] == ' '; i--);\r
83   if (i != 0)\r
84   {\r
85     AString d;\r
86     memcpy(d.GetBuffer(i), p, i);\r
87     d.ReleaseBuffer(i);\r
88     s += '\n';\r
89     s += name;\r
90     s += ": ";\r
91     s += d;\r
92   }\r
93 }\r
94 \r
95 #define ADD_STRING(n, v) AddString(s, n, vol. ## v, sizeof(vol. ## v))\r
96 \r
97 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
98 {\r
99   COM_TRY_BEGIN\r
100   NWindows::NCOM::CPropVariant prop;\r
101   const CVolumeDescriptor &vol = _archive.VolDescs[_archive.MainVolDescIndex];\r
102   switch(propID)\r
103   {\r
104     case kpidComment:\r
105     {\r
106       AString s;\r
107       ADD_STRING("System", SystemId);\r
108       ADD_STRING("Volume", VolumeId);\r
109       ADD_STRING("VolumeSet", VolumeSetId);\r
110       ADD_STRING("Publisher", PublisherId);\r
111       ADD_STRING("Preparer", DataPreparerId);\r
112       ADD_STRING("Application", ApplicationId);\r
113       ADD_STRING("Copyright", CopyrightFileId);\r
114       ADD_STRING("Abstract", AbstractFileId);\r
115       ADD_STRING("Bib", BibFileId);\r
116       prop = s;\r
117       break;\r
118     }\r
119     case kpidCTime: { FILETIME utc; if (vol.CTime.GetFileTime(utc)) prop = utc; break; }\r
120     case kpidMTime: { FILETIME utc; if (vol.MTime.GetFileTime(utc)) prop = utc; break; }\r
121     // case kpidPhySize: break;\r
122     // case kpidHeadersSize: break;\r
123     case kpidError: if (_archive.IncorrectBigEndian) prop = "Incorrect big-endian headers"; break;\r
124   }\r
125   prop.Detach(value);\r
126   return S_OK;\r
127   COM_TRY_END\r
128 }\r
129 \r
130 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
131 {\r
132   COM_TRY_BEGIN\r
133   NWindows::NCOM::CPropVariant prop;\r
134   if (index >= (UInt32)_archive.Refs.Size())\r
135   {\r
136     index -= _archive.Refs.Size();\r
137     const CBootInitialEntry &be = _archive.BootEntries[index];\r
138     switch(propID)\r
139     {\r
140       case kpidPath:\r
141       {\r
142         // wchar_t name[32];\r
143         // ConvertUInt64ToString(index + 1, name);\r
144         UString s = L"[BOOT]" WSTRING_PATH_SEPARATOR;\r
145         // s += name;\r
146         // s += L"-";\r
147         s += be.GetName();\r
148         prop = (const wchar_t *)s;\r
149         break;\r
150       }\r
151       case kpidIsDir: prop = false; break;\r
152       case kpidSize:\r
153       case kpidPackSize:\r
154         prop = (UInt64)_archive.GetBootItemSize(index);\r
155         break;\r
156     }\r
157   }\r
158   else\r
159   {\r
160     const CRef &ref = _archive.Refs[index];\r
161     const CDir &item = ref.Dir->_subItems[ref.Index];\r
162     switch(propID)\r
163     {\r
164       case kpidPath:\r
165         // if (item.FileId.GetCapacity() >= 0)\r
166         {\r
167           UString s;\r
168           if (_archive.IsJoliet())\r
169             s = item.GetPathU();\r
170           else\r
171             s = MultiByteToUnicodeString(item.GetPath(_archive.IsSusp, _archive.SuspSkipSize), CP_OEMCP);\r
172 \r
173           int pos = s.ReverseFind(L';');\r
174           if (pos >= 0 && pos == s.Length() - 2)\r
175               if (s[s.Length() - 1] == L'1')\r
176                 s = s.Left(pos);\r
177           if (!s.IsEmpty())\r
178             if (s[s.Length() - 1] == L'.')\r
179               s = s.Left(s.Length() - 1);\r
180           prop = (const wchar_t *)NItemName::GetOSName2(s);\r
181         }\r
182         break;\r
183       case kpidIsDir: prop = item.IsDir(); break;\r
184       case kpidSize:\r
185       case kpidPackSize:\r
186         if (!item.IsDir())\r
187           prop = (UInt64)item.DataLength;\r
188         break;\r
189       case kpidMTime:\r
190       {\r
191         FILETIME utc;\r
192         if (item.DateTime.GetFileTime(utc))\r
193           prop = utc;\r
194         break;\r
195       }\r
196     }\r
197   }\r
198   prop.Detach(value);\r
199   return S_OK;\r
200   COM_TRY_END\r
201 }\r
202 \r
203 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
204     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
205 {\r
206   COM_TRY_BEGIN\r
207   bool allFilesMode = (numItems == (UInt32)-1);\r
208   if (allFilesMode)\r
209     numItems = _archive.Refs.Size();\r
210   if (numItems == 0)\r
211     return S_OK;\r
212   UInt64 totalSize = 0;\r
213   UInt32 i;\r
214   for(i = 0; i < numItems; i++)\r
215   {\r
216     UInt32 index = (allFilesMode ? i : indices[i]);\r
217     if (index < (UInt32)_archive.Refs.Size())\r
218     {\r
219       const CRef &ref = _archive.Refs[index];\r
220       const CDir &item = ref.Dir->_subItems[ref.Index];\r
221       totalSize += item.DataLength;\r
222     }\r
223     else\r
224     {\r
225       totalSize += _archive.GetBootItemSize(index - _archive.Refs.Size());\r
226     }\r
227   }\r
228   extractCallback->SetTotal(totalSize);\r
229 \r
230   UInt64 currentTotalSize = 0;\r
231   UInt64 currentItemSize;\r
232   \r
233   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
234   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
235 \r
236   CLocalProgress *lps = new CLocalProgress;\r
237   CMyComPtr<ICompressProgressInfo> progress = lps;\r
238   lps->Init(extractCallback, false);\r
239 \r
240   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;\r
241   CMyComPtr<ISequentialInStream> inStream(streamSpec);\r
242   streamSpec->SetStream(_stream);\r
243 \r
244   CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;\r
245   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);\r
246 \r
247   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)\r
248   {\r
249     lps->InSize = lps->OutSize = currentTotalSize;\r
250     RINOK(lps->SetCur());\r
251     currentItemSize = 0;\r
252     CMyComPtr<ISequentialOutStream> realOutStream;\r
253     Int32 askMode = testMode ?\r
254         NExtract::NAskMode::kTest :\r
255         NExtract::NAskMode::kExtract;\r
256     UInt32 index = allFilesMode ? i : indices[i];\r
257     \r
258     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));\r
259 \r
260     UInt64 blockIndex;\r
261     if (index < (UInt32)_archive.Refs.Size())\r
262     {\r
263       const CRef &ref = _archive.Refs[index];\r
264       const CDir &item = ref.Dir->_subItems[ref.Index];\r
265       if (item.IsDir())\r
266       {\r
267         RINOK(extractCallback->PrepareOperation(askMode));\r
268         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));\r
269         continue;\r
270       }\r
271       currentItemSize = item.DataLength;\r
272       blockIndex = item.ExtentLocation;\r
273     }\r
274     else\r
275     {\r
276       int bootIndex = index - _archive.Refs.Size();\r
277       const CBootInitialEntry &be = _archive.BootEntries[bootIndex];\r
278       currentItemSize = _archive.GetBootItemSize(bootIndex);\r
279       blockIndex = be.LoadRBA;\r
280     }\r
281    \r
282     if (!testMode && !realOutStream)\r
283       continue;\r
284     RINOK(extractCallback->PrepareOperation(askMode));\r
285     outStreamSpec->SetStream(realOutStream);\r
286     realOutStream.Release();\r
287     outStreamSpec->Init(currentItemSize);\r
288     RINOK(_stream->Seek(blockIndex * _archive.BlockSize, STREAM_SEEK_SET, NULL));\r
289     streamSpec->Init(currentItemSize);\r
290     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));\r
291     outStreamSpec->ReleaseStream();\r
292     RINOK(extractCallback->SetOperationResult(outStreamSpec->IsFinishedOK() ?\r
293         NExtract::NOperationResult::kOK:\r
294         NExtract::NOperationResult::kDataError));\r
295   }\r
296   return S_OK;\r
297   COM_TRY_END\r
298 }\r
299 \r
300 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)\r
301 {\r
302   COM_TRY_BEGIN\r
303   *stream = 0;\r
304   UInt64 blockIndex;\r
305   UInt64 currentItemSize;\r
306   if (index < (UInt32)_archive.Refs.Size())\r
307   {\r
308     const CRef &ref = _archive.Refs[index];\r
309     const CDir &item = ref.Dir->_subItems[ref.Index];\r
310     if (item.IsDir())\r
311       return S_FALSE;\r
312     currentItemSize = item.DataLength;\r
313     blockIndex = item.ExtentLocation;\r
314   }\r
315   else\r
316   {\r
317     int bootIndex = index - _archive.Refs.Size();\r
318     const CBootInitialEntry &be = _archive.BootEntries[bootIndex];\r
319     currentItemSize = _archive.GetBootItemSize(bootIndex);\r
320     blockIndex = be.LoadRBA;\r
321   }\r
322   return CreateLimitedInStream(_stream, blockIndex * _archive.BlockSize, currentItemSize, stream);\r
323   COM_TRY_END\r
324 }\r
325 \r
326 }}\r