5 #include "../../../C/CpuArch.h"
\r
7 #include "Common/ComTry.h"
\r
8 #include "Common/MyString.h"
\r
10 #include "Windows/PropVariant.h"
\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
17 #include "Common/DummyOutStream.h"
\r
19 namespace NArchive {
\r
24 public CMyUnknownImp
\r
26 CMyComPtr<IInStream> _stream;
\r
31 MY_UNKNOWN_IMP1(IInArchive)
\r
32 INTERFACE_IInArchive(;)
\r
35 STATPROPSTG kProps[] =
\r
37 { NULL, kpidPath, VT_BSTR},
\r
38 { NULL, kpidSize, VT_UI8},
\r
39 { NULL, kpidPackSize, VT_UI8},
\r
42 IMP_IInArchive_Props
\r
43 IMP_IInArchive_ArcProps_NO
\r
45 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
\r
51 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
\r
54 NWindows::NCOM::CPropVariant prop;
\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
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
72 static const wchar_t *g_Exts[] =
\r
80 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPosition */,
\r
81 IArchiveOpenCallback *callback)
\r
86 Byte buffer[kHeaderSize];
\r
87 RINOK(ReadStream_FALSE(stream, buffer, kHeaderSize));
\r
88 if (memcmp(buffer, signature, kSignatureSize) != 0)
\r
90 _size = GetUi32(buffer + 10);
\r
91 if (_size > 0xFFFFFFE0)
\r
93 RINOK(stream->Seek(0, STREAM_SEEK_END, &_packSize));
\r
97 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
\r
98 callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
\r
99 if (openVolumeCallback)
\r
101 NWindows::NCOM::CPropVariant prop;
\r
102 if (openVolumeCallback->GetProperty(kpidName, &prop) == S_OK && prop.vt == VT_BSTR)
\r
104 UString baseName = prop.bstrVal;
\r
105 if (!baseName.IsEmpty() && baseName.Back() == L'_')
\r
107 baseName.DeleteBack();
\r
108 Byte replaceByte = buffer[kSignatureSize];
\r
109 if (replaceByte == 0)
\r
111 for (int i = 0; i < sizeof(g_Exts) / sizeof(g_Exts[0]); i++)
\r
113 UString s = g_Exts[i];
\r
114 int len = s.Length();
\r
115 Byte b = (Byte)s.Back();
\r
117 if (baseName.Length() >= len &&
\r
118 baseName[baseName.Length() - len] == '.' &&
\r
119 s.CompareNoCase(baseName.Right(len - 1)) == 0)
\r
126 if (replaceByte >= 0x20 && replaceByte < 0x80)
\r
127 _name = baseName + (wchar_t)replaceByte;
\r
138 STDMETHODIMP CHandler::Close()
\r
145 // MslzDec is modified LZSS algorithm of Haruhiko Okumura:
\r
146 // maxLen = 18; Okumura
\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
155 static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UInt32 unpackSize, ICompressProgressInfo *progress)
\r
157 const unsigned kBufSize = (1 << 12);
\r
158 const unsigned kMask = kBufSize - 1;
\r
159 Byte buf[kBufSize];
\r
161 memset(buf, ' ', kBufSize);
\r
162 while (dest < unpackSize)
\r
165 if (!inStream.ReadByte(b))
\r
167 for (unsigned mask = (unsigned)b | 0x100; mask > 1 && dest < unpackSize; mask >>= 1)
\r
169 if (!inStream.ReadByte(b))
\r
173 buf[dest++ & kMask] = b;
\r
179 if (!inStream.ReadByte(b1))
\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
188 buf[dest++ & kMask] = buf[src++ & kMask];
\r
191 while (--len != 0);
\r
195 return WriteStream(outStream, buf, dest & kMask);
\r
198 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
\r
199 Int32 testMode, IArchiveExtractCallback *extractCallback)
\r
204 if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
\r
205 return E_INVALIDARG;
\r
207 extractCallback->SetTotal(_size);
\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
217 extractCallback->PrepareOperation(askMode);
\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
225 CLocalProgress *lps = new CLocalProgress;
\r
226 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
227 lps->Init(extractCallback, false);
\r
229 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
\r
231 if (!s.Create(1 << 20))
\r
232 return E_OUTOFMEMORY;
\r
233 s.SetStream(_stream);
\r
235 Byte buffer[kHeaderSize];
\r
236 Int32 opRes = NExtract::NOperationResult::kDataError;
\r
237 if (s.ReadBytes(buffer, kHeaderSize) == kHeaderSize)
\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
245 outStream.Release();
\r
246 return extractCallback->SetOperationResult(opRes);
\r
250 static IInArchive *CreateArc() { return new CHandler; }
\r
252 static CArcInfo g_ArcInfo =
\r
253 { L"MsLZ", L"", 0, 0xD5, MSLZ_SIGNATURE, kSignatureSize, false, CreateArc, 0 };
\r