rlm@1: #include "7zip.h" rlm@1: rlm@1: #include "7z/C/Types.h" rlm@1: #include "7z/CPP/7zip/Archive/IArchive.h" rlm@1: #include "7z/CPP/Common/InitializeStaticLib.h" // important! (if using a static lib) rlm@1: rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: rlm@1: #include "7zipstreams.h" // defines OutStream and InFileStream rlm@1: rlm@1: STDAPI GetNumberOfFormats(UINT32 *numFormats); rlm@1: STDAPI GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT *value); rlm@1: STDAPI CreateObject(const GUID *clsid, const GUID *iid, void **outObject); rlm@1: rlm@1: struct ArchiveFormatInfo rlm@1: { rlm@1: std::string name; rlm@1: std::vector extensions; rlm@1: std::string signature; rlm@1: GUID guid; rlm@1: }; rlm@1: rlm@1: static std::vector s_formatInfos; rlm@1: rlm@1: static std::string wstrToStr(const wchar_t* wstr) rlm@1: { rlm@1: /* rlm@1: // This thing wouldn't work rlm@1: char* str = (char*)_alloca((wcslen(wstr)+1)); rlm@1: sprintf(str, "%S", wstr); rlm@1: return std::string(str); rlm@1: */ rlm@1: // setlocale(LC_CTYPE, ".ACP"); rlm@1: size_t n = wcstombs(NULL, wstr, 0); rlm@1: if (n == (size_t)-1) // failed rlm@1: { rlm@1: // setlocale(LC_CTYPE, "C"); rlm@1: return std::string(); rlm@1: } rlm@1: char* str = (char*)_alloca(n + 1); rlm@1: wcstombs(str, wstr, n); rlm@1: str[n] = '\0'; rlm@1: // setlocale(LC_CTYPE, "C"); rlm@1: return std::string(str); rlm@1: } rlm@1: rlm@1: static std::vector tokenize(const std::string & str, const std::string & delim) rlm@1: { rlm@1: std::vector tokens; rlm@1: size_t p0 = 0, p1 = std::string::npos; rlm@1: while(p0 != std::string::npos) rlm@1: { rlm@1: p1 = str.find_first_of(delim, p0); rlm@1: if(p1 != p0) rlm@1: { rlm@1: std::string token = str.substr(p0, p1 - p0); rlm@1: tokens.push_back(token); rlm@1: } rlm@1: p0 = str.find_first_not_of(delim, p1); rlm@1: } rlm@1: return tokens; rlm@1: } rlm@1: rlm@1: static std::string s_supportedFormatsFilter; rlm@1: const char* GetSupportedFormatsFilter() rlm@1: { rlm@1: assert(!s_formatInfos.empty()); rlm@1: if(s_supportedFormatsFilter.empty()) rlm@1: { rlm@1: s_supportedFormatsFilter = ""; rlm@1: for(size_t i = 0; i < s_formatInfos.size(); i++) rlm@1: { rlm@1: for(size_t j = 0; j < s_formatInfos[i].extensions.size(); j++) rlm@1: { rlm@1: s_supportedFormatsFilter += ";*."; rlm@1: s_supportedFormatsFilter += s_formatInfos[i].extensions[j]; rlm@1: } rlm@1: } rlm@1: } rlm@1: return s_supportedFormatsFilter.c_str(); rlm@1: } rlm@1: rlm@1: void InitDecoder() rlm@1: { rlm@1: CleanupDecoder(); rlm@1: rlm@1: UINT32 numFormats = 0; rlm@1: GetNumberOfFormats(&numFormats); rlm@1: rlm@1: for(unsigned int i = 0; i < numFormats; i++) rlm@1: { rlm@1: PROPVARIANT var = {VT_EMPTY}; rlm@1: ArchiveFormatInfo info; rlm@1: rlm@1: GetHandlerProperty2(i, NArchive::kName, &var); rlm@1: if(var.vt == VT_BSTR) rlm@1: info.name = wstrToStr(var.bstrVal); rlm@1: rlm@1: GetHandlerProperty2(i, NArchive::kExtension, &var); rlm@1: if(var.vt == VT_BSTR) rlm@1: info.extensions = tokenize(wstrToStr(var.bstrVal), " "); rlm@1: rlm@1: GetHandlerProperty2(i, NArchive::kStartSignature, &var); rlm@1: if(var.vt == VT_BSTR) rlm@1: info.signature = (const char*)var.bstrVal; // note: there's no 100% correct way of doing this with the existing 7-zip interface, but this is much better than using SysStringLen() in any way (it would return 1 for the signature "BZh", for example) rlm@1: rlm@1: GetHandlerProperty2(i,NArchive::kClassID,&var); rlm@1: if(var.vt == VT_BSTR) rlm@1: memcpy(&info.guid, var.bstrVal, 16); rlm@1: else rlm@1: memset(&info.guid, 0, 16); rlm@1: rlm@1: s_formatInfos.push_back(info); rlm@1: rlm@1: VariantClear((VARIANTARG*)&var); rlm@1: } rlm@1: } rlm@1: rlm@1: void CleanupDecoder() rlm@1: { rlm@1: s_formatInfos.clear(); rlm@1: s_supportedFormatsFilter.clear(); rlm@1: } rlm@1: rlm@1: #include "7z/CPP/7zip/Archive/Zip/ZipHandler.h" rlm@1: rlm@1: rlm@1: ArchiveFile::ArchiveFile(const char* filename, const char* displayFilename) rlm@1: { rlm@1: assert(!s_formatInfos.empty()); rlm@1: rlm@1: m_typeIndex = -1; rlm@1: m_numItems = 0; rlm@1: m_items = NULL; rlm@1: m_filename = NULL; rlm@1: m_displayFilename = NULL; rlm@1: m_userMadeSelection = false; rlm@1: rlm@1: FILE* file = fopen(filename, "rb"); rlm@1: if(!file) rlm@1: return; rlm@1: rlm@1: m_filename = new char[strlen(filename)+1]; rlm@1: strcpy(m_filename, filename); rlm@1: rlm@1: if(displayFilename) rlm@1: { rlm@1: m_displayFilename = new char[strlen(displayFilename)+1]; rlm@1: strcpy(m_displayFilename, displayFilename); rlm@1: } rlm@1: rlm@1: // detect archive type using format signature in file rlm@1: for(size_t i = 0; i < s_formatInfos.size() && m_typeIndex < 0; i++) rlm@1: { rlm@1: fseek(file, 0, SEEK_SET); rlm@1: rlm@1: std::string& formatSig = s_formatInfos[i].signature; rlm@1: int len = formatSig.size(); rlm@1: rlm@1: if(len == 0) rlm@1: continue; // because some formats have no signature rlm@1: rlm@1: char* fileSig = (char*)_alloca(len); rlm@1: fread(fileSig, 1, len, file); rlm@1: rlm@1: if(!memcmp(formatSig.c_str(), fileSig, len)) rlm@1: m_typeIndex = i; rlm@1: } rlm@1: rlm@1: // if no signature match has been found, detect archive type using filename. rlm@1: // this is only for signature-less formats rlm@1: const char* fileExt = strrchr(filename, '.'); rlm@1: if(fileExt++) rlm@1: { rlm@1: for(size_t i = 0; i < s_formatInfos.size() && m_typeIndex < 0; i++) rlm@1: { rlm@1: if(s_formatInfos[i].signature.empty()) rlm@1: { rlm@1: std::vector& formatExts = s_formatInfos[i].extensions; rlm@1: for(size_t j = 0; j < formatExts.size(); j++) rlm@1: { rlm@1: if(!_stricmp(formatExts[j].c_str(), fileExt)) rlm@1: { rlm@1: m_typeIndex = i; rlm@1: break; rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: if(m_typeIndex < 0) rlm@1: { rlm@1: // uncompressed rlm@1: rlm@1: m_numItems = 1; rlm@1: m_items = new ArchiveItem[m_numItems]; rlm@1: rlm@1: fseek(file, 0, SEEK_END); rlm@1: m_items[0].size = ftell(file); rlm@1: rlm@1: m_items[0].name = new char[strlen(filename)+1]; rlm@1: strcpy(m_items[0].name, filename); rlm@1: } rlm@1: else rlm@1: { rlm@1: IInArchive* object = NULL; rlm@1: if(SUCCEEDED(CreateObject(&s_formatInfos[m_typeIndex].guid, &IID_IInArchive, (void**)&object))) rlm@1: { rlm@1: InFileStream* ifs = new InFileStream(filename); rlm@1: if(SUCCEEDED(object->Open(ifs,0,0))) rlm@1: { rlm@1: UInt32 numItems = 0; rlm@1: object->GetNumberOfItems(&numItems); rlm@1: m_numItems = numItems; rlm@1: m_items = new ArchiveItem[m_numItems]; rlm@1: rlm@1: for(int i = 0; i < m_numItems; i++) rlm@1: { rlm@1: PROPVARIANT var = {VT_EMPTY}; rlm@1: ArchiveItem& item = m_items[i]; rlm@1: rlm@1: object->GetProperty(i, kpidSize, &var); rlm@1: item.size = var.uhVal.LowPart; rlm@1: rlm@1: object->GetProperty(i, kpidPath, &var); rlm@1: std::string& path = wstrToStr(var.bstrVal); rlm@1: item.name = new char[path.size()+1]; rlm@1: strcpy(item.name, path.c_str()); rlm@1: rlm@1: //object->GetProperty(i, kpidMethod, &var); rlm@1: //std::string& method = wstrToStr(var.bstrVal); rlm@1: //item.method = new char[method.size()+1]; rlm@1: //strcpy(item.method, method.c_str()); rlm@1: rlm@1: object->GetProperty(i, kpidEncrypted, &var); rlm@1: #ifdef _NO_CRYPTO rlm@1: if(var.boolVal) rlm@1: item.size = 0; // don't support decompressing it, pretend size zero rlm@1: #else rlm@1: #error password support NYI... see client7z.cpp rlm@1: item.encrypted = !!var.boolVal; rlm@1: #endif rlm@1: rlm@1: VariantClear((VARIANTARG*)&var); rlm@1: } rlm@1: rlm@1: object->Close(); rlm@1: } rlm@1: rlm@1: object->Release(); rlm@1: } rlm@1: } rlm@1: rlm@1: fclose(file); rlm@1: } rlm@1: rlm@1: ArchiveFile::~ArchiveFile() rlm@1: { rlm@1: for(int i = 0; i < m_numItems; i++) rlm@1: { rlm@1: delete[] m_items[i].name; rlm@1: } rlm@1: delete[] m_items; rlm@1: delete[] m_filename; rlm@1: delete[] m_displayFilename; rlm@1: } rlm@1: rlm@1: const char* ArchiveFile::GetArchiveTypeName() rlm@1: { rlm@1: assert(!s_formatInfos.empty()); rlm@1: rlm@1: if((size_t)m_typeIndex >= s_formatInfos.size()) rlm@1: return ""; rlm@1: rlm@1: return s_formatInfos[m_typeIndex].name.c_str(); rlm@1: } rlm@1: rlm@1: int ArchiveFile::GetNumItems() rlm@1: { rlm@1: return m_numItems; rlm@1: } rlm@1: rlm@1: int ArchiveFile::GetItemSize(int item) rlm@1: { rlm@1: assert(item >= 0 && item < m_numItems); rlm@1: if(!(item >= 0 && item < m_numItems)) return 0; rlm@1: return m_items[item].size; rlm@1: } rlm@1: rlm@1: const char* ArchiveFile::GetItemName(int item) rlm@1: { rlm@1: //assert(item >= 0 && item < m_numItems); rlm@1: if(!(item >= 0 && item < m_numItems)) return ""; rlm@1: return m_items[item].name; rlm@1: } rlm@1: rlm@1: bool ArchiveFile::IsCompressed() rlm@1: { rlm@1: return (m_typeIndex >= 0); rlm@1: } rlm@1: rlm@1: int ArchiveFile::ExtractItem(int index, unsigned char* outBuffer, int bufSize) const rlm@1: { rlm@1: assert(!s_formatInfos.empty()); rlm@1: //assert(index >= 0 && index < m_numItems); rlm@1: if(!(index >= 0 && index < m_numItems)) return 0; rlm@1: rlm@1: ArchiveItem& item = m_items[index]; rlm@1: rlm@1: if(bufSize < item.size) rlm@1: return 0; rlm@1: rlm@1: if(m_typeIndex < 0) rlm@1: { rlm@1: // uncompressed rlm@1: FILE* file = fopen(m_filename, "rb"); rlm@1: fread(outBuffer, 1, item.size, file); rlm@1: fclose(file); rlm@1: } rlm@1: else rlm@1: { rlm@1: IInArchive* object = NULL; rlm@1: HRESULT hr = E_FAIL; rlm@1: if(SUCCEEDED(CreateObject(&s_formatInfos[m_typeIndex].guid, &IID_IInArchive, (void**)&object))) rlm@1: { rlm@1: InFileStream* ifs = new InFileStream(m_filename); rlm@1: if(SUCCEEDED(object->Open(ifs,0,0))) rlm@1: { rlm@1: OutStream* os = new OutStream(index, outBuffer, item.size); rlm@1: const UInt32 indices [1] = {index}; rlm@1: hr = object->Extract(indices, 1, 0, os); rlm@1: object->Close(); rlm@1: } rlm@1: object->Release(); rlm@1: } rlm@1: if(FAILED(hr)) rlm@1: return 0; rlm@1: } rlm@1: rlm@1: return item.size; rlm@1: } rlm@1: rlm@1: rlm@1: rlm@1: int ArchiveFile::ExtractItem(int index, const char* outFilename) const rlm@1: { rlm@1: assert(!s_formatInfos.empty()); rlm@1: //assert(index >= 0 && index < m_numItems); rlm@1: if(!(index >= 0 && index < m_numItems)) return 0; rlm@1: rlm@1: ArchiveItem& item = m_items[index]; rlm@1: int rv = item.size; rlm@1: rlm@1: DWORD outAttributes = GetFileAttributes(outFilename); rlm@1: if(outAttributes & FILE_ATTRIBUTE_READONLY) rlm@1: SetFileAttributes(outFilename, outAttributes & ~FILE_ATTRIBUTE_READONLY); // temporarily remove read-only attribute so we can decompress to there rlm@1: rlm@1: if(m_typeIndex < 0) rlm@1: { rlm@1: // uncompressed rlm@1: if(!CopyFile(m_filename, outFilename, false)) rlm@1: rv = 0; rlm@1: } rlm@1: else rlm@1: { rlm@1: IInArchive* object = NULL; rlm@1: HRESULT hr = E_FAIL; rlm@1: if(SUCCEEDED(CreateObject(&s_formatInfos[m_typeIndex].guid, &IID_IInArchive, (void**)&object))) rlm@1: { rlm@1: InFileStream* ifs = new InFileStream(m_filename); rlm@1: if(SUCCEEDED(object->Open(ifs,0,0))) rlm@1: { rlm@1: OutStream* os = new OutStream(index, outFilename); rlm@1: const UInt32 indices [1] = {index}; rlm@1: hr = object->Extract(indices, 1, 0, os); rlm@1: object->Close(); rlm@1: } rlm@1: object->Release(); rlm@1: } rlm@1: if(FAILED(hr)) rlm@1: rv = 0; rlm@1: } rlm@1: rlm@1: if(outAttributes & FILE_ATTRIBUTE_READONLY) rlm@1: SetFileAttributes(outFilename, outAttributes); // restore read-only attribute rlm@1: rlm@1: return rv; rlm@1: }