|
1 /* |
|
2 7zMain.c |
|
3 Test application for 7z Decoder |
|
4 LZMA SDK 4.43 Copyright (c) 1999-2006 Igor Pavlov (2006-06-04) |
|
5 */ |
|
6 |
|
7 #include <stdio.h> |
|
8 #include <stdlib.h> |
|
9 #include <string.h> |
|
10 |
|
11 #ifdef _WIN32 |
|
12 #define USE_WINDOWS_FUNCTIONS |
|
13 #endif |
|
14 |
|
15 #ifdef USE_WINDOWS_FUNCTIONS |
|
16 #include <windows.h> |
|
17 #endif |
|
18 |
|
19 #include "7zIn.h" |
|
20 #include "7zExtract.h" |
|
21 |
|
22 #include "../../7zCrc.h" |
|
23 |
|
24 |
|
25 #ifdef USE_WINDOWS_FUNCTIONS |
|
26 typedef HANDLE MY_FILE_HANDLE; |
|
27 #else |
|
28 typedef FILE *MY_FILE_HANDLE; |
|
29 #endif |
|
30 |
|
31 void ConvertNumberToString(CFileSize value, char *s) |
|
32 { |
|
33 char temp[32]; |
|
34 int pos = 0; |
|
35 do |
|
36 { |
|
37 temp[pos++] = (char)('0' + (int)(value % 10)); |
|
38 value /= 10; |
|
39 } |
|
40 while (value != 0); |
|
41 do |
|
42 *s++ = temp[--pos]; |
|
43 while(pos > 0); |
|
44 *s = '\0'; |
|
45 } |
|
46 |
|
47 #define PERIOD_4 (4 * 365 + 1) |
|
48 #define PERIOD_100 (PERIOD_4 * 25 - 1) |
|
49 #define PERIOD_400 (PERIOD_100 * 4 + 1) |
|
50 |
|
51 void ConvertFileTimeToString(CArchiveFileTime *ft, char *s) |
|
52 { |
|
53 unsigned year, mon, day, hour, min, sec; |
|
54 UInt64 v64 = ft->Low | ((UInt64)ft->High << 32); |
|
55 Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
|
56 unsigned temp; |
|
57 UInt32 v; |
|
58 v64 /= 10000000; |
|
59 sec = (unsigned)(v64 % 60); |
|
60 v64 /= 60; |
|
61 min = (unsigned)(v64 % 60); |
|
62 v64 /= 60; |
|
63 hour = (unsigned)(v64 % 24); |
|
64 v64 /= 24; |
|
65 |
|
66 v = (UInt32)v64; |
|
67 |
|
68 year = (unsigned)(1601 + v / PERIOD_400 * 400); |
|
69 v %= PERIOD_400; |
|
70 |
|
71 temp = (unsigned)(v / PERIOD_100); |
|
72 if (temp == 4) |
|
73 temp = 3; |
|
74 year += temp * 100; |
|
75 v -= temp * PERIOD_100; |
|
76 |
|
77 temp = v / PERIOD_4; |
|
78 if (temp == 25) |
|
79 temp = 24; |
|
80 year += temp * 4; |
|
81 v -= temp * PERIOD_4; |
|
82 |
|
83 temp = v / 365; |
|
84 if (temp == 4) |
|
85 temp = 3; |
|
86 year += temp; |
|
87 v -= temp * 365; |
|
88 |
|
89 if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) |
|
90 ms[1] = 29; |
|
91 for (mon = 1; mon <= 12; mon++) |
|
92 { |
|
93 unsigned s = ms[mon - 1]; |
|
94 if (v < s) |
|
95 break; |
|
96 v -= s; |
|
97 } |
|
98 day = (unsigned)v + 1; |
|
99 sprintf(s, "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec); |
|
100 } |
|
101 |
|
102 |
|
103 #ifdef USE_WINDOWS_FUNCTIONS |
|
104 /* |
|
105 ReadFile and WriteFile functions in Windows have BUG: |
|
106 If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) |
|
107 from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES |
|
108 (Insufficient system resources exist to complete the requested service). |
|
109 */ |
|
110 #define kChunkSizeMax (1 << 24) |
|
111 #endif |
|
112 |
|
113 size_t MyReadFile(MY_FILE_HANDLE file, void *data, size_t size) |
|
114 { |
|
115 if (size == 0) |
|
116 return 0; |
|
117 #ifdef USE_WINDOWS_FUNCTIONS |
|
118 { |
|
119 size_t processedSize = 0; |
|
120 do |
|
121 { |
|
122 DWORD curSize = (size > kChunkSizeMax) ? kChunkSizeMax : (DWORD)size; |
|
123 DWORD processedLoc = 0; |
|
124 BOOL res = ReadFile(file, data, curSize, &processedLoc, NULL); |
|
125 data = (void *)((unsigned char *)data + processedLoc); |
|
126 size -= processedLoc; |
|
127 processedSize += processedLoc; |
|
128 if (!res || processedLoc == 0) |
|
129 break; |
|
130 } |
|
131 while (size > 0); |
|
132 return processedSize; |
|
133 } |
|
134 #else |
|
135 return fread(data, 1, size, file); |
|
136 #endif |
|
137 } |
|
138 |
|
139 size_t MyWriteFile(MY_FILE_HANDLE file, void *data, size_t size) |
|
140 { |
|
141 if (size == 0) |
|
142 return 0; |
|
143 #ifdef USE_WINDOWS_FUNCTIONS |
|
144 { |
|
145 size_t processedSize = 0; |
|
146 do |
|
147 { |
|
148 DWORD curSize = (size > kChunkSizeMax) ? kChunkSizeMax : (DWORD)size; |
|
149 DWORD processedLoc = 0; |
|
150 BOOL res = WriteFile(file, data, curSize, &processedLoc, NULL); |
|
151 data = (void *)((unsigned char *)data + processedLoc); |
|
152 size -= processedLoc; |
|
153 processedSize += processedLoc; |
|
154 if (!res) |
|
155 break; |
|
156 } |
|
157 while (size > 0); |
|
158 return processedSize; |
|
159 } |
|
160 #else |
|
161 return fwrite(data, 1, size, file); |
|
162 #endif |
|
163 } |
|
164 |
|
165 int MyCloseFile(MY_FILE_HANDLE file) |
|
166 { |
|
167 #ifdef USE_WINDOWS_FUNCTIONS |
|
168 return (CloseHandle(file) != FALSE) ? 0 : 1; |
|
169 #else |
|
170 return fclose(file); |
|
171 #endif |
|
172 } |
|
173 |
|
174 typedef struct _CFileInStream |
|
175 { |
|
176 ISzInStream InStream; |
|
177 MY_FILE_HANDLE File; |
|
178 } CFileInStream; |
|
179 |
|
180 #ifdef _LZMA_IN_CB |
|
181 |
|
182 #define kBufferSize (1 << 12) |
|
183 Byte g_Buffer[kBufferSize]; |
|
184 |
|
185 SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxRequiredSize, size_t *processedSize) |
|
186 { |
|
187 CFileInStream *s = (CFileInStream *)object; |
|
188 size_t processedSizeLoc; |
|
189 if (maxRequiredSize > kBufferSize) |
|
190 maxRequiredSize = kBufferSize; |
|
191 processedSizeLoc = MyReadFile(s->File, g_Buffer, maxRequiredSize); |
|
192 *buffer = g_Buffer; |
|
193 if (processedSize != 0) |
|
194 *processedSize = processedSizeLoc; |
|
195 return SZ_OK; |
|
196 } |
|
197 |
|
198 #else |
|
199 |
|
200 SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size, size_t *processedSize) |
|
201 { |
|
202 CFileInStream *s = (CFileInStream *)object; |
|
203 size_t processedSizeLoc = MyReadFile(s->File, buffer, size); |
|
204 if (processedSize != 0) |
|
205 *processedSize = processedSizeLoc; |
|
206 return SZ_OK; |
|
207 } |
|
208 |
|
209 #endif |
|
210 |
|
211 SZ_RESULT SzFileSeekImp(void *object, CFileSize pos) |
|
212 { |
|
213 CFileInStream *s = (CFileInStream *)object; |
|
214 |
|
215 #ifdef USE_WINDOWS_FUNCTIONS |
|
216 { |
|
217 LARGE_INTEGER value; |
|
218 value.LowPart = (DWORD)pos; |
|
219 value.HighPart = (LONG)((UInt64)pos >> 32); |
|
220 #ifdef _SZ_FILE_SIZE_32 |
|
221 /* VC 6.0 has bug with >> 32 shifts. */ |
|
222 value.HighPart = 0; |
|
223 #endif |
|
224 value.LowPart = SetFilePointer(s->File, value.LowPart, &value.HighPart, FILE_BEGIN); |
|
225 if (value.LowPart == 0xFFFFFFFF) |
|
226 if(GetLastError() != NO_ERROR) |
|
227 return SZE_FAIL; |
|
228 return SZ_OK; |
|
229 } |
|
230 #else |
|
231 int res = fseek(s->File, (long)pos, SEEK_SET); |
|
232 if (res == 0) |
|
233 return SZ_OK; |
|
234 return SZE_FAIL; |
|
235 #endif |
|
236 } |
|
237 |
|
238 void PrintError(char *sz) |
|
239 { |
|
240 printf("\nERROR: %s\n", sz); |
|
241 } |
|
242 |
|
243 int main(int numargs, char *args[]) |
|
244 { |
|
245 CFileInStream archiveStream; |
|
246 CArchiveDatabaseEx db; |
|
247 SZ_RESULT res; |
|
248 ISzAlloc allocImp; |
|
249 ISzAlloc allocTempImp; |
|
250 |
|
251 printf("\n7z ANSI-C Decoder 4.48 Copyright (c) 1999-2007 Igor Pavlov 2007-06-21\n"); |
|
252 if (numargs == 1) |
|
253 { |
|
254 printf( |
|
255 "\nUsage: 7zDec <command> <archive_name>\n\n" |
|
256 "<Commands>\n" |
|
257 " e: Extract files from archive\n" |
|
258 " l: List contents of archive\n" |
|
259 " t: Test integrity of archive\n"); |
|
260 return 0; |
|
261 } |
|
262 if (numargs < 3) |
|
263 { |
|
264 PrintError("incorrect command"); |
|
265 return 1; |
|
266 } |
|
267 |
|
268 archiveStream.File = |
|
269 #ifdef USE_WINDOWS_FUNCTIONS |
|
270 CreateFile(args[2], GENERIC_READ, FILE_SHARE_READ, |
|
271 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
|
272 if (archiveStream.File == INVALID_HANDLE_VALUE) |
|
273 #else |
|
274 archiveStream.File = fopen(args[2], "rb"); |
|
275 if (archiveStream.File == 0) |
|
276 #endif |
|
277 { |
|
278 PrintError("can not open input file"); |
|
279 return 1; |
|
280 } |
|
281 |
|
282 archiveStream.InStream.Read = SzFileReadImp; |
|
283 archiveStream.InStream.Seek = SzFileSeekImp; |
|
284 |
|
285 allocImp.Alloc = SzAlloc; |
|
286 allocImp.Free = SzFree; |
|
287 |
|
288 allocTempImp.Alloc = SzAllocTemp; |
|
289 allocTempImp.Free = SzFreeTemp; |
|
290 |
|
291 CrcGenerateTable(); |
|
292 |
|
293 SzArDbExInit(&db); |
|
294 res = SzArchiveOpen(&archiveStream.InStream, &db, &allocImp, &allocTempImp); |
|
295 if (res == SZ_OK) |
|
296 { |
|
297 char *command = args[1]; |
|
298 int listCommand = 0; |
|
299 int testCommand = 0; |
|
300 int extractCommand = 0; |
|
301 if (strcmp(command, "l") == 0) |
|
302 listCommand = 1; |
|
303 if (strcmp(command, "t") == 0) |
|
304 testCommand = 1; |
|
305 else if (strcmp(command, "e") == 0) |
|
306 extractCommand = 1; |
|
307 |
|
308 if (listCommand) |
|
309 { |
|
310 UInt32 i; |
|
311 for (i = 0; i < db.Database.NumFiles; i++) |
|
312 { |
|
313 CFileItem *f = db.Database.Files + i; |
|
314 char s[32], t[32]; |
|
315 ConvertNumberToString(f->Size, s); |
|
316 if (f->IsLastWriteTimeDefined) |
|
317 ConvertFileTimeToString(&f->LastWriteTime, t); |
|
318 else |
|
319 strcpy(t, " "); |
|
320 |
|
321 printf("%10s %s %s\n", s, t, f->Name); |
|
322 } |
|
323 } |
|
324 else if (testCommand || extractCommand) |
|
325 { |
|
326 UInt32 i; |
|
327 |
|
328 /* |
|
329 if you need cache, use these 3 variables. |
|
330 if you use external function, you can make these variable as static. |
|
331 */ |
|
332 UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ |
|
333 Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ |
|
334 size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ |
|
335 |
|
336 printf("\n"); |
|
337 for (i = 0; i < db.Database.NumFiles; i++) |
|
338 { |
|
339 size_t offset; |
|
340 size_t outSizeProcessed; |
|
341 CFileItem *f = db.Database.Files + i; |
|
342 if (f->IsDirectory) |
|
343 printf("Directory "); |
|
344 else |
|
345 printf(testCommand ? |
|
346 "Testing ": |
|
347 "Extracting"); |
|
348 printf(" %s", f->Name); |
|
349 if (f->IsDirectory) |
|
350 { |
|
351 printf("\n"); |
|
352 continue; |
|
353 } |
|
354 res = SzExtract(&archiveStream.InStream, &db, i, |
|
355 &blockIndex, &outBuffer, &outBufferSize, |
|
356 &offset, &outSizeProcessed, |
|
357 &allocImp, &allocTempImp); |
|
358 if (res != SZ_OK) |
|
359 break; |
|
360 if (!testCommand) |
|
361 { |
|
362 MY_FILE_HANDLE outputHandle; |
|
363 size_t processedSize; |
|
364 char *fileName = f->Name; |
|
365 size_t nameLen = strlen(f->Name); |
|
366 for (; nameLen > 0; nameLen--) |
|
367 if (f->Name[nameLen - 1] == '/') |
|
368 { |
|
369 fileName = f->Name + nameLen; |
|
370 break; |
|
371 } |
|
372 |
|
373 outputHandle = |
|
374 #ifdef USE_WINDOWS_FUNCTIONS |
|
375 CreateFile(fileName, GENERIC_WRITE, FILE_SHARE_READ, |
|
376 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
|
377 if (outputHandle == INVALID_HANDLE_VALUE) |
|
378 #else |
|
379 fopen(fileName, "wb+"); |
|
380 if (outputHandle == 0) |
|
381 #endif |
|
382 { |
|
383 PrintError("can not open output file"); |
|
384 res = SZE_FAIL; |
|
385 break; |
|
386 } |
|
387 processedSize = MyWriteFile(outputHandle, outBuffer + offset, outSizeProcessed); |
|
388 if (processedSize != outSizeProcessed) |
|
389 { |
|
390 PrintError("can not write output file"); |
|
391 res = SZE_FAIL; |
|
392 break; |
|
393 } |
|
394 if (MyCloseFile(outputHandle)) |
|
395 { |
|
396 PrintError("can not close output file"); |
|
397 res = SZE_FAIL; |
|
398 break; |
|
399 } |
|
400 } |
|
401 printf("\n"); |
|
402 } |
|
403 allocImp.Free(outBuffer); |
|
404 } |
|
405 else |
|
406 { |
|
407 PrintError("incorrect command"); |
|
408 res = SZE_FAIL; |
|
409 } |
|
410 } |
|
411 SzArDbExFree(&db, allocImp.Free); |
|
412 |
|
413 MyCloseFile(archiveStream.File); |
|
414 if (res == SZ_OK) |
|
415 { |
|
416 printf("\nEverything is Ok\n"); |
|
417 return 0; |
|
418 } |
|
419 if (res == (SZ_RESULT)SZE_NOTIMPL) |
|
420 PrintError("decoder doesn't support this archive"); |
|
421 else if (res == (SZ_RESULT)SZE_OUTOFMEMORY) |
|
422 PrintError("can not allocate memory"); |
|
423 else if (res == (SZ_RESULT)SZE_CRC_ERROR) |
|
424 PrintError("CRC error"); |
|
425 else |
|
426 printf("\nERROR #%d\n", res); |
|
427 return 1; |
|
428 } |