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