diff options
Diffstat (limited to 'src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp')
-rw-r--r-- | src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp new file mode 100644 index 000000000..dfc0326d1 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp @@ -0,0 +1,706 @@ +// SwfHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/InBuffer.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/ZlibDecoder.h" +#include "../Compress/ZlibEncoder.h" + +#include "Common/DummyOutStream.h" + +#include "DeflateProps.h" + +using namespace NWindows; + +namespace NArchive { +namespace NSwfc { + +static const UInt32 kHeaderSize = 8; + +static const Byte SWF_UNCOMPRESSED = 'F'; +static const Byte SWF_COMPRESSED = 'C'; +static const Byte SWF_MIN_COMPRESSED_VER = 6; + +struct CItem +{ + Byte Buf[kHeaderSize]; + + UInt32 GetSize() const { return GetUi32(Buf + 4); } + bool IsSwf(Byte c) const { return (Buf[0] == c && Buf[1] == 'W' && Buf[2] == 'S' && Buf[3] < 32); } + bool IsUncompressed() const { return IsSwf(SWF_UNCOMPRESSED); } + bool IsCompressed() const { return IsSwf(SWF_COMPRESSED); } + + void MakeUncompressed() { Buf[0] = SWF_UNCOMPRESSED; } + void MakeCompressed() + { + Buf[0] = SWF_COMPRESSED; + if (Buf[3] < SWF_MIN_COMPRESSED_VER) + Buf[3] = SWF_MIN_COMPRESSED_VER; + } + + HRESULT ReadHeader(ISequentialInStream *stream) { return ReadStream_FALSE(stream, Buf, kHeaderSize); } + HRESULT WriteHeader(ISequentialOutStream *stream) { return WriteStream(stream, Buf, kHeaderSize); } +}; + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public IOutArchive, + public ISetProperties, + public CMyUnknownImp +{ + CItem _item; + UInt64 _packSize; + bool _packSizeDefined; + CMyComPtr<ISequentialInStream> _seqStream; + CMyComPtr<IInStream> _stream; + + CDeflateProps _method; + +public: + MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties) + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidSize: prop = (UInt64)_item.GetSize(); break; + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) +{ + RINOK(OpenSeq(stream)); + _stream = stream; + return S_OK; +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + Close(); + HRESULT res = _item.ReadHeader(stream); + if (res == S_OK) + if (_item.IsCompressed()) + _seqStream = stream; + else + res = S_FALSE; + return res; +} + +STDMETHODIMP CHandler::Close() +{ + _packSizeDefined = false; + _seqStream.Release(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + extractCallback->SetTotal(_item.GetSize()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + NCompress::NZlib::CDecoder *_decoderSpec = new NCompress::NZlib::CDecoder; + CMyComPtr<ICompressCoder> _decoder = _decoderSpec; + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + lps->InSize = kHeaderSize; + lps->OutSize = outStreamSpec->GetSize(); + RINOK(lps->SetCur()); + + CItem item = _item; + item.MakeUncompressed(); + RINOK(item.WriteHeader(outStream)); + if (_stream) + RINOK(_stream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL)); + HRESULT result = _decoderSpec->Code(_seqStream, outStream, NULL, NULL, progress); + Int32 opRes = NExtract::NOperationResult::kDataError; + if (result == S_OK) + { + if (_item.GetSize() == outStreamSpec->GetSize()) + { + _packSizeDefined = true; + _packSize = _decoderSpec->GetInputProcessedSize() + kHeaderSize; + opRes = NExtract::NOperationResult::kOK; + } + } + else if (result != S_FALSE) + return result; + + outStream.Release(); + return extractCallback->SetOperationResult(opRes); + COM_TRY_END +} + +static HRESULT UpdateArchive(ISequentialOutStream *outStream, + UInt64 size, CDeflateProps &deflateProps, + IArchiveUpdateCallback *updateCallback) +{ + UInt64 complexity = 0; + RINOK(updateCallback->SetTotal(size)); + RINOK(updateCallback->SetCompleted(&complexity)); + + CMyComPtr<ISequentialInStream> fileInStream; + RINOK(updateCallback->GetStream(0, &fileInStream)); + + CItem item; + HRESULT res = item.ReadHeader(fileInStream); + if (res == S_FALSE) + return E_INVALIDARG; + RINOK(res); + if (!item.IsUncompressed() || size != item.GetSize()) + return E_INVALIDARG; + + item.MakeCompressed(); + item.WriteHeader(outStream); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + NCompress::NZlib::CEncoder *encoderSpec = new NCompress::NZlib::CEncoder; + CMyComPtr<ICompressCoder> encoder = encoderSpec; + encoderSpec->Create(); + RINOK(deflateProps.SetCoderProperties(encoderSpec->DeflateEncoderSpec)); + RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, progress)); + if (encoderSpec->GetInputProcessedSize() + kHeaderSize != size) + return E_INVALIDARG; + return updateCallback->SetOperationResult(NUpdate::NOperationResult::kOK); +} + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) +{ + *timeType = NFileTimeType::kUnix; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + if (numItems != 1) + return E_INVALIDARG; + + Int32 newData, newProps; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); + + if (IntToBool(newProps)) + { + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); + if (prop.vt == VT_BOOL) + { + if (prop.boolVal != VARIANT_FALSE) + return E_INVALIDARG; + } + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + } + } + + if (IntToBool(newData)) + { + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + return UpdateArchive(outStream, size, _method, updateCallback); + } + + if (indexInArchive != 0) + return E_INVALIDARG; + + if (!_seqStream) + return E_NOTIMPL; + + if (_stream) + { + RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + else + _item.WriteHeader(outStream); + return NCompress::CopyStream(_seqStream, outStream, NULL); +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps) +{ + return _method.SetProperties(names, values, numProps); +} + +static IInArchive *CreateArc() { return new CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"SWFc", L"swf", L"~.swf", 0xD8, { 'C', 'W', 'S' }, 3, true, CreateArc, CreateArcOut }; + +REGISTER_ARC(Swfc) + +} + +namespace NSwf { + +static const UInt32 kFileSizeMax = (UInt32)1 << 30; +static const int kNumTagsMax = (UInt32)1 << 23; + +struct CTag +{ + UInt32 Type; + CByteBuffer Buf; +}; + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public CMyUnknownImp +{ + CObjectVector<CTag> _tags; + NSwfc::CItem _item; + UInt64 _packSize; + + HRESULT OpenSeq3(ISequentialInStream *stream, IArchiveOpenCallback *callback); + HRESULT OpenSeq2(ISequentialInStream *stream, IArchiveOpenCallback *callback); +public: + MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq) + INTERFACE_IInArchive(;) + + STDMETHOD(OpenSeq)(ISequentialInStream *stream); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidComment, VT_BSTR} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _tags.Size(); + return S_OK; +} + +static const char *g_TagDesc[92] = +{ + "End", + "ShowFrame", + "DefineShape", + NULL, + "PlaceObject", + "RemoveObject", + "DefineBits", + "DefineButton", + "JPEGTables", + "SetBackgroundColor", + "DefineFont", + "DefineText", + "DoAction", + "DefineFontInfo", + "DefineSound", + "StartSound", + NULL, + "DefineButtonSound", + "SoundStreamHead", + "SoundStreamBlock", + "DefineBitsLossless", + "DefineBitsJPEG2", + "DefineShape2", + "DefineButtonCxform", + "Protect", + NULL, + "PlaceObject2", + NULL, + "RemoveObject2", + NULL, + NULL, + NULL, + "DefineShape3", + "DefineText2", + "DefineButton2", + "DefineBitsJPEG3", + "DefineBitsLossless2", + "DefineEditText", + NULL, + "DefineSprite", + NULL, + "41", + NULL, + "FrameLabel", + NULL, + "SoundStreamHead2", + "DefineMorphShape", + NULL, + "DefineFont2", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "ExportAssets", + "ImportAssets", + "EnableDebugger", + "DoInitAction", + "DefineVideoStream", + "VideoFrame", + "DefineFontInfo2", + NULL, + "EnableDebugger2", + "ScriptLimits", + "SetTabIndex", + NULL, + NULL, + "FileAttributes", + "PlaceObject3", + "ImportAssets2", + NULL, + "DefineFontAlignZones", + "CSMTextSettings", + "DefineFont3", + "SymbolClass", + "Metadata", + "DefineScalingGrid", + NULL, + NULL, + NULL, + "DoABC", + "DefineShape4", + "DefineMorphShape2", + NULL, + "DefineSceneAndFrameLabelData", + "DefineBinaryData", + "DefineFontName", + "StartSound2", + "DefineBitsJPEG4", + "DefineFont4" +}; + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + const CTag &tag = _tags[index]; + switch(propID) + { + case kpidPath: + { + char s[32]; + ConvertUInt32ToString(index, s); + size_t i = strlen(s); + s[i++] = '.'; + ConvertUInt32ToString(tag.Type, s + i); + prop = s; + break; + } + case kpidSize: + case kpidPackSize: + prop = (UInt64)tag.Buf.GetCapacity(); break; + case kpidComment: + if (tag.Type < sizeof(g_TagDesc) / sizeof(g_TagDesc[0])) + { + const char *s = g_TagDesc[tag.Type]; + if (s != NULL) + prop = s; + } + break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + return OpenSeq2(stream, callback); +} + +static UInt16 Read16(CInBuffer &stream) +{ + UInt16 res = 0; + for (int i = 0; i < 2; i++) + { + Byte b; + if (!stream.ReadByte(b)) + throw 1; + res |= (UInt16)b << (i * 8); + } + return res; +} + +static UInt32 Read32(CInBuffer &stream) +{ + UInt32 res = 0; + for (int i = 0; i < 4; i++) + { + Byte b; + if (!stream.ReadByte(b)) + throw 1; + res |= (UInt32)b << (i * 8); + } + return res; +} + +struct CBitReader +{ + CInBuffer *stream; + unsigned NumBits; + Byte Val; + + CBitReader(): NumBits(0), Val(0) {} + + UInt32 ReadBits(unsigned numBits); +}; + +UInt32 CBitReader::ReadBits(unsigned numBits) +{ + UInt32 res = 0; + while (numBits > 0) + { + if (NumBits == 0) + { + Val = stream->ReadByte(); + NumBits = 8; + } + if (numBits <= NumBits) + { + res <<= numBits; + NumBits -= numBits; + res |= (Val >> NumBits); + Val &= (1 << NumBits) - 1; + break; + } + else + { + res <<= NumBits; + res |= Val; + numBits -= NumBits; + NumBits = 0; + } + } + return res; +} + +HRESULT CHandler::OpenSeq3(ISequentialInStream *stream, IArchiveOpenCallback *callback) +{ + RINOK(_item.ReadHeader(stream)) + if (!_item.IsUncompressed()) + return S_FALSE; + + CInBuffer s; + if (!s.Create(1 << 20)) + return E_OUTOFMEMORY; + s.SetStream(stream); + s.Init(); + { + CBitReader br; + br.stream = &s; + unsigned numBits = br.ReadBits(5); + /* UInt32 xMin = */ br.ReadBits(numBits); + /* UInt32 xMax = */ br.ReadBits(numBits); + /* UInt32 yMin = */ br.ReadBits(numBits); + /* UInt32 yMax = */ br.ReadBits(numBits); + } + /* UInt32 frameDelay = */ Read16(s); + /* UInt32 numFrames = */ Read16(s); + + _tags.Clear(); + UInt64 offsetPrev = 0; + for (;;) + { + UInt32 pair = Read16(s); + UInt32 type = pair >> 6; + UInt32 length = pair & 0x3F; + if (length == 0x3F) + length = Read32(s); + if (type == 0) + break; + UInt64 offset = s.GetProcessedSize() + NSwfc::kHeaderSize + length; + if (offset > kFileSizeMax || _tags.Size() >= kNumTagsMax) + return S_FALSE; + _tags.Add(CTag()); + CTag &tag = _tags.Back(); + tag.Type = type; + tag.Buf.SetCapacity(length); + if (s.ReadBytes(tag.Buf, length) != length) + return S_FALSE; + if (callback && offset >= offsetPrev + (1 << 20)) + { + UInt64 numItems = _tags.Size(); + RINOK(callback->SetCompleted(&numItems, &offset)); + offsetPrev = offset; + } + } + _packSize = s.GetProcessedSize() + NSwfc::kHeaderSize; + return S_OK; +} + +HRESULT CHandler::OpenSeq2(ISequentialInStream *stream, IArchiveOpenCallback *callback) +{ + HRESULT res; + try { res = OpenSeq3(stream, callback); } + catch(...) { res = S_FALSE; } + return res; +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + return OpenSeq2(stream, NULL); +} + +STDMETHODIMP CHandler::Close() +{ + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _tags.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _tags[allFilesMode ? i : indices[i]].Buf.GetCapacity(); + extractCallback->SetTotal(totalSize); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + totalSize = 0; + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = totalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CByteBuffer &buf = _tags[index].Buf; + totalSize += buf.GetCapacity(); + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + if (outStream) + RINOK(WriteStream(outStream, buf, buf.GetCapacity())); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"SWF", L"swf", 0, 0xD7, { 'F', 'W', 'S' }, 3, true, CreateArc, 0 }; + +REGISTER_ARC(Swf) + +}} |