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(&currentTotalPacked));
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