rlm@1
|
1 #include "7zip.h"
|
rlm@1
|
2
|
rlm@1
|
3 #include "7z/C/Types.h"
|
rlm@1
|
4 #include "7z/CPP/7zip/Archive/IArchive.h"
|
rlm@1
|
5 #include "7z/CPP/Common/InitializeStaticLib.h" // important! (if using a static lib)
|
rlm@1
|
6
|
rlm@1
|
7 #include <string>
|
rlm@1
|
8 #include <vector>
|
rlm@1
|
9 #include <cassert>
|
rlm@1
|
10 #include <malloc.h>
|
rlm@1
|
11
|
rlm@1
|
12 #include "7zipstreams.h" // defines OutStream and InFileStream
|
rlm@1
|
13
|
rlm@1
|
14 STDAPI GetNumberOfFormats(UINT32 *numFormats);
|
rlm@1
|
15 STDAPI GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT *value);
|
rlm@1
|
16 STDAPI CreateObject(const GUID *clsid, const GUID *iid, void **outObject);
|
rlm@1
|
17
|
rlm@1
|
18 struct ArchiveFormatInfo
|
rlm@1
|
19 {
|
rlm@1
|
20 std::string name;
|
rlm@1
|
21 std::vector<std::string> extensions;
|
rlm@1
|
22 std::string signature;
|
rlm@1
|
23 GUID guid;
|
rlm@1
|
24 };
|
rlm@1
|
25
|
rlm@1
|
26 static std::vector<ArchiveFormatInfo> s_formatInfos;
|
rlm@1
|
27
|
rlm@1
|
28 static std::string wstrToStr(const wchar_t* wstr)
|
rlm@1
|
29 {
|
rlm@1
|
30 /*
|
rlm@1
|
31 // This thing wouldn't work
|
rlm@1
|
32 char* str = (char*)_alloca((wcslen(wstr)+1));
|
rlm@1
|
33 sprintf(str, "%S", wstr);
|
rlm@1
|
34 return std::string(str);
|
rlm@1
|
35 */
|
rlm@1
|
36 // setlocale(LC_CTYPE, ".ACP");
|
rlm@1
|
37 size_t n = wcstombs(NULL, wstr, 0);
|
rlm@1
|
38 if (n == (size_t)-1) // failed
|
rlm@1
|
39 {
|
rlm@1
|
40 // setlocale(LC_CTYPE, "C");
|
rlm@1
|
41 return std::string();
|
rlm@1
|
42 }
|
rlm@1
|
43 char* str = (char*)_alloca(n + 1);
|
rlm@1
|
44 wcstombs(str, wstr, n);
|
rlm@1
|
45 str[n] = '\0';
|
rlm@1
|
46 // setlocale(LC_CTYPE, "C");
|
rlm@1
|
47 return std::string(str);
|
rlm@1
|
48 }
|
rlm@1
|
49
|
rlm@1
|
50 static std::vector<std::string> tokenize(const std::string & str, const std::string & delim)
|
rlm@1
|
51 {
|
rlm@1
|
52 std::vector<std::string> tokens;
|
rlm@1
|
53 size_t p0 = 0, p1 = std::string::npos;
|
rlm@1
|
54 while(p0 != std::string::npos)
|
rlm@1
|
55 {
|
rlm@1
|
56 p1 = str.find_first_of(delim, p0);
|
rlm@1
|
57 if(p1 != p0)
|
rlm@1
|
58 {
|
rlm@1
|
59 std::string token = str.substr(p0, p1 - p0);
|
rlm@1
|
60 tokens.push_back(token);
|
rlm@1
|
61 }
|
rlm@1
|
62 p0 = str.find_first_not_of(delim, p1);
|
rlm@1
|
63 }
|
rlm@1
|
64 return tokens;
|
rlm@1
|
65 }
|
rlm@1
|
66
|
rlm@1
|
67 static std::string s_supportedFormatsFilter;
|
rlm@1
|
68 const char* GetSupportedFormatsFilter()
|
rlm@1
|
69 {
|
rlm@1
|
70 assert(!s_formatInfos.empty());
|
rlm@1
|
71 if(s_supportedFormatsFilter.empty())
|
rlm@1
|
72 {
|
rlm@1
|
73 s_supportedFormatsFilter = "";
|
rlm@1
|
74 for(size_t i = 0; i < s_formatInfos.size(); i++)
|
rlm@1
|
75 {
|
rlm@1
|
76 for(size_t j = 0; j < s_formatInfos[i].extensions.size(); j++)
|
rlm@1
|
77 {
|
rlm@1
|
78 s_supportedFormatsFilter += ";*.";
|
rlm@1
|
79 s_supportedFormatsFilter += s_formatInfos[i].extensions[j];
|
rlm@1
|
80 }
|
rlm@1
|
81 }
|
rlm@1
|
82 }
|
rlm@1
|
83 return s_supportedFormatsFilter.c_str();
|
rlm@1
|
84 }
|
rlm@1
|
85
|
rlm@1
|
86 void InitDecoder()
|
rlm@1
|
87 {
|
rlm@1
|
88 CleanupDecoder();
|
rlm@1
|
89
|
rlm@1
|
90 UINT32 numFormats = 0;
|
rlm@1
|
91 GetNumberOfFormats(&numFormats);
|
rlm@1
|
92
|
rlm@1
|
93 for(unsigned int i = 0; i < numFormats; i++)
|
rlm@1
|
94 {
|
rlm@1
|
95 PROPVARIANT var = {VT_EMPTY};
|
rlm@1
|
96 ArchiveFormatInfo info;
|
rlm@1
|
97
|
rlm@1
|
98 GetHandlerProperty2(i, NArchive::kName, &var);
|
rlm@1
|
99 if(var.vt == VT_BSTR)
|
rlm@1
|
100 info.name = wstrToStr(var.bstrVal);
|
rlm@1
|
101
|
rlm@1
|
102 GetHandlerProperty2(i, NArchive::kExtension, &var);
|
rlm@1
|
103 if(var.vt == VT_BSTR)
|
rlm@1
|
104 info.extensions = tokenize(wstrToStr(var.bstrVal), " ");
|
rlm@1
|
105
|
rlm@1
|
106 GetHandlerProperty2(i, NArchive::kStartSignature, &var);
|
rlm@1
|
107 if(var.vt == VT_BSTR)
|
rlm@1
|
108 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
|
109
|
rlm@1
|
110 GetHandlerProperty2(i,NArchive::kClassID,&var);
|
rlm@1
|
111 if(var.vt == VT_BSTR)
|
rlm@1
|
112 memcpy(&info.guid, var.bstrVal, 16);
|
rlm@1
|
113 else
|
rlm@1
|
114 memset(&info.guid, 0, 16);
|
rlm@1
|
115
|
rlm@1
|
116 s_formatInfos.push_back(info);
|
rlm@1
|
117
|
rlm@1
|
118 VariantClear((VARIANTARG*)&var);
|
rlm@1
|
119 }
|
rlm@1
|
120 }
|
rlm@1
|
121
|
rlm@1
|
122 void CleanupDecoder()
|
rlm@1
|
123 {
|
rlm@1
|
124 s_formatInfos.clear();
|
rlm@1
|
125 s_supportedFormatsFilter.clear();
|
rlm@1
|
126 }
|
rlm@1
|
127
|
rlm@1
|
128 #include "7z/CPP/7zip/Archive/Zip/ZipHandler.h"
|
rlm@1
|
129
|
rlm@1
|
130
|
rlm@1
|
131 ArchiveFile::ArchiveFile(const char* filename, const char* displayFilename)
|
rlm@1
|
132 {
|
rlm@1
|
133 assert(!s_formatInfos.empty());
|
rlm@1
|
134
|
rlm@1
|
135 m_typeIndex = -1;
|
rlm@1
|
136 m_numItems = 0;
|
rlm@1
|
137 m_items = NULL;
|
rlm@1
|
138 m_filename = NULL;
|
rlm@1
|
139 m_displayFilename = NULL;
|
rlm@1
|
140 m_userMadeSelection = false;
|
rlm@1
|
141
|
rlm@1
|
142 FILE* file = fopen(filename, "rb");
|
rlm@1
|
143 if(!file)
|
rlm@1
|
144 return;
|
rlm@1
|
145
|
rlm@1
|
146 m_filename = new char[strlen(filename)+1];
|
rlm@1
|
147 strcpy(m_filename, filename);
|
rlm@1
|
148
|
rlm@1
|
149 if(displayFilename)
|
rlm@1
|
150 {
|
rlm@1
|
151 m_displayFilename = new char[strlen(displayFilename)+1];
|
rlm@1
|
152 strcpy(m_displayFilename, displayFilename);
|
rlm@1
|
153 }
|
rlm@1
|
154
|
rlm@1
|
155 // detect archive type using format signature in file
|
rlm@1
|
156 for(size_t i = 0; i < s_formatInfos.size() && m_typeIndex < 0; i++)
|
rlm@1
|
157 {
|
rlm@1
|
158 fseek(file, 0, SEEK_SET);
|
rlm@1
|
159
|
rlm@1
|
160 std::string& formatSig = s_formatInfos[i].signature;
|
rlm@1
|
161 int len = formatSig.size();
|
rlm@1
|
162
|
rlm@1
|
163 if(len == 0)
|
rlm@1
|
164 continue; // because some formats have no signature
|
rlm@1
|
165
|
rlm@1
|
166 char* fileSig = (char*)_alloca(len);
|
rlm@1
|
167 fread(fileSig, 1, len, file);
|
rlm@1
|
168
|
rlm@1
|
169 if(!memcmp(formatSig.c_str(), fileSig, len))
|
rlm@1
|
170 m_typeIndex = i;
|
rlm@1
|
171 }
|
rlm@1
|
172
|
rlm@1
|
173 // if no signature match has been found, detect archive type using filename.
|
rlm@1
|
174 // this is only for signature-less formats
|
rlm@1
|
175 const char* fileExt = strrchr(filename, '.');
|
rlm@1
|
176 if(fileExt++)
|
rlm@1
|
177 {
|
rlm@1
|
178 for(size_t i = 0; i < s_formatInfos.size() && m_typeIndex < 0; i++)
|
rlm@1
|
179 {
|
rlm@1
|
180 if(s_formatInfos[i].signature.empty())
|
rlm@1
|
181 {
|
rlm@1
|
182 std::vector<std::string>& formatExts = s_formatInfos[i].extensions;
|
rlm@1
|
183 for(size_t j = 0; j < formatExts.size(); j++)
|
rlm@1
|
184 {
|
rlm@1
|
185 if(!_stricmp(formatExts[j].c_str(), fileExt))
|
rlm@1
|
186 {
|
rlm@1
|
187 m_typeIndex = i;
|
rlm@1
|
188 break;
|
rlm@1
|
189 }
|
rlm@1
|
190 }
|
rlm@1
|
191 }
|
rlm@1
|
192 }
|
rlm@1
|
193 }
|
rlm@1
|
194
|
rlm@1
|
195 if(m_typeIndex < 0)
|
rlm@1
|
196 {
|
rlm@1
|
197 // uncompressed
|
rlm@1
|
198
|
rlm@1
|
199 m_numItems = 1;
|
rlm@1
|
200 m_items = new ArchiveItem[m_numItems];
|
rlm@1
|
201
|
rlm@1
|
202 fseek(file, 0, SEEK_END);
|
rlm@1
|
203 m_items[0].size = ftell(file);
|
rlm@1
|
204
|
rlm@1
|
205 m_items[0].name = new char[strlen(filename)+1];
|
rlm@1
|
206 strcpy(m_items[0].name, filename);
|
rlm@1
|
207 }
|
rlm@1
|
208 else
|
rlm@1
|
209 {
|
rlm@1
|
210 IInArchive* object = NULL;
|
rlm@1
|
211 if(SUCCEEDED(CreateObject(&s_formatInfos[m_typeIndex].guid, &IID_IInArchive, (void**)&object)))
|
rlm@1
|
212 {
|
rlm@1
|
213 InFileStream* ifs = new InFileStream(filename);
|
rlm@1
|
214 if(SUCCEEDED(object->Open(ifs,0,0)))
|
rlm@1
|
215 {
|
rlm@1
|
216 UInt32 numItems = 0;
|
rlm@1
|
217 object->GetNumberOfItems(&numItems);
|
rlm@1
|
218 m_numItems = numItems;
|
rlm@1
|
219 m_items = new ArchiveItem[m_numItems];
|
rlm@1
|
220
|
rlm@1
|
221 for(int i = 0; i < m_numItems; i++)
|
rlm@1
|
222 {
|
rlm@1
|
223 PROPVARIANT var = {VT_EMPTY};
|
rlm@1
|
224 ArchiveItem& item = m_items[i];
|
rlm@1
|
225
|
rlm@1
|
226 object->GetProperty(i, kpidSize, &var);
|
rlm@1
|
227 item.size = var.uhVal.LowPart;
|
rlm@1
|
228
|
rlm@1
|
229 object->GetProperty(i, kpidPath, &var);
|
rlm@1
|
230 std::string& path = wstrToStr(var.bstrVal);
|
rlm@1
|
231 item.name = new char[path.size()+1];
|
rlm@1
|
232 strcpy(item.name, path.c_str());
|
rlm@1
|
233
|
rlm@1
|
234 //object->GetProperty(i, kpidMethod, &var);
|
rlm@1
|
235 //std::string& method = wstrToStr(var.bstrVal);
|
rlm@1
|
236 //item.method = new char[method.size()+1];
|
rlm@1
|
237 //strcpy(item.method, method.c_str());
|
rlm@1
|
238
|
rlm@1
|
239 object->GetProperty(i, kpidEncrypted, &var);
|
rlm@1
|
240 #ifdef _NO_CRYPTO
|
rlm@1
|
241 if(var.boolVal)
|
rlm@1
|
242 item.size = 0; // don't support decompressing it, pretend size zero
|
rlm@1
|
243 #else
|
rlm@1
|
244 #error password support NYI... see client7z.cpp
|
rlm@1
|
245 item.encrypted = !!var.boolVal;
|
rlm@1
|
246 #endif
|
rlm@1
|
247
|
rlm@1
|
248 VariantClear((VARIANTARG*)&var);
|
rlm@1
|
249 }
|
rlm@1
|
250
|
rlm@1
|
251 object->Close();
|
rlm@1
|
252 }
|
rlm@1
|
253
|
rlm@1
|
254 object->Release();
|
rlm@1
|
255 }
|
rlm@1
|
256 }
|
rlm@1
|
257
|
rlm@1
|
258 fclose(file);
|
rlm@1
|
259 }
|
rlm@1
|
260
|
rlm@1
|
261 ArchiveFile::~ArchiveFile()
|
rlm@1
|
262 {
|
rlm@1
|
263 for(int i = 0; i < m_numItems; i++)
|
rlm@1
|
264 {
|
rlm@1
|
265 delete[] m_items[i].name;
|
rlm@1
|
266 }
|
rlm@1
|
267 delete[] m_items;
|
rlm@1
|
268 delete[] m_filename;
|
rlm@1
|
269 delete[] m_displayFilename;
|
rlm@1
|
270 }
|
rlm@1
|
271
|
rlm@1
|
272 const char* ArchiveFile::GetArchiveTypeName()
|
rlm@1
|
273 {
|
rlm@1
|
274 assert(!s_formatInfos.empty());
|
rlm@1
|
275
|
rlm@1
|
276 if((size_t)m_typeIndex >= s_formatInfos.size())
|
rlm@1
|
277 return "";
|
rlm@1
|
278
|
rlm@1
|
279 return s_formatInfos[m_typeIndex].name.c_str();
|
rlm@1
|
280 }
|
rlm@1
|
281
|
rlm@1
|
282 int ArchiveFile::GetNumItems()
|
rlm@1
|
283 {
|
rlm@1
|
284 return m_numItems;
|
rlm@1
|
285 }
|
rlm@1
|
286
|
rlm@1
|
287 int ArchiveFile::GetItemSize(int item)
|
rlm@1
|
288 {
|
rlm@1
|
289 assert(item >= 0 && item < m_numItems);
|
rlm@1
|
290 if(!(item >= 0 && item < m_numItems)) return 0;
|
rlm@1
|
291 return m_items[item].size;
|
rlm@1
|
292 }
|
rlm@1
|
293
|
rlm@1
|
294 const char* ArchiveFile::GetItemName(int item)
|
rlm@1
|
295 {
|
rlm@1
|
296 //assert(item >= 0 && item < m_numItems);
|
rlm@1
|
297 if(!(item >= 0 && item < m_numItems)) return "";
|
rlm@1
|
298 return m_items[item].name;
|
rlm@1
|
299 }
|
rlm@1
|
300
|
rlm@1
|
301 bool ArchiveFile::IsCompressed()
|
rlm@1
|
302 {
|
rlm@1
|
303 return (m_typeIndex >= 0);
|
rlm@1
|
304 }
|
rlm@1
|
305
|
rlm@1
|
306 int ArchiveFile::ExtractItem(int index, unsigned char* outBuffer, int bufSize) const
|
rlm@1
|
307 {
|
rlm@1
|
308 assert(!s_formatInfos.empty());
|
rlm@1
|
309 //assert(index >= 0 && index < m_numItems);
|
rlm@1
|
310 if(!(index >= 0 && index < m_numItems)) return 0;
|
rlm@1
|
311
|
rlm@1
|
312 ArchiveItem& item = m_items[index];
|
rlm@1
|
313
|
rlm@1
|
314 if(bufSize < item.size)
|
rlm@1
|
315 return 0;
|
rlm@1
|
316
|
rlm@1
|
317 if(m_typeIndex < 0)
|
rlm@1
|
318 {
|
rlm@1
|
319 // uncompressed
|
rlm@1
|
320 FILE* file = fopen(m_filename, "rb");
|
rlm@1
|
321 fread(outBuffer, 1, item.size, file);
|
rlm@1
|
322 fclose(file);
|
rlm@1
|
323 }
|
rlm@1
|
324 else
|
rlm@1
|
325 {
|
rlm@1
|
326 IInArchive* object = NULL;
|
rlm@1
|
327 HRESULT hr = E_FAIL;
|
rlm@1
|
328 if(SUCCEEDED(CreateObject(&s_formatInfos[m_typeIndex].guid, &IID_IInArchive, (void**)&object)))
|
rlm@1
|
329 {
|
rlm@1
|
330 InFileStream* ifs = new InFileStream(m_filename);
|
rlm@1
|
331 if(SUCCEEDED(object->Open(ifs,0,0)))
|
rlm@1
|
332 {
|
rlm@1
|
333 OutStream* os = new OutStream(index, outBuffer, item.size);
|
rlm@1
|
334 const UInt32 indices [1] = {index};
|
rlm@1
|
335 hr = object->Extract(indices, 1, 0, os);
|
rlm@1
|
336 object->Close();
|
rlm@1
|
337 }
|
rlm@1
|
338 object->Release();
|
rlm@1
|
339 }
|
rlm@1
|
340 if(FAILED(hr))
|
rlm@1
|
341 return 0;
|
rlm@1
|
342 }
|
rlm@1
|
343
|
rlm@1
|
344 return item.size;
|
rlm@1
|
345 }
|
rlm@1
|
346
|
rlm@1
|
347
|
rlm@1
|
348
|
rlm@1
|
349 int ArchiveFile::ExtractItem(int index, const char* outFilename) const
|
rlm@1
|
350 {
|
rlm@1
|
351 assert(!s_formatInfos.empty());
|
rlm@1
|
352 //assert(index >= 0 && index < m_numItems);
|
rlm@1
|
353 if(!(index >= 0 && index < m_numItems)) return 0;
|
rlm@1
|
354
|
rlm@1
|
355 ArchiveItem& item = m_items[index];
|
rlm@1
|
356 int rv = item.size;
|
rlm@1
|
357
|
rlm@1
|
358 DWORD outAttributes = GetFileAttributes(outFilename);
|
rlm@1
|
359 if(outAttributes & FILE_ATTRIBUTE_READONLY)
|
rlm@1
|
360 SetFileAttributes(outFilename, outAttributes & ~FILE_ATTRIBUTE_READONLY); // temporarily remove read-only attribute so we can decompress to there
|
rlm@1
|
361
|
rlm@1
|
362 if(m_typeIndex < 0)
|
rlm@1
|
363 {
|
rlm@1
|
364 // uncompressed
|
rlm@1
|
365 if(!CopyFile(m_filename, outFilename, false))
|
rlm@1
|
366 rv = 0;
|
rlm@1
|
367 }
|
rlm@1
|
368 else
|
rlm@1
|
369 {
|
rlm@1
|
370 IInArchive* object = NULL;
|
rlm@1
|
371 HRESULT hr = E_FAIL;
|
rlm@1
|
372 if(SUCCEEDED(CreateObject(&s_formatInfos[m_typeIndex].guid, &IID_IInArchive, (void**)&object)))
|
rlm@1
|
373 {
|
rlm@1
|
374 InFileStream* ifs = new InFileStream(m_filename);
|
rlm@1
|
375 if(SUCCEEDED(object->Open(ifs,0,0)))
|
rlm@1
|
376 {
|
rlm@1
|
377 OutStream* os = new OutStream(index, outFilename);
|
rlm@1
|
378 const UInt32 indices [1] = {index};
|
rlm@1
|
379 hr = object->Extract(indices, 1, 0, os);
|
rlm@1
|
380 object->Close();
|
rlm@1
|
381 }
|
rlm@1
|
382 object->Release();
|
rlm@1
|
383 }
|
rlm@1
|
384 if(FAILED(hr))
|
rlm@1
|
385 rv = 0;
|
rlm@1
|
386 }
|
rlm@1
|
387
|
rlm@1
|
388 if(outAttributes & FILE_ATTRIBUTE_READONLY)
|
rlm@1
|
389 SetFileAttributes(outFilename, outAttributes); // restore read-only attribute
|
rlm@1
|
390
|
rlm@1
|
391 return rv;
|
rlm@1
|
392 }
|