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