5 #include "../../../../C/Alloc.h"
\r
6 #include "../../../../C/CpuArch.h"
\r
8 #include "Common/IntToString.h"
\r
9 #include "Common/MyCom.h"
\r
11 #include "../../Common/StreamUtils.h"
\r
15 #define Get16(p) GetUi16(p)
\r
16 #define Get32(p) GetUi32(p)
\r
21 static const UInt32 kSignatureSize = 8;
\r
22 static const Byte kSignature[kSignatureSize] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
\r
24 void CUInt32Buf::Free()
\r
30 bool CUInt32Buf::Allocate(UInt32 numItems)
\r
35 size_t newSize = (size_t)numItems * sizeof(UInt32);
\r
36 if (newSize / sizeof(UInt32) != numItems)
\r
38 _buf = (UInt32 *)MyAlloc(newSize);
\r
42 static HRESULT ReadSector(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid)
\r
44 RINOK(inStream->Seek((((UInt64)sid + 1) << sectorSizeBits), STREAM_SEEK_SET, NULL));
\r
45 return ReadStream_FALSE(inStream, buf, (UInt32)1 << sectorSizeBits);
\r
48 static HRESULT ReadIDs(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid, UInt32 *dest)
\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
57 static void GetFileTimeFromMem(const Byte *p, FILETIME *ft)
\r
59 ft->dwLowDateTime = Get32(p);
\r
60 ft->dwHighDateTime = Get32(p + 4);
\r
63 void CItem::Parse(const Byte *p, bool mode64bit)
\r
65 memcpy(Name, p, kNameSizeMax);
\r
66 // NameSize = Get16(p + 64);
\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
77 Size |= ((UInt64)Get32(p + 124) << 32);
\r
80 void CDatabase::Clear()
\r
89 static const UInt32 kNoDid = 0xFFFFFFFF;
\r
91 HRESULT CDatabase::AddNode(int parent, UInt32 did)
\r
95 if (did >= (UInt32)Items.Size())
\r
97 const CItem &item = Items[did];
\r
101 ref.Parent = parent;
\r
103 int index = Refs.Add(ref);
\r
104 if (Refs.Size() > Items.Size())
\r
106 RINOK(AddNode(parent, item.LeftDid));
\r
107 RINOK(AddNode(parent, item.RightDid));
\r
110 RINOK(AddNode(index, item.SonDid));
\r
115 static const char kCharOpenBracket = '[';
\r
116 static const char kCharCloseBracket = ']';
\r
118 static UString CompoundNameToFileName(const UString &s)
\r
121 for (int i = 0; i < s.Length(); i++)
\r
126 res += kCharOpenBracket;
\r
128 ConvertUInt32ToString(c, buf);
\r
130 res += kCharCloseBracket;
\r
138 static char g_MsiChars[] =
\r
139 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._";
\r
141 static const wchar_t *kMsi_ID = L""; // L"{msi}";
\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
149 bool CompoundMsiNameToFileName(const UString &name, UString &resultName)
\r
151 resultName.Empty();
\r
152 for (int i = 0; i < name.Length(); i++)
\r
154 wchar_t c = name[i];
\r
155 if (c < kMsiStartUnicodeChar || c > kMsiStartUnicodeChar + kMsiUnicodeRange)
\r
158 resultName += kMsi_ID;
\r
159 c -= kMsiStartUnicodeChar;
\r
161 UInt32 c0 = c & kMsiCharMask;
\r
162 UInt32 c1 = c >> kMsiNumBits;
\r
164 if (c1 <= kMsiNumChars)
\r
166 resultName += (wchar_t)g_MsiChars[c0];
\r
167 if (c1 == kMsiNumChars)
\r
169 resultName += (wchar_t)g_MsiChars[c1];
\r
172 resultName += L'!';
\r
177 static UString ConvertName(const Byte *p, bool &isMsi)
\r
181 for (int i = 0; i < kNameSizeMax; i += 2)
\r
183 wchar_t c = (p[i] | (wchar_t)p[i + 1] << 8);
\r
189 if (CompoundMsiNameToFileName(s, msiName))
\r
194 return CompoundNameToFileName(s);
\r
197 static UString ConvertName(const Byte *p)
\r
200 return ConvertName(p, isMsi);
\r
203 UString CDatabase::GetItemPath(UInt32 index) const
\r
206 while (index != kNoDid)
\r
208 const CRef &ref = Refs[index];
\r
209 const CItem &item = Items[ref.Did];
\r
211 s = (UString)WCHAR_PATH_SEPARATOR + s;
\r
212 s = ConvertName(item.Name) + s;
\r
213 index = ref.Parent;
\r
218 HRESULT CDatabase::Open(IInStream *inStream)
\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
226 if (Get16(p + 0x1A) > 4) // majorVer
\r
228 if (Get16(p + 0x1C) != 0xFFFE)
\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
236 if (sectorSizeBits > 28 || miniSectorSizeBits > 28 ||
\r
237 sectorSizeBits < 7 || miniSectorSizeBits < 2 || miniSectorSizeBits > sectorSizeBits)
\r
239 UInt32 numSectorsForFAT = Get32(p + 0x2C);
\r
240 LongStreamMinSize = Get32(p + 0x38);
\r
242 UInt32 sectSize = (UInt32)1 << (int)sectorSizeBits;
\r
245 sect.SetCapacity(sectSize);
\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
252 FatSize = numFatItems;
\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
261 if (!bat.Allocate(numBatItems))
\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
269 RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i));
\r
270 i += numSidsInSec - 1;
\r
275 if (!Fat.Allocate(numFatItems))
\r
279 for (i = 0; i < numFatItems; j++, i += numSidsInSec)
\r
281 if (j >= numBatItems)
\r
283 RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i));
\r
287 UInt32 numMatItems;
\r
289 UInt32 numSectorsForMat = Get32(p + 0x40);
\r
290 numMatItems = (UInt32)numSectorsForMat << ssb2;
\r
291 if ((numMatItems >> ssb2) != numSectorsForMat)
\r
293 if (!Mat.Allocate(numMatItems))
\r
296 UInt32 sid = Get32(p + 0x3C);
\r
297 for (i = 0; i < numMatItems; i += numSidsInSec)
\r
299 RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i));
\r
300 if (sid >= numFatItems)
\r
304 if (sid != NFatID::kEndOfChain)
\r
309 UInt32 sid = Get32(p + 0x30);
\r
312 if (sid >= numFatItems)
\r
314 RINOK(ReadSector(inStream, sect, sectorSizeBits, sid));
\r
315 for (UInt32 i = 0; i < sectSize; i += 128)
\r
318 item.Parse(sect + i, mode64bit);
\r
322 if (sid == NFatID::kEndOfChain)
\r
327 CItem root = Items[0];
\r
330 UInt32 numSectorsInMiniStream;
\r
332 UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits;
\r
333 if (numSatSects64 > NFatID::kMaxValue)
\r
335 numSectorsInMiniStream = (UInt32)numSatSects64;
\r
337 NumSectorsInMiniStream = numSectorsInMiniStream;
\r
338 if (!MiniSids.Allocate(numSectorsInMiniStream))
\r
341 UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits;
\r
342 if (matSize64 > NFatID::kMaxValue)
\r
344 MatSize = (UInt32)matSize64;
\r
345 if (numMatItems < MatSize)
\r
349 UInt32 sid = root.Sid;
\r
350 for (UInt32 i = 0; ; i++)
\r
352 if (sid == NFatID::kEndOfChain)
\r
354 if (i != numSectorsInMiniStream)
\r
358 if (i >= numSectorsInMiniStream)
\r
361 if (sid >= numFatItems)
\r
367 RINOK(AddNode(-1, root.SonDid));
\r
369 unsigned numCabs = 0;
\r
370 for (int i = 0; i < Refs.Size(); i++)
\r
372 const CItem &item = Items[Refs[i].Did];
\r
373 if (item.IsDir() || numCabs > 1)
\r
376 UString msiName = ConvertName(item.Name, isMsiName);
\r
377 if (isMsiName && msiName.Right(4).CompareNoCase(L".cab") == 0)
\r