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