Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / Com / ComIn.cpp
1 // Archive/ComIn.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../../C/Alloc.h"\r
6 #include "../../../../C/CpuArch.h"\r
7 \r
8 #include "Common/IntToString.h"\r
9 #include "Common/MyCom.h"\r
10 \r
11 #include "../../Common/StreamUtils.h"\r
12 \r
13 #include "ComIn.h"\r
14 \r
15 #define Get16(p) GetUi16(p)\r
16 #define Get32(p) GetUi32(p)\r
17 \r
18 namespace NArchive{\r
19 namespace NCom{\r
20 \r
21 static const UInt32 kSignatureSize = 8;\r
22 static const Byte kSignature[kSignatureSize] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };\r
23 \r
24 void CUInt32Buf::Free()\r
25 {\r
26   MyFree(_buf);\r
27   _buf = 0;\r
28 }\r
29 \r
30 bool CUInt32Buf::Allocate(UInt32 numItems)\r
31 {\r
32   Free();\r
33   if (numItems == 0)\r
34     return true;\r
35   size_t newSize = (size_t)numItems * sizeof(UInt32);\r
36   if (newSize / sizeof(UInt32) != numItems)\r
37     return false;\r
38   _buf = (UInt32 *)MyAlloc(newSize);\r
39   return (_buf != 0);\r
40 }\r
41 \r
42 static HRESULT ReadSector(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid)\r
43 {\r
44   RINOK(inStream->Seek((((UInt64)sid + 1) << sectorSizeBits), STREAM_SEEK_SET, NULL));\r
45   return ReadStream_FALSE(inStream, buf, (UInt32)1 << sectorSizeBits);\r
46 }\r
47 \r
48 static HRESULT ReadIDs(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid, UInt32 *dest)\r
49 {\r
50   RINOK(ReadSector(inStream, buf, sectorSizeBits, sid));\r
51   UInt32 sectorSize = (UInt32)1 << sectorSizeBits;\r
52   for (UInt32 t = 0; t < sectorSize; t += 4)\r
53     *dest++ = Get32(buf + t);\r
54   return S_OK;\r
55 }\r
56 \r
57 static void GetFileTimeFromMem(const Byte *p, FILETIME *ft)\r
58 {\r
59   ft->dwLowDateTime = Get32(p);\r
60   ft->dwHighDateTime = Get32(p + 4);\r
61 }\r
62 \r
63 void CItem::Parse(const Byte *p, bool mode64bit)\r
64 {\r
65   memcpy(Name, p, kNameSizeMax);\r
66   // NameSize = Get16(p + 64);\r
67   Type = p[66];\r
68   LeftDid = Get32(p + 68);\r
69   RightDid = Get32(p + 72);\r
70   SonDid = Get32(p + 76);\r
71   // Flags = Get32(p + 96);\r
72   GetFileTimeFromMem(p + 100, &CTime);\r
73   GetFileTimeFromMem(p + 108, &MTime);\r
74   Sid = Get32(p + 116);\r
75   Size = Get32(p + 120);\r
76   if (mode64bit)\r
77     Size |= ((UInt64)Get32(p + 124) << 32);\r
78 }\r
79 \r
80 void CDatabase::Clear()\r
81 {\r
82   Fat.Free();\r
83   MiniSids.Free();\r
84   Mat.Free();\r
85   Items.Clear();\r
86   Refs.Clear();\r
87 }\r
88 \r
89 static const UInt32 kNoDid = 0xFFFFFFFF;\r
90 \r
91 HRESULT CDatabase::AddNode(int parent, UInt32 did)\r
92 {\r
93   if (did == kNoDid)\r
94     return S_OK;\r
95   if (did >= (UInt32)Items.Size())\r
96     return S_FALSE;\r
97   const CItem &item = Items[did];\r
98   if (item.IsEmpty())\r
99     return S_FALSE;\r
100   CRef ref;\r
101   ref.Parent = parent;\r
102   ref.Did = did;\r
103   int index = Refs.Add(ref);\r
104   if (Refs.Size() > Items.Size())\r
105     return S_FALSE;\r
106   RINOK(AddNode(parent, item.LeftDid));\r
107   RINOK(AddNode(parent, item.RightDid));\r
108   if (item.IsDir())\r
109   {\r
110     RINOK(AddNode(index, item.SonDid));\r
111   }\r
112   return S_OK;\r
113 }\r
114 \r
115 static const char kCharOpenBracket  = '[';\r
116 static const char kCharCloseBracket = ']';\r
117 \r
118 static UString CompoundNameToFileName(const UString &s)\r
119 {\r
120   UString res;\r
121   for (int i = 0; i < s.Length(); i++)\r
122   {\r
123     wchar_t c = s[i];\r
124     if (c < 0x20)\r
125     {\r
126       res += kCharOpenBracket;\r
127       wchar_t buf[32];\r
128       ConvertUInt32ToString(c, buf);\r
129       res += buf;\r
130       res += kCharCloseBracket;\r
131     }\r
132     else\r
133       res += c;\r
134   }\r
135   return res;\r
136 }\r
137 \r
138 static char g_MsiChars[] =\r
139 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._";\r
140 \r
141 static const wchar_t *kMsi_ID = L""; // L"{msi}";\r
142 \r
143 static const int kMsiNumBits = 6;\r
144 static const UInt32 kMsiNumChars = 1 << kMsiNumBits;\r
145 static const UInt32 kMsiCharMask = kMsiNumChars - 1;\r
146 static const UInt32 kMsiStartUnicodeChar = 0x3800;\r
147 static const UInt32 kMsiUnicodeRange = kMsiNumChars * (kMsiNumChars + 1);\r
148 \r
149 bool CompoundMsiNameToFileName(const UString &name, UString &resultName)\r
150 {\r
151   resultName.Empty();\r
152   for (int i = 0; i < name.Length(); i++)\r
153   {\r
154     wchar_t c =  name[i];\r
155     if (c < kMsiStartUnicodeChar || c > kMsiStartUnicodeChar + kMsiUnicodeRange)\r
156       return false;\r
157     if (i == 0)\r
158       resultName += kMsi_ID;\r
159     c -= kMsiStartUnicodeChar;\r
160     \r
161     UInt32 c0 = c & kMsiCharMask;\r
162     UInt32 c1 = c >> kMsiNumBits;\r
163 \r
164     if (c1 <= kMsiNumChars)\r
165     {\r
166       resultName += (wchar_t)g_MsiChars[c0];\r
167       if (c1 == kMsiNumChars)\r
168         break;\r
169       resultName += (wchar_t)g_MsiChars[c1];\r
170     }\r
171     else\r
172       resultName += L'!';\r
173   }\r
174   return true;\r
175 }\r
176 \r
177 static UString ConvertName(const Byte *p, bool &isMsi)\r
178 {\r
179   isMsi = false;\r
180   UString s;\r
181   for (int i = 0; i < kNameSizeMax; i += 2)\r
182   {\r
183     wchar_t c = (p[i] | (wchar_t)p[i + 1] << 8);\r
184     if (c == 0)\r
185       break;\r
186     s += c;\r
187   }\r
188   UString msiName;\r
189   if (CompoundMsiNameToFileName(s, msiName))\r
190   {\r
191     isMsi = true;\r
192     return msiName;\r
193   }\r
194   return CompoundNameToFileName(s);\r
195 }\r
196 \r
197 static UString ConvertName(const Byte *p)\r
198 {\r
199   bool isMsi;\r
200   return ConvertName(p, isMsi);\r
201 }\r
202 \r
203 UString CDatabase::GetItemPath(UInt32 index) const\r
204 {\r
205   UString s;\r
206   while (index != kNoDid)\r
207   {\r
208     const CRef &ref = Refs[index];\r
209     const CItem &item = Items[ref.Did];\r
210     if (!s.IsEmpty())\r
211       s = (UString)WCHAR_PATH_SEPARATOR + s;\r
212     s = ConvertName(item.Name) + s;\r
213     index = ref.Parent;\r
214   }\r
215   return s;\r
216 }\r
217 \r
218 HRESULT CDatabase::Open(IInStream *inStream)\r
219 {\r
220   MainSubfile = -1;\r
221   static const UInt32 kHeaderSize = 512;\r
222   Byte p[kHeaderSize];\r
223   RINOK(ReadStream_FALSE(inStream, p, kHeaderSize));\r
224   if (memcmp(p, kSignature, kSignatureSize) != 0)\r
225     return S_FALSE;\r
226   if (Get16(p + 0x1A) > 4) // majorVer\r
227     return S_FALSE;\r
228   if (Get16(p + 0x1C) != 0xFFFE)\r
229     return S_FALSE;\r
230   int sectorSizeBits = Get16(p + 0x1E);\r
231   bool mode64bit = (sectorSizeBits >= 12);\r
232   int miniSectorSizeBits = Get16(p + 0x20);\r
233   SectorSizeBits = sectorSizeBits;\r
234   MiniSectorSizeBits = miniSectorSizeBits;\r
235 \r
236   if (sectorSizeBits > 28 || miniSectorSizeBits > 28 ||\r
237       sectorSizeBits < 7 || miniSectorSizeBits < 2 || miniSectorSizeBits > sectorSizeBits)\r
238     return S_FALSE;\r
239   UInt32 numSectorsForFAT = Get32(p + 0x2C);\r
240   LongStreamMinSize = Get32(p + 0x38);\r
241   \r
242   UInt32 sectSize = (UInt32)1 << (int)sectorSizeBits;\r
243 \r
244   CByteBuffer sect;\r
245   sect.SetCapacity(sectSize);\r
246 \r
247   int ssb2 = (int)(sectorSizeBits - 2);\r
248   UInt32 numSidsInSec = (UInt32)1 << ssb2;\r
249   UInt32 numFatItems = numSectorsForFAT << ssb2;\r
250   if ((numFatItems >> ssb2) != numSectorsForFAT)\r
251     return S_FALSE;\r
252   FatSize = numFatItems;\r
253 \r
254   {\r
255     CUInt32Buf bat;\r
256     UInt32 numSectorsForBat = Get32(p + 0x48);\r
257     const UInt32 kNumHeaderBatItems = 109;\r
258     UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2);\r
259     if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat)\r
260       return S_FALSE;\r
261     if (!bat.Allocate(numBatItems))\r
262       return S_FALSE;\r
263     UInt32 i;\r
264     for (i = 0; i < kNumHeaderBatItems; i++)\r
265       bat[i] = Get32(p + 0x4c + i * 4);\r
266     UInt32 sid = Get32(p + 0x44);\r
267     for (UInt32 s = 0; s < numSectorsForBat; s++)\r
268     {\r
269       RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i));\r
270       i += numSidsInSec - 1;\r
271       sid = bat[i];\r
272     }\r
273     numBatItems = i;\r
274     \r
275     if (!Fat.Allocate(numFatItems))\r
276       return S_FALSE;\r
277     UInt32 j = 0;\r
278       \r
279     for (i = 0; i < numFatItems; j++, i += numSidsInSec)\r
280     {\r
281       if (j >= numBatItems)\r
282         return S_FALSE;\r
283       RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i));\r
284     }\r
285   }\r
286 \r
287   UInt32 numMatItems;\r
288   {\r
289     UInt32 numSectorsForMat = Get32(p + 0x40);\r
290     numMatItems = (UInt32)numSectorsForMat << ssb2;\r
291     if ((numMatItems >> ssb2) != numSectorsForMat)\r
292       return S_FALSE;\r
293     if (!Mat.Allocate(numMatItems))\r
294       return S_FALSE;\r
295     UInt32 i;\r
296     UInt32 sid = Get32(p + 0x3C);\r
297     for (i = 0; i < numMatItems; i += numSidsInSec)\r
298     {\r
299       RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i));\r
300       if (sid >= numFatItems)\r
301         return S_FALSE;\r
302       sid = Fat[sid];\r
303     }\r
304     if (sid != NFatID::kEndOfChain)\r
305       return S_FALSE;\r
306   }\r
307 \r
308   {\r
309     UInt32 sid = Get32(p + 0x30);\r
310     for (;;)\r
311     {\r
312       if (sid >= numFatItems)\r
313         return S_FALSE;\r
314       RINOK(ReadSector(inStream, sect, sectorSizeBits, sid));\r
315       for (UInt32 i = 0; i < sectSize; i += 128)\r
316       {\r
317         CItem item;\r
318         item.Parse(sect + i, mode64bit);\r
319         Items.Add(item);\r
320       }\r
321       sid = Fat[sid];\r
322       if (sid == NFatID::kEndOfChain)\r
323         break;\r
324     }\r
325   }\r
326 \r
327   CItem root = Items[0];\r
328 \r
329   {\r
330     UInt32 numSectorsInMiniStream;\r
331     {\r
332       UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits;\r
333       if (numSatSects64 > NFatID::kMaxValue)\r
334         return S_FALSE;\r
335       numSectorsInMiniStream = (UInt32)numSatSects64;\r
336     }\r
337     NumSectorsInMiniStream = numSectorsInMiniStream;\r
338     if (!MiniSids.Allocate(numSectorsInMiniStream))\r
339       return S_FALSE;\r
340     {\r
341       UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits;\r
342       if (matSize64 > NFatID::kMaxValue)\r
343         return S_FALSE;\r
344       MatSize = (UInt32)matSize64;\r
345       if (numMatItems < MatSize)\r
346         return S_FALSE;\r
347     }\r
348 \r
349     UInt32 sid = root.Sid;\r
350     for (UInt32 i = 0; ; i++)\r
351     {\r
352       if (sid == NFatID::kEndOfChain)\r
353       {\r
354         if (i != numSectorsInMiniStream)\r
355           return S_FALSE;\r
356         break;\r
357       }\r
358       if (i >= numSectorsInMiniStream)\r
359         return S_FALSE;\r
360       MiniSids[i] = sid;\r
361       if (sid >= numFatItems)\r
362         return S_FALSE;\r
363       sid = Fat[sid];\r
364     }\r
365   }\r
366 \r
367   RINOK(AddNode(-1, root.SonDid));\r
368   \r
369   unsigned numCabs = 0;\r
370   for (int i = 0; i < Refs.Size(); i++)\r
371   {\r
372     const CItem &item = Items[Refs[i].Did];\r
373     if (item.IsDir() || numCabs > 1)\r
374       continue;\r
375     bool isMsiName;\r
376     UString msiName = ConvertName(item.Name, isMsiName);\r
377     if (isMsiName && msiName.Right(4).CompareNoCase(L".cab") == 0)\r
378     {\r
379       numCabs++;\r
380       MainSubfile = i;\r
381     }\r
382   }\r
383   if (numCabs > 1)\r
384     MainSubfile = -1;\r
385 \r
386   return S_OK;\r
387 }\r
388 \r
389 }}\r