Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / MslzHandler.cpp
1 // MslzHandler.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 #include "Common/MyString.h"\r
9 \r
10 #include "Windows/PropVariant.h"\r
11 \r
12 #include "../Common/InBuffer.h"\r
13 #include "../Common/ProgressUtils.h"\r
14 #include "../Common/RegisterArc.h"\r
15 #include "../Common/StreamUtils.h"\r
16 \r
17 #include "Common/DummyOutStream.h"\r
18 \r
19 namespace NArchive {\r
20 namespace NMslz {\r
21 \r
22 class CHandler:\r
23   public IInArchive,\r
24   public CMyUnknownImp\r
25 {\r
26   CMyComPtr<IInStream> _stream;\r
27   UInt32 _size;\r
28   UInt64 _packSize;\r
29   UString _name;\r
30 public:\r
31   MY_UNKNOWN_IMP1(IInArchive)\r
32   INTERFACE_IInArchive(;)\r
33 };\r
34 \r
35 STATPROPSTG kProps[] =\r
36 {\r
37   { NULL, kpidPath, VT_BSTR},\r
38   { NULL, kpidSize, VT_UI8},\r
39   { NULL, kpidPackSize, VT_UI8},\r
40 };\r
41 \r
42 IMP_IInArchive_Props\r
43 IMP_IInArchive_ArcProps_NO\r
44 \r
45 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
46 {\r
47   *numItems = 1;\r
48   return S_OK;\r
49 }\r
50 \r
51 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)\r
52 {\r
53   COM_TRY_BEGIN\r
54   NWindows::NCOM::CPropVariant prop;\r
55   switch(propID)\r
56   {\r
57     case kpidPath: if (!_name.IsEmpty()) prop = _name; break;\r
58     case kpidSize: prop = _size; break;\r
59     case kpidPackSize: prop = _packSize; break;\r
60   }\r
61   prop.Detach(value);\r
62   return S_OK;\r
63   COM_TRY_END\r
64 }\r
65 \r
66 static const unsigned kSignatureSize = 9;\r
67 static const unsigned kHeaderSize = kSignatureSize + 1 + 4;\r
68 #define MSLZ_SIGNATURE { 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33, 0x41 }\r
69 // old signature: 53 5A 20 88 F0 27 33\r
70 static const Byte signature[kSignatureSize] = MSLZ_SIGNATURE;\r
71 \r
72 static const wchar_t *g_Exts[] =\r
73 {\r
74   L"dll",\r
75   L"exe",\r
76   L"kmd",\r
77   L"sys"\r
78 };\r
79 \r
80 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPosition */,\r
81     IArchiveOpenCallback *callback)\r
82 {\r
83   COM_TRY_BEGIN\r
84   {\r
85     Close();\r
86     Byte buffer[kHeaderSize];\r
87     RINOK(ReadStream_FALSE(stream, buffer, kHeaderSize));\r
88     if (memcmp(buffer, signature, kSignatureSize) != 0)\r
89       return S_FALSE;\r
90     _size = GetUi32(buffer + 10);\r
91     if (_size > 0xFFFFFFE0)\r
92       return S_FALSE;\r
93     RINOK(stream->Seek(0, STREAM_SEEK_END, &_packSize));\r
94 \r
95     if (callback)\r
96     {\r
97       CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;\r
98       callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);\r
99       if (openVolumeCallback)\r
100       {\r
101         NWindows::NCOM::CPropVariant prop;\r
102         if (openVolumeCallback->GetProperty(kpidName, &prop) == S_OK && prop.vt == VT_BSTR)\r
103         {\r
104           UString baseName = prop.bstrVal;\r
105           if (!baseName.IsEmpty() && baseName.Back() == L'_')\r
106           {\r
107             baseName.DeleteBack();\r
108             Byte replaceByte = buffer[kSignatureSize];\r
109             if (replaceByte == 0)\r
110             {\r
111               for (int i = 0; i < sizeof(g_Exts) / sizeof(g_Exts[0]); i++)\r
112               {\r
113                 UString s = g_Exts[i];\r
114                 int len = s.Length();\r
115                 Byte b = (Byte)s.Back();\r
116                 s.DeleteBack();\r
117                 if (baseName.Length() >= len &&\r
118                     baseName[baseName.Length() - len] == '.' &&\r
119                     s.CompareNoCase(baseName.Right(len - 1)) == 0)\r
120                 {\r
121                   replaceByte = b;\r
122                   break;\r
123                 }\r
124               }\r
125             }\r
126             if (replaceByte >= 0x20 && replaceByte < 0x80)\r
127               _name = baseName + (wchar_t)replaceByte;\r
128           }\r
129         }\r
130       }\r
131     }\r
132     _stream = stream;\r
133   }\r
134   return S_OK;\r
135   COM_TRY_END\r
136 }\r
137 \r
138 STDMETHODIMP CHandler::Close()\r
139 {\r
140   _stream.Release();\r
141   _name.Empty();\r
142   return S_OK;\r
143 }\r
144 \r
145 // MslzDec is modified LZSS algorithm of Haruhiko Okumura:\r
146 //   maxLen = 18; Okumura\r
147 //   maxLen = 16; MS\r
148 \r
149 #define PROGRESS_AND_WRITE \\r
150   if ((dest & kMask) == 0) { RINOK(WriteStream(outStream, buf, kBufSize)); \\r
151     if ((dest & ((1 << 20) - 1)) == 0) \\r
152     { UInt64 inSize = inStream.GetProcessedSize(); UInt64 outSize = dest; \\r
153       RINOK(progress->SetRatioInfo(&inSize, &outSize)); }}\r
154 \r
155 static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UInt32 unpackSize, ICompressProgressInfo *progress)\r
156 {\r
157   const unsigned kBufSize = (1 << 12);\r
158   const unsigned kMask = kBufSize - 1;\r
159   Byte buf[kBufSize];\r
160   UInt32 dest = 0;\r
161   memset(buf, ' ', kBufSize);\r
162   while (dest < unpackSize)\r
163   {\r
164     Byte b;\r
165     if (!inStream.ReadByte(b))\r
166       return S_FALSE;\r
167     for (unsigned mask = (unsigned)b | 0x100; mask > 1 && dest < unpackSize; mask >>= 1)\r
168     {\r
169       if (!inStream.ReadByte(b))\r
170         return S_FALSE;\r
171       if (mask & 1)\r
172       {\r
173         buf[dest++ & kMask] = b;\r
174         PROGRESS_AND_WRITE\r
175       }\r
176       else\r
177       {\r
178         Byte b1;\r
179         if (!inStream.ReadByte(b1))\r
180           return S_FALSE;\r
181         const unsigned kMaxLen = 16; // 18 in Okumura's code.\r
182         unsigned src = (((((unsigned)b1 & 0xF0) << 4) | b) + kMaxLen) & kMask;\r
183         unsigned len = (b1 & 0xF) + 3;\r
184         if (len > kMaxLen || dest + len > unpackSize)\r
185           return S_FALSE;\r
186         do\r
187         {\r
188           buf[dest++ & kMask] = buf[src++ & kMask];\r
189           PROGRESS_AND_WRITE\r
190         }\r
191         while (--len != 0);\r
192       }\r
193     }\r
194   }\r
195   return WriteStream(outStream, buf, dest & kMask);\r
196 }\r
197 \r
198 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
199     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
200 {\r
201   COM_TRY_BEGIN\r
202   if (numItems == 0)\r
203     return S_OK;\r
204   if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))\r
205     return E_INVALIDARG;\r
206 \r
207   extractCallback->SetTotal(_size);\r
208 \r
209   CMyComPtr<ISequentialOutStream> realOutStream;\r
210   Int32 askMode = testMode ?\r
211       NExtract::NAskMode::kTest :\r
212       NExtract::NAskMode::kExtract;\r
213   RINOK(extractCallback->GetStream(0, &realOutStream, askMode));\r
214   if (!testMode && !realOutStream)\r
215     return S_OK;\r
216 \r
217   extractCallback->PrepareOperation(askMode);\r
218 \r
219   CDummyOutStream *outStreamSpec = new CDummyOutStream;\r
220   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);\r
221   outStreamSpec->SetStream(realOutStream);\r
222   outStreamSpec->Init();\r
223   realOutStream.Release();\r
224 \r
225   CLocalProgress *lps = new CLocalProgress;\r
226   CMyComPtr<ICompressProgressInfo> progress = lps;\r
227   lps->Init(extractCallback, false);\r
228   \r
229   RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));\r
230   CInBuffer s;\r
231   if (!s.Create(1 << 20))\r
232     return E_OUTOFMEMORY;\r
233   s.SetStream(_stream);\r
234   s.Init();\r
235   Byte buffer[kHeaderSize];\r
236   Int32 opRes = NExtract::NOperationResult::kDataError;\r
237   if (s.ReadBytes(buffer, kHeaderSize) == kHeaderSize)\r
238   {\r
239     HRESULT result = MslzDec(s, outStream, _size, progress);\r
240     if (result == S_OK)\r
241       opRes = NExtract::NOperationResult::kOK;\r
242     else if (result != S_FALSE)\r
243       return result;\r
244   }\r
245   outStream.Release();\r
246   return extractCallback->SetOperationResult(opRes);\r
247   COM_TRY_END\r
248 }\r
249 \r
250 static IInArchive *CreateArc() { return new CHandler; }\r
251 \r
252 static CArcInfo g_ArcInfo =\r
253   { L"MsLZ", L"", 0, 0xD5, MSLZ_SIGNATURE, kSignatureSize, false, CreateArc, 0 };\r
254 \r
255 REGISTER_ARC(Mslz)\r
256 \r
257 }}\r