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