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(¤tTotalPacked))
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