add CHD support.
[pcsx_rearmed.git] / deps / lzma-16.04 / C / Util / 7z / 7zMain.c
1 /* 7zMain.c - Test application for 7z Decoder\r
2 2016-05-16 : Igor Pavlov : Public domain */\r
3 \r
4 #include "Precomp.h"\r
5 \r
6 #include <stdio.h>\r
7 #include <string.h>\r
8 \r
9 #include "../../7z.h"\r
10 #include "../../7zAlloc.h"\r
11 #include "../../7zBuf.h"\r
12 #include "../../7zCrc.h"\r
13 #include "../../7zFile.h"\r
14 #include "../../7zVersion.h"\r
15 \r
16 #ifndef USE_WINDOWS_FILE\r
17 /* for mkdir */\r
18 #ifdef _WIN32\r
19 #include <direct.h>\r
20 #else\r
21 #include <sys/stat.h>\r
22 #include <errno.h>\r
23 #endif\r
24 #endif\r
25 \r
26 static ISzAlloc g_Alloc = { SzAlloc, SzFree };\r
27 \r
28 static int Buf_EnsureSize(CBuf *dest, size_t size)\r
29 {\r
30   if (dest->size >= size)\r
31     return 1;\r
32   Buf_Free(dest, &g_Alloc);\r
33   return Buf_Create(dest, size, &g_Alloc);\r
34 }\r
35 \r
36 #ifndef _WIN32\r
37 #define _USE_UTF8\r
38 #endif\r
39 \r
40 /* #define _USE_UTF8 */\r
41 \r
42 #ifdef _USE_UTF8\r
43 \r
44 #define _UTF8_START(n) (0x100 - (1 << (7 - (n))))\r
45 \r
46 #define _UTF8_RANGE(n) (((UInt32)1) << ((n) * 5 + 6))\r
47 \r
48 #define _UTF8_HEAD(n, val) ((Byte)(_UTF8_START(n) + (val >> (6 * (n)))))\r
49 #define _UTF8_CHAR(n, val) ((Byte)(0x80 + (((val) >> (6 * (n))) & 0x3F)))\r
50 \r
51 static size_t Utf16_To_Utf8_Calc(const UInt16 *src, const UInt16 *srcLim)\r
52 {\r
53   size_t size = 0;\r
54   for (;;)\r
55   {\r
56     UInt32 val;\r
57     if (src == srcLim)\r
58       return size;\r
59     \r
60     size++;\r
61     val = *src++;\r
62    \r
63     if (val < 0x80)\r
64       continue;\r
65 \r
66     if (val < _UTF8_RANGE(1))\r
67     {\r
68       size++;\r
69       continue;\r
70     }\r
71 \r
72     if (val >= 0xD800 && val < 0xDC00 && src != srcLim)\r
73     {\r
74       UInt32 c2 = *src;\r
75       if (c2 >= 0xDC00 && c2 < 0xE000)\r
76       {\r
77         src++;\r
78         size += 3;\r
79         continue;\r
80       }\r
81     }\r
82 \r
83     size += 2;\r
84   }\r
85 }\r
86 \r
87 static Byte *Utf16_To_Utf8(Byte *dest, const UInt16 *src, const UInt16 *srcLim)\r
88 {\r
89   for (;;)\r
90   {\r
91     UInt32 val;\r
92     if (src == srcLim)\r
93       return dest;\r
94     \r
95     val = *src++;\r
96     \r
97     if (val < 0x80)\r
98     {\r
99       *dest++ = (char)val;\r
100       continue;\r
101     }\r
102 \r
103     if (val < _UTF8_RANGE(1))\r
104     {\r
105       dest[0] = _UTF8_HEAD(1, val);\r
106       dest[1] = _UTF8_CHAR(0, val);\r
107       dest += 2;\r
108       continue;\r
109     }\r
110 \r
111     if (val >= 0xD800 && val < 0xDC00 && src != srcLim)\r
112     {\r
113       UInt32 c2 = *src;\r
114       if (c2 >= 0xDC00 && c2 < 0xE000)\r
115       {\r
116         src++;\r
117         val = (((val - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;\r
118         dest[0] = _UTF8_HEAD(3, val);\r
119         dest[1] = _UTF8_CHAR(2, val);\r
120         dest[2] = _UTF8_CHAR(1, val);\r
121         dest[3] = _UTF8_CHAR(0, val);\r
122         dest += 4;\r
123         continue;\r
124       }\r
125     }\r
126     \r
127     dest[0] = _UTF8_HEAD(2, val);\r
128     dest[1] = _UTF8_CHAR(1, val);\r
129     dest[2] = _UTF8_CHAR(0, val);\r
130     dest += 3;\r
131   }\r
132 }\r
133 \r
134 static SRes Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen)\r
135 {\r
136   size_t destLen = Utf16_To_Utf8_Calc(src, src + srcLen);\r
137   destLen += 1;\r
138   if (!Buf_EnsureSize(dest, destLen))\r
139     return SZ_ERROR_MEM;\r
140   *Utf16_To_Utf8(dest->data, src, src + srcLen) = 0;\r
141   return SZ_OK;\r
142 }\r
143 \r
144 #endif\r
145 \r
146 static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s\r
147     #ifndef _USE_UTF8\r
148     , UINT codePage\r
149     #endif\r
150     )\r
151 {\r
152   unsigned len = 0;\r
153   for (len = 0; s[len] != 0; len++);\r
154 \r
155   #ifndef _USE_UTF8\r
156   {\r
157     unsigned size = len * 3 + 100;\r
158     if (!Buf_EnsureSize(buf, size))\r
159       return SZ_ERROR_MEM;\r
160     {\r
161       buf->data[0] = 0;\r
162       if (len != 0)\r
163       {\r
164         char defaultChar = '_';\r
165         BOOL defUsed;\r
166         unsigned numChars = 0;\r
167         numChars = WideCharToMultiByte(codePage, 0, s, len, (char *)buf->data, size, &defaultChar, &defUsed);\r
168         if (numChars == 0 || numChars >= size)\r
169           return SZ_ERROR_FAIL;\r
170         buf->data[numChars] = 0;\r
171       }\r
172       return SZ_OK;\r
173     }\r
174   }\r
175   #else\r
176   return Utf16_To_Utf8Buf(buf, s, len);\r
177   #endif\r
178 }\r
179 \r
180 #ifdef _WIN32\r
181   #ifndef USE_WINDOWS_FILE\r
182     static UINT g_FileCodePage = CP_ACP;\r
183   #endif\r
184   #define MY_FILE_CODE_PAGE_PARAM ,g_FileCodePage\r
185 #else\r
186   #define MY_FILE_CODE_PAGE_PARAM\r
187 #endif\r
188 \r
189 static WRes MyCreateDir(const UInt16 *name)\r
190 {\r
191   #ifdef USE_WINDOWS_FILE\r
192   \r
193   return CreateDirectoryW(name, NULL) ? 0 : GetLastError();\r
194   \r
195   #else\r
196 \r
197   CBuf buf;\r
198   WRes res;\r
199   Buf_Init(&buf);\r
200   RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM));\r
201 \r
202   res =\r
203   #ifdef _WIN32\r
204   _mkdir((const char *)buf.data)\r
205   #else\r
206   mkdir((const char *)buf.data, 0777)\r
207   #endif\r
208   == 0 ? 0 : errno;\r
209   Buf_Free(&buf, &g_Alloc);\r
210   return res;\r
211   \r
212   #endif\r
213 }\r
214 \r
215 static WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name)\r
216 {\r
217   #ifdef USE_WINDOWS_FILE\r
218   return OutFile_OpenW(p, name);\r
219   #else\r
220   CBuf buf;\r
221   WRes res;\r
222   Buf_Init(&buf);\r
223   RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM));\r
224   res = OutFile_Open(p, (const char *)buf.data);\r
225   Buf_Free(&buf, &g_Alloc);\r
226   return res;\r
227   #endif\r
228 }\r
229 \r
230 static SRes PrintString(const UInt16 *s)\r
231 {\r
232   CBuf buf;\r
233   SRes res;\r
234   Buf_Init(&buf);\r
235   res = Utf16_To_Char(&buf, s\r
236       #ifndef _USE_UTF8\r
237       , CP_OEMCP\r
238       #endif\r
239       );\r
240   if (res == SZ_OK)\r
241     fputs((const char *)buf.data, stdout);\r
242   Buf_Free(&buf, &g_Alloc);\r
243   return res;\r
244 }\r
245 \r
246 static void UInt64ToStr(UInt64 value, char *s)\r
247 {\r
248   char temp[32];\r
249   int pos = 0;\r
250   do\r
251   {\r
252     temp[pos++] = (char)('0' + (unsigned)(value % 10));\r
253     value /= 10;\r
254   }\r
255   while (value != 0);\r
256   do\r
257     *s++ = temp[--pos];\r
258   while (pos);\r
259   *s = '\0';\r
260 }\r
261 \r
262 static char *UIntToStr(char *s, unsigned value, int numDigits)\r
263 {\r
264   char temp[16];\r
265   int pos = 0;\r
266   do\r
267     temp[pos++] = (char)('0' + (value % 10));\r
268   while (value /= 10);\r
269   for (numDigits -= pos; numDigits > 0; numDigits--)\r
270     *s++ = '0';\r
271   do\r
272     *s++ = temp[--pos];\r
273   while (pos);\r
274   *s = '\0';\r
275   return s;\r
276 }\r
277 \r
278 static void UIntToStr_2(char *s, unsigned value)\r
279 {\r
280   s[0] = (char)('0' + (value / 10));\r
281   s[1] = (char)('0' + (value % 10));\r
282 }\r
283 \r
284 #define PERIOD_4 (4 * 365 + 1)\r
285 #define PERIOD_100 (PERIOD_4 * 25 - 1)\r
286 #define PERIOD_400 (PERIOD_100 * 4 + 1)\r
287 \r
288 static void ConvertFileTimeToString(const CNtfsFileTime *nt, char *s)\r
289 {\r
290   unsigned year, mon, hour, min, sec;\r
291   Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
292   unsigned t;\r
293   UInt32 v;\r
294   UInt64 v64 = nt->Low | ((UInt64)nt->High << 32);\r
295   v64 /= 10000000;\r
296   sec = (unsigned)(v64 % 60); v64 /= 60;\r
297   min = (unsigned)(v64 % 60); v64 /= 60;\r
298   hour = (unsigned)(v64 % 24); v64 /= 24;\r
299 \r
300   v = (UInt32)v64;\r
301 \r
302   year = (unsigned)(1601 + v / PERIOD_400 * 400);\r
303   v %= PERIOD_400;\r
304 \r
305   t = v / PERIOD_100; if (t ==  4) t =  3; year += t * 100; v -= t * PERIOD_100;\r
306   t = v / PERIOD_4;   if (t == 25) t = 24; year += t * 4;   v -= t * PERIOD_4;\r
307   t = v / 365;        if (t ==  4) t =  3; year += t;       v -= t * 365;\r
308 \r
309   if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))\r
310     ms[1] = 29;\r
311   for (mon = 0;; mon++)\r
312   {\r
313     unsigned d = ms[mon];\r
314     if (v < d)\r
315       break;\r
316     v -= d;\r
317   }\r
318   s = UIntToStr(s, year, 4); *s++ = '-';\r
319   UIntToStr_2(s, mon + 1); s[2] = '-'; s += 3;\r
320   UIntToStr_2(s, (unsigned)v + 1); s[2] = ' '; s += 3;\r
321   UIntToStr_2(s, hour); s[2] = ':'; s += 3;\r
322   UIntToStr_2(s, min); s[2] = ':'; s += 3;\r
323   UIntToStr_2(s, sec); s[2] = 0;\r
324 }\r
325 \r
326 void PrintError(char *sz)\r
327 {\r
328   printf("\nERROR: %s\n", sz);\r
329 }\r
330 \r
331 static void GetAttribString(UInt32 wa, Bool isDir, char *s)\r
332 {\r
333   #ifdef USE_WINDOWS_FILE\r
334   s[0] = (char)(((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : '.');\r
335   s[1] = (char)(((wa & FILE_ATTRIBUTE_READONLY ) != 0) ? 'R': '.');\r
336   s[2] = (char)(((wa & FILE_ATTRIBUTE_HIDDEN   ) != 0) ? 'H': '.');\r
337   s[3] = (char)(((wa & FILE_ATTRIBUTE_SYSTEM   ) != 0) ? 'S': '.');\r
338   s[4] = (char)(((wa & FILE_ATTRIBUTE_ARCHIVE  ) != 0) ? 'A': '.');\r
339   s[5] = 0;\r
340   #else\r
341   s[0] = (char)(((wa & (1 << 4)) != 0 || isDir) ? 'D' : '.');\r
342   s[1] = 0;\r
343   #endif\r
344 }\r
345 \r
346 // #define NUM_PARENTS_MAX 128\r
347 \r
348 int MY_CDECL main(int numargs, char *args[])\r
349 {\r
350   CFileInStream archiveStream;\r
351   CLookToRead lookStream;\r
352   CSzArEx db;\r
353   SRes res;\r
354   ISzAlloc allocImp;\r
355   ISzAlloc allocTempImp;\r
356   UInt16 *temp = NULL;\r
357   size_t tempSize = 0;\r
358   // UInt32 parents[NUM_PARENTS_MAX];\r
359 \r
360   printf("\n7z ANSI-C Decoder " MY_VERSION_COPYRIGHT_DATE "\n\n");\r
361 \r
362   if (numargs == 1)\r
363   {\r
364     printf(\r
365       "Usage: 7zDec <command> <archive_name>\n\n"\r
366       "<Commands>\n"\r
367       "  e: Extract files from archive (without using directory names)\n"\r
368       "  l: List contents of archive\n"\r
369       "  t: Test integrity of archive\n"\r
370       "  x: eXtract files with full paths\n");\r
371     return 0;\r
372   }\r
373   \r
374   if (numargs < 3)\r
375   {\r
376     PrintError("incorrect command");\r
377     return 1;\r
378   }\r
379 \r
380   #if defined(_WIN32) && !defined(USE_WINDOWS_FILE) && !defined(UNDER_CE)\r
381   g_FileCodePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;\r
382   #endif\r
383 \r
384   allocImp.Alloc = SzAlloc;\r
385   allocImp.Free = SzFree;\r
386 \r
387   allocTempImp.Alloc = SzAllocTemp;\r
388   allocTempImp.Free = SzFreeTemp;\r
389 \r
390   #ifdef UNDER_CE\r
391   if (InFile_OpenW(&archiveStream.file, L"\test.7z"))\r
392   #else\r
393   if (InFile_Open(&archiveStream.file, args[2]))\r
394   #endif\r
395   {\r
396     PrintError("can not open input file");\r
397     return 1;\r
398   }\r
399 \r
400   FileInStream_CreateVTable(&archiveStream);\r
401   LookToRead_CreateVTable(&lookStream, False);\r
402   \r
403   lookStream.realStream = &archiveStream.s;\r
404   LookToRead_Init(&lookStream);\r
405 \r
406   CrcGenerateTable();\r
407 \r
408   SzArEx_Init(&db);\r
409   \r
410   res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);\r
411   \r
412   if (res == SZ_OK)\r
413   {\r
414     char *command = args[1];\r
415     int listCommand = 0, testCommand = 0, fullPaths = 0;\r
416     \r
417     if (strcmp(command, "l") == 0) listCommand = 1;\r
418     else if (strcmp(command, "t") == 0) testCommand = 1;\r
419     else if (strcmp(command, "e") == 0) { }\r
420     else if (strcmp(command, "x") == 0) { fullPaths = 1; }\r
421     else\r
422     {\r
423       PrintError("incorrect command");\r
424       res = SZ_ERROR_FAIL;\r
425     }\r
426 \r
427     if (res == SZ_OK)\r
428     {\r
429       UInt32 i;\r
430 \r
431       /*\r
432       if you need cache, use these 3 variables.\r
433       if you use external function, you can make these variable as static.\r
434       */\r
435       UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */\r
436       Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */\r
437       size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */\r
438 \r
439       for (i = 0; i < db.NumFiles; i++)\r
440       {\r
441         size_t offset = 0;\r
442         size_t outSizeProcessed = 0;\r
443         // const CSzFileItem *f = db.Files + i;\r
444         size_t len;\r
445         unsigned isDir = SzArEx_IsDir(&db, i);\r
446         if (listCommand == 0 && isDir && !fullPaths)\r
447           continue;\r
448         len = SzArEx_GetFileNameUtf16(&db, i, NULL);\r
449         // len = SzArEx_GetFullNameLen(&db, i);\r
450 \r
451         if (len > tempSize)\r
452         {\r
453           SzFree(NULL, temp);\r
454           tempSize = len;\r
455           temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0]));\r
456           if (!temp)\r
457           {\r
458             res = SZ_ERROR_MEM;\r
459             break;\r
460           }\r
461         }\r
462 \r
463         SzArEx_GetFileNameUtf16(&db, i, temp);\r
464         /*\r
465         if (SzArEx_GetFullNameUtf16_Back(&db, i, temp + len) != temp)\r
466         {\r
467           res = SZ_ERROR_FAIL;\r
468           break;\r
469         }\r
470         */\r
471 \r
472         if (listCommand)\r
473         {\r
474           char attr[8], s[32], t[32];\r
475           UInt64 fileSize;\r
476 \r
477           GetAttribString(SzBitWithVals_Check(&db.Attribs, i) ? db.Attribs.Vals[i] : 0, isDir, attr);\r
478 \r
479           fileSize = SzArEx_GetFileSize(&db, i);\r
480           UInt64ToStr(fileSize, s);\r
481           \r
482           if (SzBitWithVals_Check(&db.MTime, i))\r
483             ConvertFileTimeToString(&db.MTime.Vals[i], t);\r
484           else\r
485           {\r
486             size_t j;\r
487             for (j = 0; j < 19; j++)\r
488               t[j] = ' ';\r
489             t[j] = '\0';\r
490           }\r
491           \r
492           printf("%s %s %10s  ", t, attr, s);\r
493           res = PrintString(temp);\r
494           if (res != SZ_OK)\r
495             break;\r
496           if (isDir)\r
497             printf("/");\r
498           printf("\n");\r
499           continue;\r
500         }\r
501 \r
502         fputs(testCommand ?\r
503             "Testing    ":\r
504             "Extracting ",\r
505             stdout);\r
506         res = PrintString(temp);\r
507         if (res != SZ_OK)\r
508           break;\r
509         \r
510         if (isDir)\r
511           printf("/");\r
512         else\r
513         {\r
514           res = SzArEx_Extract(&db, &lookStream.s, i,\r
515               &blockIndex, &outBuffer, &outBufferSize,\r
516               &offset, &outSizeProcessed,\r
517               &allocImp, &allocTempImp);\r
518           if (res != SZ_OK)\r
519             break;\r
520         }\r
521         \r
522         if (!testCommand)\r
523         {\r
524           CSzFile outFile;\r
525           size_t processedSize;\r
526           size_t j;\r
527           UInt16 *name = (UInt16 *)temp;\r
528           const UInt16 *destPath = (const UInt16 *)name;\r
529  \r
530           for (j = 0; name[j] != 0; j++)\r
531             if (name[j] == '/')\r
532             {\r
533               if (fullPaths)\r
534               {\r
535                 name[j] = 0;\r
536                 MyCreateDir(name);\r
537                 name[j] = CHAR_PATH_SEPARATOR;\r
538               }\r
539               else\r
540                 destPath = name + j + 1;\r
541             }\r
542     \r
543           if (isDir)\r
544           {\r
545             MyCreateDir(destPath);\r
546             printf("\n");\r
547             continue;\r
548           }\r
549           else if (OutFile_OpenUtf16(&outFile, destPath))\r
550           {\r
551             PrintError("can not open output file");\r
552             res = SZ_ERROR_FAIL;\r
553             break;\r
554           }\r
555 \r
556           processedSize = outSizeProcessed;\r
557           \r
558           if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)\r
559           {\r
560             PrintError("can not write output file");\r
561             res = SZ_ERROR_FAIL;\r
562             break;\r
563           }\r
564           \r
565           if (File_Close(&outFile))\r
566           {\r
567             PrintError("can not close output file");\r
568             res = SZ_ERROR_FAIL;\r
569             break;\r
570           }\r
571           \r
572           #ifdef USE_WINDOWS_FILE\r
573           if (SzBitWithVals_Check(&db.Attribs, i))\r
574             SetFileAttributesW(destPath, db.Attribs.Vals[i]);\r
575           #endif\r
576         }\r
577         printf("\n");\r
578       }\r
579       IAlloc_Free(&allocImp, outBuffer);\r
580     }\r
581   }\r
582 \r
583   SzArEx_Free(&db, &allocImp);\r
584   SzFree(NULL, temp);\r
585 \r
586   File_Close(&archiveStream.file);\r
587   \r
588   if (res == SZ_OK)\r
589   {\r
590     printf("\nEverything is Ok\n");\r
591     return 0;\r
592   }\r
593   \r
594   if (res == SZ_ERROR_UNSUPPORTED)\r
595     PrintError("decoder doesn't support this archive");\r
596   else if (res == SZ_ERROR_MEM)\r
597     PrintError("can not allocate memory");\r
598   else if (res == SZ_ERROR_CRC)\r
599     PrintError("CRC error");\r
600   else\r
601     printf("\nERROR #%d\n", res);\r
602   \r
603   return 1;\r
604 }\r