xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/PpmdHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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(NULL) {}
~CBufNArchive::NPpmd::CBuf37   ~CBuf() { ::MidFree(Buf); }
AllocNArchive::NPpmd::CBuf38   bool Alloc()
39   {
40     if (!Buf)
41       Buf = (Byte *)::MidAlloc(kBufSize);
42     return (Buf != NULL);
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
63   {
64     return (Ver == 7 && Order >= PPMD7_MIN_ORDER)
65         || (Ver == 8 && Order >= PPMD8_MIN_ORDER && Restor < PPMD8_RESTORE_METHOD_UNSUPPPORTED);
66   }
67 };
68 
ReadHeader(ISequentialInStream * s,UInt32 & headerSize)69 HRESULT CItem::ReadHeader(ISequentialInStream *s, UInt32 &headerSize)
70 {
71   Byte h[kHeaderSize];
72   RINOK(ReadStream_FALSE(s, h, kHeaderSize))
73   if (GetUi32(h) != kSignature)
74     return S_FALSE;
75   Attrib = GetUi32(h + 4);
76   Time = GetUi32(h + 12);
77   const unsigned info = GetUi16(h + 8);
78   Order = (info & 0xF) + 1;
79   MemInMB = ((info >> 4) & 0xFF) + 1;
80   Ver = info >> 12;
81 
82   if (Ver < 6 || Ver > 11) return S_FALSE;
83 
84   UInt32 nameLen = GetUi16(h + 10);
85   Restor = nameLen >> 14;
86   if (Restor > 2)
87     return S_FALSE;
88   if (Ver >= kNewHeaderVer)
89     nameLen &= 0x3FFF;
90   if (nameLen > (1 << 9))
91     return S_FALSE;
92   char *name = Name.GetBuf(nameLen);
93   const HRESULT res = ReadStream_FALSE(s, name, nameLen);
94   Name.ReleaseBuf_CalcLen(nameLen);
95   headerSize = kHeaderSize + nameLen;
96   return res;
97 }
98 
99 
100 Z7_CLASS_IMP_CHandler_IInArchive_1(
101   IArchiveOpenSeq
102 )
103   CItem _item;
104   UInt32 _headerSize;
105   bool _packSize_Defined;
106   UInt64 _packSize;
107   CMyComPtr<ISequentialInStream> _stream;
108 
109   void GetVersion(NCOM::CPropVariant &prop);
110 };
111 
112 static const Byte kProps[] =
113 {
114   kpidPath,
115   kpidMTime,
116   kpidAttrib,
117   kpidMethod
118 };
119 
120 static const Byte kArcProps[] =
121 {
122   kpidMethod
123 };
124 
125 IMP_IInArchive_Props
126 IMP_IInArchive_ArcProps
127 
GetVersion(NCOM::CPropVariant & prop)128 void CHandler::GetVersion(NCOM::CPropVariant &prop)
129 {
130   AString s ("PPMd");
131   s.Add_Char((char)('A' + _item.Ver));
132   s += ":o";
133   s.Add_UInt32(_item.Order);
134   s += ":mem";
135   s.Add_UInt32(_item.MemInMB);
136   s.Add_Char('m');
137   if (_item.Ver >= kNewHeaderVer && _item.Restor != 0)
138   {
139     s += ":r";
140     s.Add_UInt32(_item.Restor);
141   }
142   prop = s;
143 }
144 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))145 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
146 {
147   NCOM::CPropVariant prop;
148   switch (propID)
149   {
150     case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
151     case kpidMethod: GetVersion(prop); break;
152   }
153   prop.Detach(value);
154   return S_OK;
155 }
156 
157 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))158 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
159 {
160   *numItems = 1;
161   return S_OK;
162 }
163 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32,PROPID propID,PROPVARIANT * value))164 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
165 {
166   COM_TRY_BEGIN
167   NCOM::CPropVariant prop;
168   switch (propID)
169   {
170     case kpidPath: prop = MultiByteToUnicodeString(_item.Name, CP_ACP); break;
171     case kpidMTime:
172     {
173       // time can be in Unix format ???
174       FILETIME utc;
175       if (NTime::DosTime_To_FileTime(_item.Time, utc))
176         prop = utc;
177       break;
178     }
179     case kpidAttrib: prop = _item.Attrib; break;
180     case kpidPackSize: if (_packSize_Defined) prop = _packSize; break;
181     case kpidMethod: GetVersion(prop); break;
182   }
183   prop.Detach(value);
184   return S_OK;
185   COM_TRY_END
186 }
187 
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback *))188 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *))
189 {
190   return OpenSeq(stream);
191 }
192 
Z7_COM7F_IMF(CHandler::OpenSeq (ISequentialInStream * stream))193 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
194 {
195   COM_TRY_BEGIN
196   HRESULT res;
197   try
198   {
199     Close();
200     res = _item.ReadHeader(stream, _headerSize);
201   }
202   catch(...) { res = S_FALSE; }
203   if (res == S_OK)
204     _stream = stream;
205   else
206     Close();
207   return res;
208   COM_TRY_END
209 }
210 
Z7_COM7F_IMF(CHandler::Close ())211 Z7_COM7F_IMF(CHandler::Close())
212 {
213   _packSize = 0;
214   _packSize_Defined = false;
215   _stream.Release();
216   return S_OK;
217 }
218 
219 
220 
221 struct CPpmdCpp
222 {
223   unsigned Ver;
224   CPpmd7 _ppmd7;
225   CPpmd8 _ppmd8;
226 
CPpmdCppNArchive::CPpmdCpp227   CPpmdCpp(unsigned version)
228   {
229     Ver = version;
230     Ppmd7_Construct(&_ppmd7);
231     Ppmd8_Construct(&_ppmd8);
232   }
233 
~CPpmdCppNArchive::CPpmdCpp234   ~CPpmdCpp()
235   {
236     Ppmd7_Free(&_ppmd7, &g_BigAlloc);
237     Ppmd8_Free(&_ppmd8, &g_BigAlloc);
238   }
239 
AllocNArchive::CPpmdCpp240   bool Alloc(UInt32 memInMB)
241   {
242     memInMB <<= 20;
243     if (Ver == 7)
244       return Ppmd7_Alloc(&_ppmd7, memInMB, &g_BigAlloc) != 0;
245     return Ppmd8_Alloc(&_ppmd8, memInMB, &g_BigAlloc) != 0;
246   }
247 
InitNArchive::CPpmdCpp248   void Init(unsigned order, unsigned restor)
249   {
250     if (Ver == 7)
251       Ppmd7_Init(&_ppmd7, order);
252     else
253       Ppmd8_Init(&_ppmd8, order, restor);
254   }
255 
InitRcNArchive::CPpmdCpp256   bool InitRc(CByteInBufWrap *inStream)
257   {
258     if (Ver == 7)
259     {
260       _ppmd7.rc.dec.Stream = &inStream->vt;
261       return (Ppmd7a_RangeDec_Init(&_ppmd7.rc.dec) != 0);
262     }
263     else
264     {
265       _ppmd8.Stream.In = &inStream->vt;
266       return Ppmd8_Init_RangeDec(&_ppmd8) != 0;
267     }
268   }
269 
IsFinishedOKNArchive::CPpmdCpp270   bool IsFinishedOK()
271   {
272     if (Ver == 7)
273       return Ppmd7z_RangeDec_IsFinishedOK(&_ppmd7.rc.dec);
274     return Ppmd8_RangeDec_IsFinishedOK(&_ppmd8);
275   }
276 };
277 
278 
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))279 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
280     Int32 testMode, IArchiveExtractCallback *extractCallback))
281 {
282   if (numItems == 0)
283     return S_OK;
284   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
285     return E_INVALIDARG;
286 
287   // extractCallback->SetTotal(_packSize);
288   UInt64 currentTotalPacked = 0;
289   RINOK(extractCallback->SetCompleted(&currentTotalPacked))
290   Int32 opRes;
291 {
292   CMyComPtr<ISequentialOutStream> realOutStream;
293   const 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   RINOK(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   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
312   lps->Init(extractCallback, true);
313 
314   CPpmdCpp ppmd(_item.Ver);
315   if (!ppmd.Alloc(_item.MemInMB))
316     return E_OUTOFMEMORY;
317 
318   opRes = NExtract::NOperationResult::kUnsupportedMethod;
319 
320   if (_item.IsSupported())
321   {
322     opRes = NExtract::NOperationResult::kDataError;
323 
324     ppmd.Init(_item.Order, _item.Restor);
325     inBuf.Init();
326     UInt64 outSize = 0;
327 
328     if (ppmd.InitRc(&inBuf) && !inBuf.Extra && inBuf.Res == S_OK)
329     for (;;)
330     {
331       lps->InSize = _packSize = inBuf.GetProcessed();
332       lps->OutSize = outSize;
333       RINOK(lps->SetCur())
334 
335       size_t i;
336       int sym = 0;
337 
338       Byte *buf = outBuf.Buf;
339       if (ppmd.Ver == 7)
340       {
341         for (i = 0; i < kBufSize; i++)
342         {
343           sym = Ppmd7a_DecodeSymbol(&ppmd._ppmd7);
344           if (inBuf.Extra || sym < 0)
345             break;
346           buf[i] = (Byte)sym;
347         }
348       }
349       else
350       {
351         for (i = 0; i < kBufSize; i++)
352         {
353           sym = Ppmd8_DecodeSymbol(&ppmd._ppmd8);
354           if (inBuf.Extra || sym < 0)
355             break;
356           buf[i] = (Byte)sym;
357         }
358       }
359 
360       outSize += i;
361       _packSize = _headerSize + inBuf.GetProcessed();
362       _packSize_Defined = true;
363       if (realOutStream)
364       {
365         RINOK(WriteStream(realOutStream, outBuf.Buf, i))
366       }
367 
368       if (inBuf.Extra)
369       {
370         opRes = NExtract::NOperationResult::kUnexpectedEnd;
371         break;
372       }
373 
374       if (sym < 0)
375       {
376         if (sym == -1 && ppmd.IsFinishedOK())
377           opRes = NExtract::NOperationResult::kOK;
378         break;
379       }
380     }
381 
382     RINOK(inBuf.Res)
383   }
384 }
385   return extractCallback->SetOperationResult(opRes);
386 }
387 
388 
389 static const Byte k_Signature[] = { 0x8F, 0xAF, 0xAC, 0x84 };
390 
391 REGISTER_ARC_I(
392   "Ppmd", "pmd", NULL, 0xD,
393   k_Signature,
394   0,
395   0,
396   NULL)
397 
398 }}
399