xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/SwfHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // SwfHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/IntToString.h"
9 #include "../../Common/MyBuffer.h"
10 #include "../../Common/MyString.h"
11 
12 #include "../../Windows/PropVariant.h"
13 #include "../../Windows/PropVariantUtils.h"
14 
15 #include "../Common/InBuffer.h"
16 #include "../Common/LimitedStreams.h"
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamObjects.h"
20 #include "../Common/StreamUtils.h"
21 
22 #include "../Compress/CopyCoder.h"
23 #include "../Compress/LzmaDecoder.h"
24 #include "../Compress/ZlibDecoder.h"
25 
26 #include "Common/DummyOutStream.h"
27 
28 // #define Z7_SWF_UPDATE
29 
30 #ifdef Z7_SWF_UPDATE
31 
32 #include "../Compress/LzmaEncoder.h"
33 #include "../Compress/ZlibEncoder.h"
34 
35 #include "Common/HandlerOut.h"
36 
37 #endif
38 
39 using namespace NWindows;
40 
41 namespace NArchive {
42 
43 static const UInt32 kFileSizeMax = (UInt32)1 << 29;
44 
45 namespace NSwfc {
46 
47 static const unsigned kHeaderBaseSize = 8;
48 static const unsigned kHeaderLzmaSize = 17;
49 
50 static const Byte SWF_UNCOMPRESSED = 'F';
51 static const Byte SWF_COMPRESSED_ZLIB = 'C';
52 static const Byte SWF_COMPRESSED_LZMA = 'Z';
53 
54 static const Byte SWF_MIN_COMPRESSED_ZLIB_VER = 6;
55 static const Byte SWF_MIN_COMPRESSED_LZMA_VER = 13;
56 
57 static const Byte kVerLim = 64;
58 
IsArc_Swf(const Byte * p,size_t size)59 API_FUNC_static_IsArc IsArc_Swf(const Byte *p, size_t size)
60 {
61   if (size < kHeaderBaseSize)
62     return k_IsArc_Res_NEED_MORE;
63   if (p[0] != SWF_UNCOMPRESSED ||
64       p[1] != 'W' ||
65       p[2] != 'S' ||
66       p[3] >= kVerLim)
67     return k_IsArc_Res_NO;
68   UInt32 uncompressedSize = GetUi32(p + 4);
69   if (uncompressedSize > kFileSizeMax)
70     return k_IsArc_Res_NO;
71   return k_IsArc_Res_YES;
72 }
73 }
74 
IsArc_Swfc(const Byte * p,size_t size)75 API_FUNC_static_IsArc IsArc_Swfc(const Byte *p, size_t size)
76 {
77   if (size < kHeaderBaseSize + 2 + 1) // 2 + 1 (for zlib check)
78     return k_IsArc_Res_NEED_MORE;
79   if ((p[0] != SWF_COMPRESSED_ZLIB &&
80       p[0] != SWF_COMPRESSED_LZMA) ||
81       p[1] != 'W' ||
82       p[2] != 'S' ||
83       p[3] >= kVerLim)
84     return k_IsArc_Res_NO;
85   UInt32 uncompressedSize = GetUi32(p + 4);
86   if (uncompressedSize > kFileSizeMax)
87     return k_IsArc_Res_NO;
88 
89   if (p[0] == SWF_COMPRESSED_ZLIB)
90   {
91     if (!NCompress::NZlib::IsZlib_3bytes(p + 8))
92       return k_IsArc_Res_NO;
93   }
94   else
95   {
96     if (size < kHeaderLzmaSize + 2)
97       return k_IsArc_Res_NEED_MORE;
98     if (p[kHeaderLzmaSize] != 0 ||
99         (p[kHeaderLzmaSize + 1] & 0x80) != 0)
100       return k_IsArc_Res_NO;
101     UInt32 lzmaPackSize = GetUi32(p + 8);
102     UInt32 lzmaProp = p[12];
103     UInt32 lzmaDicSize = GetUi32(p + 13);
104     if (lzmaProp > 5 * 5 * 9 ||
105         lzmaDicSize > ((UInt32)1 << 28) ||
106         lzmaPackSize < 5 ||
107         lzmaPackSize > ((UInt32)1 << 28))
108       return k_IsArc_Res_NO;
109   }
110 
111   return k_IsArc_Res_YES;
112 }
113 }
114 
115 struct CItem
116 {
117   Byte Buf[kHeaderLzmaSize];
118   unsigned HeaderSize;
119 
GetSizeCItem120   UInt32 GetSize() const { return GetUi32(Buf + 4); }
GetLzmaPackSizeCItem121   UInt32 GetLzmaPackSize() const { return GetUi32(Buf + 8); }
GetLzmaDicSizeCItem122   UInt32 GetLzmaDicSize() const { return GetUi32(Buf + 13); }
123 
IsSwfCItem124   bool IsSwf() const { return (Buf[1] == 'W' && Buf[2] == 'S' && Buf[3] < kVerLim); }
IsUncompressedCItem125   bool IsUncompressed() const { return Buf[0] == SWF_UNCOMPRESSED; }
IsZlibCItem126   bool IsZlib() const { return Buf[0] == SWF_COMPRESSED_ZLIB; }
IsLzmaCItem127   bool IsLzma() const { return Buf[0] == SWF_COMPRESSED_LZMA; }
128 
MakeUncompressedCItem129   void MakeUncompressed()
130   {
131     Buf[0] = SWF_UNCOMPRESSED;
132     HeaderSize = kHeaderBaseSize;
133   }
MakeZlibCItem134   void MakeZlib()
135   {
136     Buf[0] = SWF_COMPRESSED_ZLIB;
137     if (Buf[3] < SWF_MIN_COMPRESSED_ZLIB_VER)
138       Buf[3] = SWF_MIN_COMPRESSED_ZLIB_VER;
139   }
MakeLzmaCItem140   void MakeLzma(UInt32 packSize)
141   {
142     Buf[0] = SWF_COMPRESSED_LZMA;
143     if (Buf[3] < SWF_MIN_COMPRESSED_LZMA_VER)
144       Buf[3] = SWF_MIN_COMPRESSED_LZMA_VER;
145     SetUi32(Buf + 8, packSize)
146     HeaderSize = kHeaderLzmaSize;
147   }
148 
ReadHeaderCItem149   HRESULT ReadHeader(ISequentialInStream *stream)
150   {
151     HeaderSize = kHeaderBaseSize;
152     return ReadStream_FALSE(stream, Buf, kHeaderBaseSize);
153   }
WriteHeaderCItem154   HRESULT WriteHeader(ISequentialOutStream *stream)
155   {
156     return WriteStream(stream, Buf, HeaderSize);
157   }
158 };
159 
160 
161 Z7_class_CHandler_final:
162   public IInArchive,
163   public IArchiveOpenSeq,
164  #ifdef Z7_SWF_UPDATE
165   public IOutArchive,
166   public ISetProperties,
167  #endif
168   public CMyUnknownImp
169 {
170  #ifdef Z7_SWF_UPDATE
171   Z7_IFACES_IMP_UNK_4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties)
172  #else
173   Z7_IFACES_IMP_UNK_2(IInArchive, IArchiveOpenSeq)
174  #endif
175 
176   CItem _item;
177   UInt64 _packSize;
178   bool _packSizeDefined;
179   CMyComPtr<ISequentialInStream> _seqStream;
180   CMyComPtr<IInStream> _stream;
181 
182 #ifdef Z7_SWF_UPDATE
183   CSingleMethodProps _props;
184   bool _lzmaMode;
185 #endif
186 
187 public:
188  #ifdef Z7_SWF_UPDATE
189   CHandler(): _lzmaMode(false) {}
190  #endif
191 };
192 
193 static const Byte kProps[] =
194 {
195   kpidSize,
196   kpidPackSize,
197   kpidMethod
198 };
199 
200 IMP_IInArchive_Props
201 IMP_IInArchive_ArcProps_NO_Table
202 
203 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
204 {
205   NCOM::CPropVariant prop;
206   switch (propID)
207   {
208     case kpidPhySize: if (_packSizeDefined) prop = _item.HeaderSize + _packSize; break;
209     case kpidIsNotArcType: prop = true; break;
210   }
211   prop.Detach(value);
212   return S_OK;
213 }
214 
215 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
216 {
217   *numItems = 1;
218   return S_OK;
219 }
220 
221 static void DicSizeToString(char *s, UInt32 val)
222 {
223   char c = 0;
224   unsigned i;
225   for (i = 0; i < 32; i++)
226     if (((UInt32)1 << i) == val)
227     {
228       val = i;
229       break;
230     }
231   if (i == 32)
232   {
233     c = 'b';
234     if      ((val & ((1 << 20) - 1)) == 0) { val >>= 20; c = 'm'; }
235     else if ((val & ((1 << 10) - 1)) == 0) { val >>= 10; c = 'k'; }
236   }
237   ::ConvertUInt32ToString(val, s);
238   unsigned pos = MyStringLen(s);
239   s[pos++] = c;
240   s[pos] = 0;
241 }
242 
243 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
244 {
245   NWindows::NCOM::CPropVariant prop;
246   switch (propID)
247   {
248     case kpidSize: prop = (UInt64)_item.GetSize(); break;
249     case kpidPackSize: if (_packSizeDefined) prop = _item.HeaderSize + _packSize; break;
250     case kpidMethod:
251     {
252       char s[32];
253       if (_item.IsZlib())
254         MyStringCopy(s, "zlib");
255       else
256       {
257         MyStringCopy(s, "LZMA:");
258         DicSizeToString(s + 5, _item.GetLzmaDicSize());
259       }
260       prop = s;
261       break;
262     }
263   }
264   prop.Detach(value);
265   return S_OK;
266 }
267 
268 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *))
269 {
270   RINOK(OpenSeq(stream))
271   _stream = stream;
272   return S_OK;
273 }
274 
275 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
276 {
277   Close();
278   RINOK(_item.ReadHeader(stream))
279   if (!_item.IsSwf())
280     return S_FALSE;
281   if (_item.IsLzma())
282   {
283     RINOK(ReadStream_FALSE(stream, _item.Buf + kHeaderBaseSize, kHeaderLzmaSize - kHeaderBaseSize))
284     _item.HeaderSize = kHeaderLzmaSize;
285     _packSize = _item.GetLzmaPackSize();
286     _packSizeDefined = true;
287   }
288   else if (!_item.IsZlib())
289     return S_FALSE;
290   if (_item.GetSize() < _item.HeaderSize)
291     return S_FALSE;
292   _seqStream = stream;
293   return S_OK;
294 }
295 
296 Z7_COM7F_IMF(CHandler::Close())
297 {
298   _packSize = 0;
299   _packSizeDefined = false;
300   _seqStream.Release();
301   _stream.Release();
302   return S_OK;
303 }
304 
305 Z7_CLASS_IMP_COM_1(
306   CCompressProgressInfoImp,
307   ICompressProgressInfo
308 )
309   CMyComPtr<IArchiveOpenCallback> Callback;
310 public:
311   UInt64 Offset;
312   void Init(IArchiveOpenCallback *callback) { Callback = callback; }
313 };
314 
315 Z7_COM7F_IMF(CCompressProgressInfoImp::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */))
316 {
317   if (Callback)
318   {
319     const UInt64 files = 0;
320     const UInt64 value = Offset + *inSize;
321     return Callback->SetCompleted(&files, &value);
322   }
323   return S_OK;
324 }
325 
326 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
327     Int32 testMode, IArchiveExtractCallback *extractCallback))
328 {
329   COM_TRY_BEGIN
330   if (numItems == 0)
331     return S_OK;
332   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
333     return E_INVALIDARG;
334 
335   RINOK(extractCallback->SetTotal(_item.GetSize()))
336   Int32 opRes;
337 {
338   CMyComPtr<ISequentialOutStream> realOutStream;
339   const Int32 askMode = testMode ?
340       NExtract::NAskMode::kTest :
341       NExtract::NAskMode::kExtract;
342   RINOK(extractCallback->GetStream(0, &realOutStream, askMode))
343   if (!testMode && !realOutStream)
344     return S_OK;
345 
346   RINOK(extractCallback->PrepareOperation(askMode))
347 
348   CMyComPtr2_Create<ISequentialOutStream, CDummyOutStream> outStream;
349   outStream->SetStream(realOutStream);
350   outStream->Init();
351   // realOutStream.Release();
352 
353   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
354   lps->Init(extractCallback, false);
355 
356   lps->InSize = _item.HeaderSize;
357   lps->OutSize = outStream->GetSize();
358   RINOK(lps->SetCur())
359 
360   CItem item = _item;
361   item.MakeUncompressed();
362   if (_stream)
363     RINOK(InStream_SeekSet(_stream, _item.HeaderSize))
364   NCompress::NZlib::CDecoder *_decoderZlibSpec = NULL;
365   NCompress::NLzma::CDecoder *_decoderLzmaSpec = NULL;
366   CMyComPtr<ICompressCoder> _decoder;
367 
368   CMyComPtr<ISequentialInStream> inStream2;
369 
370   const UInt64 unpackSize = _item.GetSize() - (UInt32)8;
371   if (_item.IsZlib())
372   {
373     _decoderZlibSpec = new NCompress::NZlib::CDecoder;
374     _decoder = _decoderZlibSpec;
375     inStream2 = _seqStream;
376   }
377   else
378   {
379     /* Some .swf files with lzma contain additional 8 bytes at the end
380        in uncompressed stream.
381        What does that data mean ???
382        We don't decompress these additional 8 bytes */
383 
384     // unpackSize = _item.GetSize();
385     // SetUi32(item.Buf + 4, (UInt32)(unpackSize + 8));
386     CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream;
387     inStream2 = limitedStreamSpec;
388     limitedStreamSpec->SetStream(_seqStream);
389     limitedStreamSpec->Init(_item.GetLzmaPackSize());
390 
391     _decoderLzmaSpec = new NCompress::NLzma::CDecoder;
392     _decoder = _decoderLzmaSpec;
393     // _decoderLzmaSpec->FinishStream = true;
394 
395     Byte props[5];
396     memcpy(props, _item.Buf + 12, 5);
397     UInt32 dicSize = _item.GetLzmaDicSize();
398     if (dicSize > (UInt32)unpackSize)
399     {
400       dicSize = (UInt32)unpackSize;
401       SetUi32(props + 1, dicSize)
402     }
403     RINOK(_decoderLzmaSpec->SetDecoderProperties2(props, 5))
404   }
405   RINOK(item.WriteHeader(outStream))
406   const HRESULT result = _decoder->Code(inStream2, outStream, NULL, &unpackSize, lps);
407   opRes = NExtract::NOperationResult::kDataError;
408   if (result == S_OK)
409   {
410     if (item.GetSize() == outStream->GetSize())
411     {
412       if (_item.IsZlib())
413       {
414         _packSizeDefined = true;
415         _packSize = _decoderZlibSpec->GetInputProcessedSize();
416         opRes = NExtract::NOperationResult::kOK;
417       }
418       else
419       {
420         // if (_decoderLzmaSpec->GetInputProcessedSize() == _packSize)
421           opRes = NExtract::NOperationResult::kOK;
422       }
423     }
424   }
425   else if (result != S_FALSE)
426     return result;
427 
428   // outStream.Release();
429  }
430   return extractCallback->SetOperationResult(opRes);
431   COM_TRY_END
432 }
433 
434 
435 #ifdef Z7_SWF_UPDATE
436 
437 static HRESULT UpdateArchive(ISequentialOutStream *outStream, UInt64 size,
438     bool lzmaMode, const CSingleMethodProps &props,
439     IArchiveUpdateCallback *updateCallback)
440 {
441   UInt64 complexity = 0;
442   RINOK(updateCallback->SetTotal(size))
443   RINOK(updateCallback->SetCompleted(&complexity))
444 
445   CMyComPtr<ISequentialInStream> fileInStream;
446   RINOK(updateCallback->GetStream(0, &fileInStream))
447 
448   /*
449   CDummyOutStream *outStreamSpec = new CDummyOutStream;
450   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
451   outStreamSpec->SetStream(realOutStream);
452   outStreamSpec->Init();
453   realOutStream.Release();
454   */
455 
456   CItem item;
457   const HRESULT res = item.ReadHeader(fileInStream);
458   if (res == S_FALSE)
459     return E_INVALIDARG;
460   RINOK(res)
461   if (!item.IsSwf() || !item.IsUncompressed() || size != item.GetSize())
462     return E_INVALIDARG;
463 
464   NCompress::NZlib::CEncoder *encoderZlibSpec = NULL;
465   NCompress::NLzma::CEncoder *encoderLzmaSpec = NULL;
466   CMyComPtr<ICompressCoder> encoder;
467   CMyComPtr<IOutStream> outSeekStream;
468   if (lzmaMode)
469   {
470     outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream);
471     if (!outSeekStream)
472       return E_NOTIMPL;
473     encoderLzmaSpec = new NCompress::NLzma::CEncoder;
474     encoder = encoderLzmaSpec;
475     RINOK(props.SetCoderProps(encoderLzmaSpec, &size))
476     item.MakeLzma((UInt32)0xFFFFFFFF);
477     CBufPtrSeqOutStream *propStreamSpec = new CBufPtrSeqOutStream;
478     CMyComPtr<ISequentialOutStream> propStream = propStreamSpec;
479     propStreamSpec->Init(item.Buf + 12, 5);
480     RINOK(encoderLzmaSpec->WriteCoderProperties(propStream))
481   }
482   else
483   {
484     encoderZlibSpec = new NCompress::NZlib::CEncoder;
485     encoder = encoderZlibSpec;
486     encoderZlibSpec->Create();
487     RINOK(props.SetCoderProps(encoderZlibSpec->DeflateEncoderSpec, NULL))
488     item.MakeZlib();
489   }
490   RINOK(item.WriteHeader(outStream))
491 
492   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
493   lps->Init(updateCallback, true);
494 
495   RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, lps))
496   UInt64 inputProcessed;
497   if (lzmaMode)
498   {
499     UInt64 curPos = 0;
500     RINOK(outSeekStream->Seek(0, STREAM_SEEK_CUR, &curPos))
501     const UInt64 packSize = curPos - kHeaderLzmaSize;
502     if (packSize > (UInt32)0xFFFFFFFF)
503       return E_INVALIDARG;
504     item.MakeLzma((UInt32)packSize);
505     RINOK(outSeekStream->Seek(0, STREAM_SEEK_SET, NULL))
506     RINOK(item.WriteHeader(outStream))
507     inputProcessed = encoderLzmaSpec->GetInputProcessedSize();
508   }
509   else
510   {
511     inputProcessed = encoderZlibSpec->GetInputProcessedSize();
512   }
513   if (inputProcessed + kHeaderBaseSize != size)
514     return E_INVALIDARG;
515   return updateCallback->SetOperationResult(NUpdate::NOperationResult::kOK);
516 }
517 
518 Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *timeType))
519 {
520   *timeType = NFileTimeType::kUnix;
521   return S_OK;
522 }
523 
524 Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
525     IArchiveUpdateCallback *updateCallback))
526 {
527   if (numItems != 1)
528     return E_INVALIDARG;
529 
530   Int32 newData, newProps;
531   UInt32 indexInArchive;
532   if (!updateCallback)
533     return E_FAIL;
534   RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive))
535 
536   if (IntToBool(newProps))
537   {
538     {
539       NCOM::CPropVariant prop;
540       RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop))
541       if (prop.vt == VT_BOOL)
542       {
543         if (prop.boolVal != VARIANT_FALSE)
544           return E_INVALIDARG;
545       }
546       else if (prop.vt != VT_EMPTY)
547         return E_INVALIDARG;
548     }
549   }
550 
551   if (IntToBool(newData))
552   {
553     UInt64 size;
554     {
555       NCOM::CPropVariant prop;
556       RINOK(updateCallback->GetProperty(0, kpidSize, &prop))
557       if (prop.vt != VT_UI8)
558         return E_INVALIDARG;
559       size = prop.uhVal.QuadPart;
560     }
561     return UpdateArchive(outStream, size, _lzmaMode, _props, updateCallback);
562   }
563 
564   if (indexInArchive != 0)
565     return E_INVALIDARG;
566 
567   if (!_seqStream)
568     return E_NOTIMPL;
569 
570   if (_stream)
571   {
572     RINOK(InStream_SeekToBegin(_stream))
573   }
574   else
575     _item.WriteHeader(outStream);
576   return NCompress::CopyStream(_seqStream, outStream, NULL);
577 }
578 
579 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
580 {
581   _lzmaMode = false;
582   RINOK(_props.SetProperties(names, values, numProps))
583   const AString &m = _props.MethodName;
584   if (m.IsEqualTo_Ascii_NoCase("lzma"))
585   {
586     return E_NOTIMPL;
587     // _lzmaMode = true;
588   }
589   else if (m.IsEqualTo_Ascii_NoCase("Deflate") || m.IsEmpty())
590     _lzmaMode = false;
591   else
592     return E_INVALIDARG;
593   return S_OK;
594 }
595 
596 #endif
597 
598 
599 static const Byte k_Signature[] = {
600     3, 'C', 'W', 'S',
601     3, 'Z', 'W', 'S' };
602 
603 REGISTER_ARC_I(
604   "SWFc", "swf", "~.swf", 0xD8,
605   k_Signature,
606   0,
607   NArcInfoFlags::kMultiSignature,
608   IsArc_Swfc)
609 
610 }
611 
612 namespace NSwf {
613 
614 static const unsigned kNumTagsMax = 1 << 23;
615 
616 struct CTag
617 {
618   UInt32 Type;
619   CByteBuffer Buf;
620 };
621 
622 
623 Z7_CLASS_IMP_CHandler_IInArchive_1(
624   IArchiveOpenSeq
625 )
626   CObjectVector<CTag> _tags;
627   NSwfc::CItem _item;
628   UInt64 _phySize;
629 
630   HRESULT OpenSeq3(ISequentialInStream *stream, IArchiveOpenCallback *callback);
631   HRESULT OpenSeq2(ISequentialInStream *stream, IArchiveOpenCallback *callback);
632 };
633 
634 static const Byte kProps[] =
635 {
636   kpidPath,
637   kpidSize,
638   kpidComment,
639 };
640 
641 IMP_IInArchive_Props
642 IMP_IInArchive_ArcProps_NO_Table
643 
644 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
645 {
646   NCOM::CPropVariant prop;
647   switch (propID)
648   {
649     case kpidPhySize: prop = _phySize; break;
650     case kpidIsNotArcType: prop = true; break;
651   }
652   prop.Detach(value);
653   return S_OK;
654 }
655 
656 
657 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
658 {
659   *numItems = _tags.Size();
660   return S_OK;
661 }
662 
663 static const char * const g_TagDesc[92] =
664 {
665     "End"
666   , "ShowFrame"
667   , "DefineShape"
668   , NULL
669   , "PlaceObject"
670   , "RemoveObject"
671   , "DefineBits"
672   , "DefineButton"
673   , "JPEGTables"
674   , "SetBackgroundColor"
675   , "DefineFont"
676   , "DefineText"
677   , "DoAction"
678   , "DefineFontInfo"
679   , "DefineSound"
680   , "StartSound"
681   , NULL
682   , "DefineButtonSound"
683   , "SoundStreamHead"
684   , "SoundStreamBlock"
685   , "DefineBitsLossless"
686   , "DefineBitsJPEG2"
687   , "DefineShape2"
688   , "DefineButtonCxform"
689   , "Protect"
690   , NULL
691   , "PlaceObject2"
692   , NULL
693   , "RemoveObject2"
694   , NULL
695   , NULL
696   , NULL
697   , "DefineShape3"
698   , "DefineText2"
699   , "DefineButton2"
700   , "DefineBitsJPEG3"
701   , "DefineBitsLossless2"
702   , "DefineEditText"
703   , NULL
704   , "DefineSprite"
705   , NULL
706   , "41"
707   , NULL
708   , "FrameLabel"
709   , NULL
710   , "SoundStreamHead2"
711   , "DefineMorphShape"
712   , NULL
713   , "DefineFont2"
714   , NULL
715   , NULL
716   , NULL
717   , NULL
718   , NULL
719   , NULL
720   , NULL
721   , "ExportAssets"
722   , "ImportAssets"
723   , "EnableDebugger"
724   , "DoInitAction"
725   , "DefineVideoStream"
726   , "VideoFrame"
727   , "DefineFontInfo2"
728   , NULL
729   , "EnableDebugger2"
730   , "ScriptLimits"
731   , "SetTabIndex"
732   , NULL
733   , NULL
734   , "FileAttributes"
735   , "PlaceObject3"
736   , "ImportAssets2"
737   , NULL
738   , "DefineFontAlignZones"
739   , "CSMTextSettings"
740   , "DefineFont3"
741   , "SymbolClass"
742   , "Metadata"
743   , "DefineScalingGrid"
744   , NULL
745   , NULL
746   , NULL
747   , "DoABC"
748   , "DefineShape4"
749   , "DefineMorphShape2"
750   , NULL
751   , "DefineSceneAndFrameLabelData"
752   , "DefineBinaryData"
753   , "DefineFontName"
754   , "StartSound2"
755   , "DefineBitsJPEG4"
756   , "DefineFont4"
757 };
758 
759 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
760 {
761   NWindows::NCOM::CPropVariant prop;
762   const CTag &tag = _tags[index];
763   switch (propID)
764   {
765     case kpidPath:
766     {
767       char s[32];
768       ConvertUInt32ToString(index, s);
769       size_t i = strlen(s);
770       s[i++] = '.';
771       ConvertUInt32ToString(tag.Type, s + i);
772       prop = s;
773       break;
774     }
775     case kpidSize:
776     case kpidPackSize:
777       prop = (UInt64)tag.Buf.Size(); break;
778     case kpidComment:
779       TYPE_TO_PROP(g_TagDesc, tag.Type, prop);
780       break;
781   }
782   prop.Detach(value);
783   return S_OK;
784 }
785 
786 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback))
787 {
788   return OpenSeq2(stream, callback);
789 }
790 
791 static UInt16 Read16(CInBuffer &stream)
792 {
793   UInt32 res = 0;
794   for (unsigned i = 0; i < 2; i++)
795   {
796     Byte b;
797     if (!stream.ReadByte(b))
798       throw 1;
799     res |= (UInt32)b << (i * 8);
800   }
801   return (UInt16)res;
802 }
803 
804 static UInt32 Read32(CInBuffer &stream)
805 {
806   UInt32 res = 0;
807   for (unsigned i = 0; i < 4; i++)
808   {
809     Byte b;
810     if (!stream.ReadByte(b))
811       throw 1;
812     res |= (UInt32)b << (i * 8);
813   }
814   return res;
815 }
816 
817 struct CBitReader
818 {
819   CInBuffer *stream;
820   unsigned NumBits;
821   Byte Val;
822 
823   CBitReader(): NumBits(0), Val(0) {}
824 
825   UInt32 ReadBits(unsigned numBits);
826 };
827 
828 UInt32 CBitReader::ReadBits(unsigned numBits)
829 {
830   UInt32 res = 0;
831   while (numBits > 0)
832   {
833     if (NumBits == 0)
834     {
835       Val = stream->ReadByte();
836       NumBits = 8;
837     }
838     if (numBits <= NumBits)
839     {
840       res <<= numBits;
841       NumBits -= numBits;
842       res |= (Val >> NumBits);
843       Val = (Byte)(Val & (((unsigned)1 << NumBits) - 1));
844       break;
845     }
846     else
847     {
848       res <<= NumBits;
849       res |= Val;
850       numBits -= NumBits;
851       NumBits = 0;
852     }
853   }
854   return res;
855 }
856 
857 HRESULT CHandler::OpenSeq3(ISequentialInStream *stream, IArchiveOpenCallback *callback)
858 {
859   RINOK(_item.ReadHeader(stream))
860   if (!_item.IsSwf() || !_item.IsUncompressed())
861     return S_FALSE;
862   const UInt32 uncompressedSize = _item.GetSize();
863   if (uncompressedSize > kFileSizeMax)
864     return S_FALSE;
865 
866 
867   CInBuffer s;
868   if (!s.Create(1 << 20))
869     return E_OUTOFMEMORY;
870   s.SetStream(stream);
871   s.Init();
872   {
873     CBitReader br;
874     br.stream = &s;
875     const unsigned numBits = br.ReadBits(5);
876     /* UInt32 xMin = */ br.ReadBits(numBits);
877     /* UInt32 xMax = */ br.ReadBits(numBits);
878     /* UInt32 yMin = */ br.ReadBits(numBits);
879     /* UInt32 yMax = */ br.ReadBits(numBits);
880   }
881   /* UInt32 frameDelay = */ Read16(s);
882   /* UInt32 numFrames =  */ Read16(s);
883 
884   _tags.Clear();
885   UInt64 offsetPrev = 0;
886   for (;;)
887   {
888     const UInt32 pair = Read16(s);
889     const UInt32 type = pair >> 6;
890     UInt32 length = pair & 0x3F;
891     if (length == 0x3F)
892       length = Read32(s);
893     if (type == 0)
894       break;
895     const UInt64 offset = s.GetProcessedSize() + NSwfc::kHeaderBaseSize + length;
896     if (offset > uncompressedSize || _tags.Size() >= kNumTagsMax)
897       return S_FALSE;
898     CTag &tag = _tags.AddNew();
899     tag.Type = type;
900     tag.Buf.Alloc(length);
901     if (s.ReadBytes(tag.Buf, length) != length)
902       return S_FALSE;
903     if (callback && offset >= offsetPrev + (1 << 20))
904     {
905       const UInt64 numItems = _tags.Size();
906       RINOK(callback->SetCompleted(&numItems, &offset))
907       offsetPrev = offset;
908     }
909   }
910   _phySize = s.GetProcessedSize() + NSwfc::kHeaderBaseSize;
911   if (_phySize != uncompressedSize)
912   {
913     // do we need to support files extracted from SFW-LZMA with additional 8 bytes?
914     return S_FALSE;
915   }
916   return S_OK;
917 }
918 
919 HRESULT CHandler::OpenSeq2(ISequentialInStream *stream, IArchiveOpenCallback *callback)
920 {
921   HRESULT res;
922   try { res = OpenSeq3(stream, callback); }
923   catch(...) { res = S_FALSE; }
924   return res;
925 }
926 
927 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
928 {
929   return OpenSeq2(stream, NULL);
930 }
931 
932 Z7_COM7F_IMF(CHandler::Close())
933 {
934   _phySize = 0;
935   return S_OK;
936 }
937 
938 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
939     Int32 testMode, IArchiveExtractCallback *extractCallback))
940 {
941   COM_TRY_BEGIN
942   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
943   if (allFilesMode)
944     numItems = _tags.Size();
945   if (numItems == 0)
946     return S_OK;
947   UInt64 totalSize = 0;
948   UInt32 i;
949   for (i = 0; i < numItems; i++)
950     totalSize += _tags[allFilesMode ? i : indices[i]].Buf.Size();
951   RINOK(extractCallback->SetTotal(totalSize))
952 
953   CLocalProgress *lps = new CLocalProgress;
954   CMyComPtr<ICompressProgressInfo> progress = lps;
955   lps->Init(extractCallback, false);
956 
957   totalSize = 0;
958 
959   for (i = 0; i < numItems; i++)
960   {
961     lps->InSize = lps->OutSize = totalSize;
962     RINOK(lps->SetCur())
963     const Int32 askMode = testMode ?
964         NExtract::NAskMode::kTest :
965         NExtract::NAskMode::kExtract;
966     const UInt32 index = allFilesMode ? i : indices[i];
967     const CByteBuffer &buf = _tags[index].Buf;
968     totalSize += buf.Size();
969 
970     CMyComPtr<ISequentialOutStream> outStream;
971     RINOK(extractCallback->GetStream(index, &outStream, askMode))
972     if (!testMode && !outStream)
973       continue;
974 
975     RINOK(extractCallback->PrepareOperation(askMode))
976     if (outStream)
977       RINOK(WriteStream(outStream, buf, buf.Size()))
978     outStream.Release();
979     RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
980   }
981   return S_OK;
982   COM_TRY_END
983 }
984 
985 static const Byte k_Signature[] = { 'F', 'W', 'S' };
986 
987 REGISTER_ARC_I(
988   "SWF", "swf", NULL, 0xD7,
989   k_Signature,
990   0,
991   NArcInfoFlags::kKeepName,
992   NSwfc::IsArc_Swf)
993 
994 }}
995