rlm@1: // LzhHandler.cpp rlm@1: rlm@1: #include "StdAfx.h" rlm@1: rlm@1: #include "Common/ComTry.h" rlm@1: #include "Common/Defs.h" rlm@1: #include "Common/StringConvert.h" rlm@1: rlm@1: #include "Windows/PropVariant.h" rlm@1: #include "Windows/Time.h" rlm@1: rlm@1: #include "LzhHandler.h" rlm@1: #include "LzhOutStreamWithCRC.h" rlm@1: rlm@1: #include "../../ICoder.h" rlm@1: rlm@1: #include "../../Common/LimitedStreams.h" rlm@1: #include "../../Common/ProgressUtils.h" rlm@1: rlm@1: #include "../../Compress/CopyCoder.h" rlm@1: #include "../../Compress/LzhDecoder.h" rlm@1: rlm@1: #include "../Common/ItemNameUtils.h" rlm@1: rlm@1: using namespace NWindows; rlm@1: using namespace NTime; rlm@1: rlm@1: namespace NArchive { rlm@1: namespace NLzh{ rlm@1: rlm@1: struct COsPair rlm@1: { rlm@1: Byte Id; rlm@1: const wchar_t *Name; rlm@1: }; rlm@1: rlm@1: COsPair g_OsPairs[] = rlm@1: { rlm@1: { 'M', L"MS-DOS" }, rlm@1: { '2', L"OS/2" }, rlm@1: { '9', L"OS9" }, rlm@1: { 'K', L"OS/68K" }, rlm@1: { '3', L"OS/386" }, rlm@1: { 'H', L"HUMAN" }, rlm@1: { 'U', L"UNIX" }, rlm@1: { 'C', L"CP/M" }, rlm@1: { 'F', L"FLEX" }, rlm@1: { 'm', L"Mac" }, rlm@1: { 'R', L"Runser" }, rlm@1: { 'T', L"TownsOS" }, rlm@1: { 'X', L"XOSK" }, rlm@1: { 'w', L"Windows95" }, rlm@1: { 'W', L"WindowsNT" }, rlm@1: { 0, L"MS-DOS" }, rlm@1: { 'J', L"Java VM" } rlm@1: }; rlm@1: rlm@1: const wchar_t *kUnknownOS = L"Unknown"; rlm@1: rlm@1: const int kNumHostOSes = sizeof(g_OsPairs) / sizeof(g_OsPairs[0]); rlm@1: rlm@1: static const wchar_t *GetOS(Byte osId) rlm@1: { rlm@1: for (int i = 0; i < kNumHostOSes; i++) rlm@1: if (g_OsPairs[i].Id == osId) rlm@1: return g_OsPairs[i].Name; rlm@1: return kUnknownOS; rlm@1: }; rlm@1: rlm@1: STATPROPSTG kProps[] = rlm@1: { rlm@1: { NULL, kpidPath, VT_BSTR}, rlm@1: { NULL, kpidIsDir, VT_BOOL}, rlm@1: { NULL, kpidSize, VT_UI8}, rlm@1: { NULL, kpidPackSize, VT_UI8}, rlm@1: { NULL, kpidMTime, VT_FILETIME}, rlm@1: { NULL, kpidAttrib, VT_UI4}, rlm@1: rlm@1: // { NULL, kpidCommented, VT_BOOL}, rlm@1: rlm@1: { NULL, kpidCRC, VT_UI4}, rlm@1: rlm@1: { NULL, kpidMethod, VT_UI1}, rlm@1: { NULL, kpidHostOS, VT_BSTR} rlm@1: rlm@1: }; rlm@1: rlm@1: IMP_IInArchive_Props rlm@1: IMP_IInArchive_ArcProps_NO rlm@1: rlm@1: CHandler::CHandler() {} rlm@1: rlm@1: STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) rlm@1: { rlm@1: *numItems = _items.Size(); rlm@1: return S_OK; rlm@1: } rlm@1: rlm@1: STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) rlm@1: { rlm@1: COM_TRY_BEGIN rlm@1: NWindows::NCOM::CPropVariant prop; rlm@1: const CItemEx &item = _items[index]; rlm@1: switch(propID) rlm@1: { rlm@1: case kpidPath: rlm@1: { rlm@1: UString s = NItemName::WinNameToOSName(MultiByteToUnicodeString(item.GetName(), CP_OEMCP)); rlm@1: if (!s.IsEmpty()) rlm@1: { rlm@1: if (s[s.Length() - 1] == WCHAR_PATH_SEPARATOR) rlm@1: s.Delete(s.Length() - 1); rlm@1: prop = s; rlm@1: } rlm@1: break; rlm@1: } rlm@1: case kpidIsDir: prop = item.IsDir(); break; rlm@1: case kpidSize: prop = item.Size; break; rlm@1: case kpidPackSize: prop = item.PackSize; break; rlm@1: case kpidCRC: prop = (UInt32)item.CRC; break; rlm@1: case kpidHostOS: prop = GetOS(item.OsId); break; rlm@1: case kpidMTime: rlm@1: { rlm@1: FILETIME utcFileTime; rlm@1: UInt32 unixTime; rlm@1: if (item.GetUnixTime(unixTime)) rlm@1: NTime::UnixTimeToFileTime(unixTime, utcFileTime); rlm@1: else rlm@1: { rlm@1: FILETIME localFileTime; rlm@1: if (DosTimeToFileTime(item.ModifiedTime, localFileTime)) rlm@1: { rlm@1: if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime)) rlm@1: utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0; rlm@1: } rlm@1: else rlm@1: utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0; rlm@1: } rlm@1: prop = utcFileTime; rlm@1: break; rlm@1: } rlm@1: /* rlm@1: case kpidAttrib: prop = (UInt32)item.Attributes; break; rlm@1: case kpidCommented: prop = item.IsCommented(); break; rlm@1: */ rlm@1: case kpidMethod: rlm@1: { rlm@1: wchar_t method2[kMethodIdSize + 1]; rlm@1: method2[kMethodIdSize] = 0; rlm@1: for (int i = 0; i < kMethodIdSize; i++) rlm@1: method2[i] = item.Method[i]; rlm@1: prop = method2; rlm@1: break; rlm@1: } rlm@1: } rlm@1: prop.Detach(value); rlm@1: return S_OK; rlm@1: COM_TRY_END rlm@1: } rlm@1: rlm@1: /* rlm@1: class CProgressImp: public CProgressVirt rlm@1: { rlm@1: public: rlm@1: CMyComPtr Callback; rlm@1: STDMETHOD(SetCompleted)(const UInt64 *numFiles); rlm@1: }; rlm@1: rlm@1: STDMETHODIMP CProgressImp::SetCompleted(const UInt64 *numFiles) rlm@1: { rlm@1: if (Callback) rlm@1: return Callback->SetCompleted(numFiles, NULL); rlm@1: return S_OK; rlm@1: } rlm@1: */ rlm@1: rlm@1: STDMETHODIMP CHandler::Open(IInStream *stream, rlm@1: const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback) rlm@1: { rlm@1: COM_TRY_BEGIN rlm@1: try rlm@1: { rlm@1: _items.Clear(); rlm@1: CInArchive archive; rlm@1: rlm@1: UInt64 endPos = 0; rlm@1: bool needSetTotal = true; rlm@1: rlm@1: if (callback != NULL) rlm@1: { rlm@1: RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); rlm@1: RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); rlm@1: } rlm@1: rlm@1: RINOK(archive.Open(stream)); rlm@1: for (;;) rlm@1: { rlm@1: CItemEx item; rlm@1: bool filled; rlm@1: HRESULT result = archive.GetNextItem(filled, item); rlm@1: if (result == S_FALSE) rlm@1: return S_FALSE; rlm@1: if (result != S_OK) rlm@1: return S_FALSE; rlm@1: if (!filled) rlm@1: break; rlm@1: _items.Add(item); rlm@1: archive.Skeep(item.PackSize); rlm@1: if (callback != NULL) rlm@1: { rlm@1: if (needSetTotal) rlm@1: { rlm@1: RINOK(callback->SetTotal(NULL, &endPos)); rlm@1: needSetTotal = false; rlm@1: } rlm@1: if (_items.Size() % 100 == 0) rlm@1: { rlm@1: UInt64 numFiles = _items.Size(); rlm@1: UInt64 numBytes = item.DataPosition; rlm@1: RINOK(callback->SetCompleted(&numFiles, &numBytes)); rlm@1: } rlm@1: } rlm@1: } rlm@1: if (_items.IsEmpty()) rlm@1: return S_FALSE; rlm@1: rlm@1: _stream = stream; rlm@1: } rlm@1: catch(...) rlm@1: { rlm@1: return S_FALSE; rlm@1: } rlm@1: COM_TRY_END rlm@1: return S_OK; rlm@1: } rlm@1: rlm@1: STDMETHODIMP CHandler::Close() rlm@1: { rlm@1: _items.Clear(); rlm@1: _stream.Release(); rlm@1: return S_OK; rlm@1: } rlm@1: rlm@1: rlm@1: rlm@1: ////////////////////////////////////// rlm@1: // CHandler::DecompressItems rlm@1: rlm@1: STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems, rlm@1: Int32 testModeSpec, IArchiveExtractCallback *extractCallback) rlm@1: { rlm@1: COM_TRY_BEGIN rlm@1: bool testMode = (testModeSpec != 0); rlm@1: UInt64 totalUnPacked = 0, totalPacked = 0; rlm@1: bool allFilesMode = (numItems == UInt32(-1)); rlm@1: if (allFilesMode) rlm@1: numItems = _items.Size(); rlm@1: if(numItems == 0) rlm@1: return S_OK; rlm@1: UInt32 i; rlm@1: for(i = 0; i < numItems; i++) rlm@1: { rlm@1: const CItemEx &item = _items[allFilesMode ? i : indices[i]]; rlm@1: totalUnPacked += item.Size; rlm@1: totalPacked += item.PackSize; rlm@1: } rlm@1: extractCallback->SetTotal(totalUnPacked); rlm@1: rlm@1: UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0; rlm@1: UInt64 currentItemUnPacked, currentItemPacked; rlm@1: rlm@1: NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = 0; rlm@1: CMyComPtr lzhDecoder; rlm@1: CMyComPtr lzh1Decoder; rlm@1: CMyComPtr arj2Decoder; rlm@1: rlm@1: NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); rlm@1: CMyComPtr copyCoder = copyCoderSpec; rlm@1: rlm@1: CLocalProgress *lps = new CLocalProgress; rlm@1: CMyComPtr progress = lps; rlm@1: lps->Init(extractCallback, false); rlm@1: rlm@1: CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; rlm@1: CMyComPtr inStream(streamSpec); rlm@1: streamSpec->SetStream(_stream); rlm@1: rlm@1: for(i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked, rlm@1: currentTotalPacked += currentItemPacked) rlm@1: { rlm@1: currentItemUnPacked = 0; rlm@1: currentItemPacked = 0; rlm@1: rlm@1: lps->InSize = currentTotalPacked; rlm@1: lps->OutSize = currentTotalUnPacked; rlm@1: RINOK(lps->SetCur()); rlm@1: rlm@1: CMyComPtr realOutStream; rlm@1: Int32 askMode; rlm@1: askMode = testMode ? NExtract::NAskMode::kTest : rlm@1: NExtract::NAskMode::kExtract; rlm@1: Int32 index = allFilesMode ? i : indices[i]; rlm@1: const CItemEx &item = _items[index]; rlm@1: RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); rlm@1: rlm@1: if (item.IsDir()) rlm@1: { rlm@1: // if (!testMode) rlm@1: { rlm@1: RINOK(extractCallback->PrepareOperation(askMode)); rlm@1: RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); rlm@1: } rlm@1: continue; rlm@1: } rlm@1: rlm@1: if (!testMode && (!realOutStream)) rlm@1: continue; rlm@1: rlm@1: RINOK(extractCallback->PrepareOperation(askMode)); rlm@1: currentItemUnPacked = item.Size; rlm@1: currentItemPacked = item.PackSize; rlm@1: rlm@1: { rlm@1: COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; rlm@1: CMyComPtr outStream(outStreamSpec); rlm@1: outStreamSpec->Init(realOutStream); rlm@1: realOutStream.Release(); rlm@1: rlm@1: UInt64 pos; rlm@1: _stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos); rlm@1: rlm@1: streamSpec->Init(item.PackSize); rlm@1: rlm@1: HRESULT result = S_OK; rlm@1: Int32 opRes = NExtract::NOperationResult::kOK; rlm@1: rlm@1: if (item.IsCopyMethod()) rlm@1: { rlm@1: result = copyCoder->Code(inStream, outStream, NULL, NULL, progress); rlm@1: if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize) rlm@1: result = S_FALSE; rlm@1: } rlm@1: else if (item.IsLh4GroupMethod()) rlm@1: { rlm@1: if (!lzhDecoder) rlm@1: { rlm@1: lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder; rlm@1: lzhDecoder = lzhDecoderSpec; rlm@1: } rlm@1: lzhDecoderSpec->SetDictionary(item.GetNumDictBits()); rlm@1: result = lzhDecoder->Code(inStream, outStream, NULL, ¤tItemUnPacked, progress); rlm@1: } rlm@1: /* rlm@1: else if (item.IsLh1GroupMethod()) rlm@1: { rlm@1: if (!lzh1Decoder) rlm@1: { rlm@1: lzh1DecoderSpec = new NCompress::NLzh1::NDecoder::CCoder; rlm@1: lzh1Decoder = lzh1DecoderSpec; rlm@1: } rlm@1: lzh1DecoderSpec->SetDictionary(item.GetNumDictBits()); rlm@1: result = lzh1Decoder->Code(inStream, outStream, NULL, ¤tItemUnPacked, progress); rlm@1: } rlm@1: */ rlm@1: else rlm@1: opRes = NExtract::NOperationResult::kUnSupportedMethod; rlm@1: rlm@1: if (opRes == NExtract::NOperationResult::kOK) rlm@1: { rlm@1: if (result == S_FALSE) rlm@1: opRes = NExtract::NOperationResult::kDataError; rlm@1: else rlm@1: { rlm@1: RINOK(result); rlm@1: if (outStreamSpec->GetCRC() != item.CRC) rlm@1: opRes = NExtract::NOperationResult::kCRCError; rlm@1: } rlm@1: } rlm@1: outStream.Release(); rlm@1: RINOK(extractCallback->SetOperationResult(opRes)); rlm@1: } rlm@1: } rlm@1: return S_OK; rlm@1: COM_TRY_END rlm@1: } rlm@1: rlm@1: }}