1 /* PpmdHandler.cpp -- PPMd format handler
2 2020 : Igor Pavlov : Public domain
3 This code is based on:
4 PPMd var.H (2001) / var.I (2002): Dmitry Shkarin : Public domain
5 Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
6
7 #include "StdAfx.h"
8
9 #include "../../../C/CpuArch.h"
10 #include "../../../C/Alloc.h"
11 #include "../../../C/Ppmd7.h"
12 #include "../../../C/Ppmd8.h"
13
14 #include "../../Common/ComTry.h"
15 #include "../../Common/StringConvert.h"
16
17 #include "../../Windows/PropVariant.h"
18 #include "../../Windows/TimeUtils.h"
19
20 #include "../Common/CWrappers.h"
21 #include "../Common/ProgressUtils.h"
22 #include "../Common/RegisterArc.h"
23 #include "../Common/StreamUtils.h"
24
25 using namespace NWindows;
26
27 namespace NArchive {
28 namespace NPpmd {
29
30 static const UInt32 kBufSize = (1 << 20);
31
32 struct CBuf
33 {
34 Byte *Buf;
35
CBufNArchive::NPpmd::CBuf36 CBuf(): Buf(0) {}
~CBufNArchive::NPpmd::CBuf37 ~CBuf() { ::MidFree(Buf); }
AllocNArchive::NPpmd::CBuf38 bool Alloc()
39 {
40 if (!Buf)
41 Buf = (Byte *)::MidAlloc(kBufSize);
42 return (Buf != 0);
43 }
44 };
45
46 static const UInt32 kHeaderSize = 16;
47 static const UInt32 kSignature = 0x84ACAF8F;
48 static const unsigned kNewHeaderVer = 8;
49
50 struct CItem
51 {
52 UInt32 Attrib;
53 UInt32 Time;
54 AString Name;
55
56 unsigned Order;
57 unsigned MemInMB;
58 unsigned Ver;
59 unsigned Restor;
60
61 HRESULT ReadHeader(ISequentialInStream *s, UInt32 &headerSize);
IsSupportedNArchive::NPpmd::CItem62 bool IsSupported() const { return Ver == 7 || (Ver == 8 && Restor < PPMD8_RESTORE_METHOD_UNSUPPPORTED); }
63 };
64
ReadHeader(ISequentialInStream * s,UInt32 & headerSize)65 HRESULT CItem::ReadHeader(ISequentialInStream *s, UInt32 &headerSize)
66 {
67 Byte h[kHeaderSize];
68 RINOK(ReadStream_FALSE(s, h, kHeaderSize));
69 if (GetUi32(h) != kSignature)
70 return S_FALSE;
71 Attrib = GetUi32(h + 4);
72 Time = GetUi32(h + 12);
73 unsigned info = GetUi16(h + 8);
74 Order = (info & 0xF) + 1;
75 MemInMB = ((info >> 4) & 0xFF) + 1;
76 Ver = info >> 12;
77
78 if (Ver < 6 || Ver > 11) return S_FALSE;
79
80 UInt32 nameLen = GetUi16(h + 10);
81 Restor = nameLen >> 14;
82 if (Restor > 2)
83 return S_FALSE;
84 if (Ver >= kNewHeaderVer)
85 nameLen &= 0x3FFF;
86 if (nameLen > (1 << 9))
87 return S_FALSE;
88 char *name = Name.GetBuf(nameLen);
89 HRESULT res = ReadStream_FALSE(s, name, nameLen);
90 Name.ReleaseBuf_CalcLen(nameLen);
91 headerSize = kHeaderSize + nameLen;
92 return res;
93 }
94
95 class CHandler:
96 public IInArchive,
97 public IArchiveOpenSeq,
98 public CMyUnknownImp
99 {
100 CItem _item;
101 UInt32 _headerSize;
102 bool _packSize_Defined;
103 UInt64 _packSize;
104 CMyComPtr<ISequentialInStream> _stream;
105
106 void GetVersion(NCOM::CPropVariant &prop);
107
108 public:
109 MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq)
110 INTERFACE_IInArchive(;)
111 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
112 };
113
114 static const Byte kProps[] =
115 {
116 kpidPath,
117 kpidMTime,
118 kpidAttrib,
119 kpidMethod
120 };
121
122 static const Byte kArcProps[] =
123 {
124 kpidMethod
125 };
126
127 IMP_IInArchive_Props
128 IMP_IInArchive_ArcProps
129
GetVersion(NCOM::CPropVariant & prop)130 void CHandler::GetVersion(NCOM::CPropVariant &prop)
131 {
132 AString s ("PPMd");
133 s += (char)('A' + _item.Ver);
134 s += ":o";
135 s.Add_UInt32(_item.Order);
136 s += ":mem";
137 s.Add_UInt32(_item.MemInMB);
138 s += 'm';
139 if (_item.Ver >= kNewHeaderVer && _item.Restor != 0)
140 {
141 s += ":r";
142 s.Add_UInt32(_item.Restor);
143 }
144 prop = s;
145 }
146
GetArchiveProperty(PROPID propID,PROPVARIANT * value)147 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
148 {
149 NCOM::CPropVariant prop;
150 switch (propID)
151 {
152 case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
153 case kpidMethod: GetVersion(prop); break;
154 }
155 prop.Detach(value);
156 return S_OK;
157 }
158
159
GetNumberOfItems(UInt32 * numItems)160 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
161 {
162 *numItems = 1;
163 return S_OK;
164 }
165
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)166 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
167 {
168 COM_TRY_BEGIN
169 NCOM::CPropVariant prop;
170 switch (propID)
171 {
172 case kpidPath: prop = MultiByteToUnicodeString(_item.Name, CP_ACP); break;
173 case kpidMTime:
174 {
175 // time can be in Unix format ???
176 FILETIME utc;
177 if (NTime::DosTimeToFileTime(_item.Time, utc))
178 prop = utc;
179 break;
180 }
181 case kpidAttrib: prop = _item.Attrib; break;
182 case kpidPackSize: if (_packSize_Defined) prop = _packSize; break;
183 case kpidMethod: GetVersion(prop); break;
184 }
185 prop.Detach(value);
186 return S_OK;
187 COM_TRY_END
188 }
189
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)190 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)
191 {
192 return OpenSeq(stream);
193 }
194
OpenSeq(ISequentialInStream * stream)195 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
196 {
197 COM_TRY_BEGIN
198 HRESULT res;
199 try
200 {
201 Close();
202 res = _item.ReadHeader(stream, _headerSize);
203 }
204 catch(...) { res = S_FALSE; }
205 if (res == S_OK)
206 _stream = stream;
207 else
208 Close();
209 return res;
210 COM_TRY_END
211 }
212
Close()213 STDMETHODIMP CHandler::Close()
214 {
215 _packSize = 0;
216 _packSize_Defined = false;
217 _stream.Release();
218 return S_OK;
219 }
220
221
222
223 struct CPpmdCpp
224 {
225 unsigned Ver;
226 CPpmd7 _ppmd7;
227 CPpmd8 _ppmd8;
228
CPpmdCppNArchive::NPpmd::CPpmdCpp229 CPpmdCpp(unsigned version)
230 {
231 Ver = version;
232 Ppmd7_Construct(&_ppmd7);
233 Ppmd8_Construct(&_ppmd8);
234 }
235
~CPpmdCppNArchive::NPpmd::CPpmdCpp236 ~CPpmdCpp()
237 {
238 Ppmd7_Free(&_ppmd7, &g_BigAlloc);
239 Ppmd8_Free(&_ppmd8, &g_BigAlloc);
240 }
241
AllocNArchive::NPpmd::CPpmdCpp242 bool Alloc(UInt32 memInMB)
243 {
244 memInMB <<= 20;
245 if (Ver == 7)
246 return Ppmd7_Alloc(&_ppmd7, memInMB, &g_BigAlloc) != 0;
247 return Ppmd8_Alloc(&_ppmd8, memInMB, &g_BigAlloc) != 0;
248 }
249
InitNArchive::NPpmd::CPpmdCpp250 void Init(unsigned order, unsigned restor)
251 {
252 if (Ver == 7)
253 Ppmd7_Init(&_ppmd7, order);
254 else
255 Ppmd8_Init(&_ppmd8, order, restor);;
256 }
257
InitRcNArchive::NPpmd::CPpmdCpp258 bool InitRc(CByteInBufWrap *inStream)
259 {
260 if (Ver == 7)
261 {
262 _ppmd7.rc.dec.Stream = &inStream->vt;
263 return (Ppmd7a_RangeDec_Init(&_ppmd7.rc.dec) != 0);
264 }
265 else
266 {
267 _ppmd8.Stream.In = &inStream->vt;
268 return Ppmd8_Init_RangeDec(&_ppmd8) != 0;
269 }
270 }
271
IsFinishedOKNArchive::NPpmd::CPpmdCpp272 bool IsFinishedOK()
273 {
274 if (Ver == 7)
275 return Ppmd7z_RangeDec_IsFinishedOK(&_ppmd7.rc.dec);
276 return Ppmd8_RangeDec_IsFinishedOK(&_ppmd8);
277 }
278 };
279
280
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)281 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
282 Int32 testMode, IArchiveExtractCallback *extractCallback)
283 {
284 if (numItems == 0)
285 return S_OK;
286 if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
287 return E_INVALIDARG;
288
289 // extractCallback->SetTotal(_packSize);
290 UInt64 currentTotalPacked = 0;
291 RINOK(extractCallback->SetCompleted(¤tTotalPacked));
292 CMyComPtr<ISequentialOutStream> realOutStream;
293 Int32 askMode = testMode ?
294 NExtract::NAskMode::kTest :
295 NExtract::NAskMode::kExtract;
296 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
297 if (!testMode && !realOutStream)
298 return S_OK;
299
300 extractCallback->PrepareOperation(askMode);
301
302 CByteInBufWrap inBuf;
303 if (!inBuf.Alloc(1 << 20))
304 return E_OUTOFMEMORY;
305 inBuf.Stream = _stream;
306
307 CBuf outBuf;
308 if (!outBuf.Alloc())
309 return E_OUTOFMEMORY;
310
311 CLocalProgress *lps = new CLocalProgress;
312 CMyComPtr<ICompressProgressInfo> progress = lps;
313 lps->Init(extractCallback, true);
314
315 CPpmdCpp ppmd(_item.Ver);
316 if (!ppmd.Alloc(_item.MemInMB))
317 return E_OUTOFMEMORY;
318
319 Int32 opRes = NExtract::NOperationResult::kUnsupportedMethod;
320
321 if (_item.IsSupported())
322 {
323 opRes = NExtract::NOperationResult::kDataError;
324
325 ppmd.Init(_item.Order, _item.Restor);
326 inBuf.Init();
327 UInt64 outSize = 0;
328
329 if (ppmd.InitRc(&inBuf) && !inBuf.Extra && inBuf.Res == S_OK)
330 for (;;)
331 {
332 lps->InSize = _packSize = inBuf.GetProcessed();
333 lps->OutSize = outSize;
334 RINOK(lps->SetCur());
335
336 size_t i;
337 int sym = 0;
338
339 Byte *buf = outBuf.Buf;
340 if (ppmd.Ver == 7)
341 {
342 for (i = 0; i < kBufSize; i++)
343 {
344 sym = Ppmd7a_DecodeSymbol(&ppmd._ppmd7);
345 if (inBuf.Extra || sym < 0)
346 break;
347 buf[i] = (Byte)sym;
348 }
349 }
350 else
351 {
352 for (i = 0; i < kBufSize; i++)
353 {
354 sym = Ppmd8_DecodeSymbol(&ppmd._ppmd8);
355 if (inBuf.Extra || sym < 0)
356 break;
357 buf[i] = (Byte)sym;
358 }
359 }
360
361 outSize += i;
362 _packSize = _headerSize + inBuf.GetProcessed();
363 _packSize_Defined = true;
364 if (realOutStream)
365 {
366 RINOK(WriteStream(realOutStream, outBuf.Buf, i));
367 }
368
369 if (inBuf.Extra)
370 {
371 opRes = NExtract::NOperationResult::kUnexpectedEnd;
372 break;
373 }
374
375 if (sym < 0)
376 {
377 if (sym == -1 && ppmd.IsFinishedOK())
378 opRes = NExtract::NOperationResult::kOK;
379 break;
380 }
381 }
382
383 RINOK(inBuf.Res);
384 }
385
386 realOutStream.Release();
387 return extractCallback->SetOperationResult(opRes);
388 }
389
390
391 static const Byte k_Signature[] = { 0x8F, 0xAF, 0xAC, 0x84 };
392
393 REGISTER_ARC_I(
394 "Ppmd", "pmd", 0, 0xD,
395 k_Signature,
396 0,
397 0,
398 NULL)
399
400 }}
401