xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/FatHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1*f6dc9357SAndroid Build Coastguard Worker // FatHandler.cpp
2*f6dc9357SAndroid Build Coastguard Worker 
3*f6dc9357SAndroid Build Coastguard Worker #include "StdAfx.h"
4*f6dc9357SAndroid Build Coastguard Worker 
5*f6dc9357SAndroid Build Coastguard Worker // #include <stdio.h>
6*f6dc9357SAndroid Build Coastguard Worker 
7*f6dc9357SAndroid Build Coastguard Worker #include "../../../C/CpuArch.h"
8*f6dc9357SAndroid Build Coastguard Worker 
9*f6dc9357SAndroid Build Coastguard Worker #include "../../Common/ComTry.h"
10*f6dc9357SAndroid Build Coastguard Worker #include "../../Common/IntToString.h"
11*f6dc9357SAndroid Build Coastguard Worker #include "../../Common/MyBuffer.h"
12*f6dc9357SAndroid Build Coastguard Worker #include "../../Common/MyCom.h"
13*f6dc9357SAndroid Build Coastguard Worker #include "../../Common/StringConvert.h"
14*f6dc9357SAndroid Build Coastguard Worker 
15*f6dc9357SAndroid Build Coastguard Worker #include "../../Windows/PropVariant.h"
16*f6dc9357SAndroid Build Coastguard Worker #include "../../Windows/TimeUtils.h"
17*f6dc9357SAndroid Build Coastguard Worker 
18*f6dc9357SAndroid Build Coastguard Worker #include "../Common/LimitedStreams.h"
19*f6dc9357SAndroid Build Coastguard Worker #include "../Common/ProgressUtils.h"
20*f6dc9357SAndroid Build Coastguard Worker #include "../Common/RegisterArc.h"
21*f6dc9357SAndroid Build Coastguard Worker #include "../Common/StreamUtils.h"
22*f6dc9357SAndroid Build Coastguard Worker 
23*f6dc9357SAndroid Build Coastguard Worker #include "../Compress/CopyCoder.h"
24*f6dc9357SAndroid Build Coastguard Worker 
25*f6dc9357SAndroid Build Coastguard Worker #include "Common/DummyOutStream.h"
26*f6dc9357SAndroid Build Coastguard Worker 
27*f6dc9357SAndroid Build Coastguard Worker #define Get16(p) GetUi16(p)
28*f6dc9357SAndroid Build Coastguard Worker #define Get32(p) GetUi32(p)
29*f6dc9357SAndroid Build Coastguard Worker #define Get16a(p) GetUi16a(p)
30*f6dc9357SAndroid Build Coastguard Worker #define Get32a(p) GetUi32a(p)
31*f6dc9357SAndroid Build Coastguard Worker 
32*f6dc9357SAndroid Build Coastguard Worker #define PRF(x) /* x */
33*f6dc9357SAndroid Build Coastguard Worker 
34*f6dc9357SAndroid Build Coastguard Worker namespace NArchive {
35*f6dc9357SAndroid Build Coastguard Worker namespace NFat {
36*f6dc9357SAndroid Build Coastguard Worker 
37*f6dc9357SAndroid Build Coastguard Worker static const UInt32 kFatItemUsedByDirMask = (UInt32)1 << 31;
38*f6dc9357SAndroid Build Coastguard Worker 
39*f6dc9357SAndroid Build Coastguard Worker struct CHeader
40*f6dc9357SAndroid Build Coastguard Worker {
41*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumSectors;
42*f6dc9357SAndroid Build Coastguard Worker   UInt16 NumReservedSectors;
43*f6dc9357SAndroid Build Coastguard Worker   Byte NumFats;
44*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumFatSectors;
45*f6dc9357SAndroid Build Coastguard Worker   UInt32 RootDirSector;
46*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumRootDirSectors;
47*f6dc9357SAndroid Build Coastguard Worker   UInt32 DataSector;
48*f6dc9357SAndroid Build Coastguard Worker 
49*f6dc9357SAndroid Build Coastguard Worker   UInt32 FatSize;
50*f6dc9357SAndroid Build Coastguard Worker   UInt32 BadCluster;
51*f6dc9357SAndroid Build Coastguard Worker 
52*f6dc9357SAndroid Build Coastguard Worker   Byte NumFatBits;
53*f6dc9357SAndroid Build Coastguard Worker   Byte SectorSizeLog;
54*f6dc9357SAndroid Build Coastguard Worker   Byte SectorsPerClusterLog;
55*f6dc9357SAndroid Build Coastguard Worker   Byte ClusterSizeLog;
56*f6dc9357SAndroid Build Coastguard Worker 
57*f6dc9357SAndroid Build Coastguard Worker   UInt16 SectorsPerTrack;
58*f6dc9357SAndroid Build Coastguard Worker   UInt16 NumHeads;
59*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumHiddenSectors;
60*f6dc9357SAndroid Build Coastguard Worker 
61*f6dc9357SAndroid Build Coastguard Worker   bool VolFieldsDefined;
62*f6dc9357SAndroid Build Coastguard Worker   bool HeadersWarning;
63*f6dc9357SAndroid Build Coastguard Worker 
64*f6dc9357SAndroid Build Coastguard Worker   UInt32 VolId;
65*f6dc9357SAndroid Build Coastguard Worker   // Byte VolName[11];
66*f6dc9357SAndroid Build Coastguard Worker   // Byte FileSys[8];
67*f6dc9357SAndroid Build Coastguard Worker 
68*f6dc9357SAndroid Build Coastguard Worker   // Byte OemName[5];
69*f6dc9357SAndroid Build Coastguard Worker   Byte MediaType;
70*f6dc9357SAndroid Build Coastguard Worker 
71*f6dc9357SAndroid Build Coastguard Worker   // 32-bit FAT
72*f6dc9357SAndroid Build Coastguard Worker   UInt16 Flags;
73*f6dc9357SAndroid Build Coastguard Worker   UInt16 FsInfoSector;
74*f6dc9357SAndroid Build Coastguard Worker   UInt32 RootCluster;
75*f6dc9357SAndroid Build Coastguard Worker 
IsFat32NArchive::NFat::CHeader76*f6dc9357SAndroid Build Coastguard Worker   bool IsFat32() const { return NumFatBits == 32; }
GetPhySizeNArchive::NFat::CHeader77*f6dc9357SAndroid Build Coastguard Worker   UInt64 GetPhySize() const { return (UInt64)NumSectors << SectorSizeLog; }
SectorSizeNArchive::NFat::CHeader78*f6dc9357SAndroid Build Coastguard Worker   UInt32 SectorSize() const { return (UInt32)1 << SectorSizeLog; }
ClusterSizeNArchive::NFat::CHeader79*f6dc9357SAndroid Build Coastguard Worker   UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; }
ClusterToSectorNArchive::NFat::CHeader80*f6dc9357SAndroid Build Coastguard Worker   UInt32 ClusterToSector(UInt32 c) const { return DataSector + ((c - 2) << SectorsPerClusterLog); }
IsEocNArchive::NFat::CHeader81*f6dc9357SAndroid Build Coastguard Worker   UInt32 IsEoc(UInt32 c) const { return c > BadCluster; }
IsEocAndUnusedNArchive::NFat::CHeader82*f6dc9357SAndroid Build Coastguard Worker   UInt32 IsEocAndUnused(UInt32 c) const { return c > BadCluster && (c & kFatItemUsedByDirMask) == 0; }
IsValidClusterNArchive::NFat::CHeader83*f6dc9357SAndroid Build Coastguard Worker   UInt32 IsValidCluster(UInt32 c) const { return c >= 2 && c < FatSize; }
SizeToSectorsNArchive::NFat::CHeader84*f6dc9357SAndroid Build Coastguard Worker   UInt32 SizeToSectors(UInt32 size) const { return (size + SectorSize() - 1) >> SectorSizeLog; }
CalcFatSizeInSectorsNArchive::NFat::CHeader85*f6dc9357SAndroid Build Coastguard Worker   UInt32 CalcFatSizeInSectors() const { return SizeToSectors((FatSize * (NumFatBits / 4) + 1) / 2); }
86*f6dc9357SAndroid Build Coastguard Worker 
GetFatSectorNArchive::NFat::CHeader87*f6dc9357SAndroid Build Coastguard Worker   UInt32 GetFatSector() const
88*f6dc9357SAndroid Build Coastguard Worker   {
89*f6dc9357SAndroid Build Coastguard Worker     UInt32 index = (IsFat32() && (Flags & 0x80) != 0) ? (Flags & 0xF) : 0;
90*f6dc9357SAndroid Build Coastguard Worker     if (index > NumFats)
91*f6dc9357SAndroid Build Coastguard Worker       index = 0;
92*f6dc9357SAndroid Build Coastguard Worker     return NumReservedSectors + index * NumFatSectors;
93*f6dc9357SAndroid Build Coastguard Worker   }
94*f6dc9357SAndroid Build Coastguard Worker 
GetFilePackSizeNArchive::NFat::CHeader95*f6dc9357SAndroid Build Coastguard Worker   UInt64 GetFilePackSize(UInt32 unpackSize) const
96*f6dc9357SAndroid Build Coastguard Worker   {
97*f6dc9357SAndroid Build Coastguard Worker     UInt64 mask = ClusterSize() - 1;
98*f6dc9357SAndroid Build Coastguard Worker     return (unpackSize + mask) & ~mask;
99*f6dc9357SAndroid Build Coastguard Worker   }
100*f6dc9357SAndroid Build Coastguard Worker 
GetNumClustersNArchive::NFat::CHeader101*f6dc9357SAndroid Build Coastguard Worker   UInt32 GetNumClusters(UInt32 size) const
102*f6dc9357SAndroid Build Coastguard Worker     { return (UInt32)(((UInt64)size + ClusterSize() - 1) >> ClusterSizeLog); }
103*f6dc9357SAndroid Build Coastguard Worker 
104*f6dc9357SAndroid Build Coastguard Worker   bool Parse(const Byte *p);
105*f6dc9357SAndroid Build Coastguard Worker };
106*f6dc9357SAndroid Build Coastguard Worker 
GetLog(UInt32 num)107*f6dc9357SAndroid Build Coastguard Worker static int GetLog(UInt32 num)
108*f6dc9357SAndroid Build Coastguard Worker {
109*f6dc9357SAndroid Build Coastguard Worker   for (int i = 0; i < 31; i++)
110*f6dc9357SAndroid Build Coastguard Worker     if (((UInt32)1 << i) == num)
111*f6dc9357SAndroid Build Coastguard Worker       return i;
112*f6dc9357SAndroid Build Coastguard Worker   return -1;
113*f6dc9357SAndroid Build Coastguard Worker }
114*f6dc9357SAndroid Build Coastguard Worker 
115*f6dc9357SAndroid Build Coastguard Worker static const UInt32 kHeaderSize = 512;
116*f6dc9357SAndroid Build Coastguard Worker 
117*f6dc9357SAndroid Build Coastguard Worker API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size);
IsArc_Fat(const Byte * p,size_t size)118*f6dc9357SAndroid Build Coastguard Worker API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size)
119*f6dc9357SAndroid Build Coastguard Worker {
120*f6dc9357SAndroid Build Coastguard Worker   if (size < kHeaderSize)
121*f6dc9357SAndroid Build Coastguard Worker     return k_IsArc_Res_NEED_MORE;
122*f6dc9357SAndroid Build Coastguard Worker   CHeader h;
123*f6dc9357SAndroid Build Coastguard Worker   return h.Parse(p) ? k_IsArc_Res_YES : k_IsArc_Res_NO;
124*f6dc9357SAndroid Build Coastguard Worker }
125*f6dc9357SAndroid Build Coastguard Worker 
Parse(const Byte * p)126*f6dc9357SAndroid Build Coastguard Worker bool CHeader::Parse(const Byte *p)
127*f6dc9357SAndroid Build Coastguard Worker {
128*f6dc9357SAndroid Build Coastguard Worker   if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA)
129*f6dc9357SAndroid Build Coastguard Worker     return false;
130*f6dc9357SAndroid Build Coastguard Worker 
131*f6dc9357SAndroid Build Coastguard Worker   HeadersWarning = false;
132*f6dc9357SAndroid Build Coastguard Worker 
133*f6dc9357SAndroid Build Coastguard Worker   int codeOffset = 0;
134*f6dc9357SAndroid Build Coastguard Worker   switch (p[0])
135*f6dc9357SAndroid Build Coastguard Worker   {
136*f6dc9357SAndroid Build Coastguard Worker     case 0xE9: codeOffset = 3 + (Int16)Get16(p + 1); break;
137*f6dc9357SAndroid Build Coastguard Worker     case 0xEB: if (p[2] != 0x90) return false; codeOffset = 2 + (signed char)p[1]; break;
138*f6dc9357SAndroid Build Coastguard Worker     default: return false;
139*f6dc9357SAndroid Build Coastguard Worker   }
140*f6dc9357SAndroid Build Coastguard Worker   {
141*f6dc9357SAndroid Build Coastguard Worker     {
142*f6dc9357SAndroid Build Coastguard Worker       const UInt32 val32 = Get16(p + 11);
143*f6dc9357SAndroid Build Coastguard Worker       const int s = GetLog(val32);
144*f6dc9357SAndroid Build Coastguard Worker       if (s < 9 || s > 12)
145*f6dc9357SAndroid Build Coastguard Worker         return false;
146*f6dc9357SAndroid Build Coastguard Worker       SectorSizeLog = (Byte)s;
147*f6dc9357SAndroid Build Coastguard Worker     }
148*f6dc9357SAndroid Build Coastguard Worker     {
149*f6dc9357SAndroid Build Coastguard Worker       const UInt32 val32 = p[13];
150*f6dc9357SAndroid Build Coastguard Worker       const int s = GetLog(val32);
151*f6dc9357SAndroid Build Coastguard Worker       if (s < 0)
152*f6dc9357SAndroid Build Coastguard Worker         return false;
153*f6dc9357SAndroid Build Coastguard Worker       SectorsPerClusterLog = (Byte)s;
154*f6dc9357SAndroid Build Coastguard Worker     }
155*f6dc9357SAndroid Build Coastguard Worker     ClusterSizeLog = (Byte)(SectorSizeLog + SectorsPerClusterLog);
156*f6dc9357SAndroid Build Coastguard Worker     if (ClusterSizeLog > 24)
157*f6dc9357SAndroid Build Coastguard Worker       return false;
158*f6dc9357SAndroid Build Coastguard Worker   }
159*f6dc9357SAndroid Build Coastguard Worker 
160*f6dc9357SAndroid Build Coastguard Worker   NumReservedSectors = Get16(p + 14);
161*f6dc9357SAndroid Build Coastguard Worker   if (NumReservedSectors == 0)
162*f6dc9357SAndroid Build Coastguard Worker     return false;
163*f6dc9357SAndroid Build Coastguard Worker 
164*f6dc9357SAndroid Build Coastguard Worker   NumFats = p[16];
165*f6dc9357SAndroid Build Coastguard Worker   if (NumFats < 1 || NumFats > 4)
166*f6dc9357SAndroid Build Coastguard Worker     return false;
167*f6dc9357SAndroid Build Coastguard Worker 
168*f6dc9357SAndroid Build Coastguard Worker   // we also support images that contain 0 in offset field.
169*f6dc9357SAndroid Build Coastguard Worker   const bool isOkOffset = (codeOffset == 0)
170*f6dc9357SAndroid Build Coastguard Worker       || (codeOffset == (p[0] == 0xEB ? 2 : 3));
171*f6dc9357SAndroid Build Coastguard Worker 
172*f6dc9357SAndroid Build Coastguard Worker   const UInt16 numRootDirEntries = Get16(p + 17);
173*f6dc9357SAndroid Build Coastguard Worker   if (numRootDirEntries == 0)
174*f6dc9357SAndroid Build Coastguard Worker   {
175*f6dc9357SAndroid Build Coastguard Worker     if (codeOffset < 90 && !isOkOffset)
176*f6dc9357SAndroid Build Coastguard Worker       return false;
177*f6dc9357SAndroid Build Coastguard Worker     NumFatBits = 32;
178*f6dc9357SAndroid Build Coastguard Worker     NumRootDirSectors = 0;
179*f6dc9357SAndroid Build Coastguard Worker   }
180*f6dc9357SAndroid Build Coastguard Worker   else
181*f6dc9357SAndroid Build Coastguard Worker   {
182*f6dc9357SAndroid Build Coastguard Worker     // Some FAT12s don't contain VolFields
183*f6dc9357SAndroid Build Coastguard Worker     if (codeOffset < 62 - 24 && !isOkOffset)
184*f6dc9357SAndroid Build Coastguard Worker       return false;
185*f6dc9357SAndroid Build Coastguard Worker     NumFatBits = 0;
186*f6dc9357SAndroid Build Coastguard Worker     const UInt32 mask = (1 << (SectorSizeLog - 5)) - 1;
187*f6dc9357SAndroid Build Coastguard Worker     if ((numRootDirEntries & mask) != 0)
188*f6dc9357SAndroid Build Coastguard Worker       return false;
189*f6dc9357SAndroid Build Coastguard Worker     NumRootDirSectors = (numRootDirEntries + mask) >> (SectorSizeLog - 5);
190*f6dc9357SAndroid Build Coastguard Worker   }
191*f6dc9357SAndroid Build Coastguard Worker 
192*f6dc9357SAndroid Build Coastguard Worker   NumSectors = Get16(p + 19);
193*f6dc9357SAndroid Build Coastguard Worker   if (NumSectors == 0)
194*f6dc9357SAndroid Build Coastguard Worker     NumSectors = Get32(p + 32);
195*f6dc9357SAndroid Build Coastguard Worker   /*
196*f6dc9357SAndroid Build Coastguard Worker   // mkfs.fat could create fat32 image with 16-bit number of sectors.
197*f6dc9357SAndroid Build Coastguard Worker   // v23: we allow 16-bit value for number of sectors in fat32.
198*f6dc9357SAndroid Build Coastguard Worker   else if (IsFat32())
199*f6dc9357SAndroid Build Coastguard Worker     return false;
200*f6dc9357SAndroid Build Coastguard Worker   */
201*f6dc9357SAndroid Build Coastguard Worker 
202*f6dc9357SAndroid Build Coastguard Worker   MediaType = p[21];
203*f6dc9357SAndroid Build Coastguard Worker   NumFatSectors = Get16(p + 22);
204*f6dc9357SAndroid Build Coastguard Worker   SectorsPerTrack = Get16(p + 24);
205*f6dc9357SAndroid Build Coastguard Worker   NumHeads = Get16(p + 26);
206*f6dc9357SAndroid Build Coastguard Worker   NumHiddenSectors = Get32(p + 28);
207*f6dc9357SAndroid Build Coastguard Worker 
208*f6dc9357SAndroid Build Coastguard Worker   // memcpy(OemName, p + 3, 5);
209*f6dc9357SAndroid Build Coastguard Worker 
210*f6dc9357SAndroid Build Coastguard Worker   int curOffset = 36;
211*f6dc9357SAndroid Build Coastguard Worker   p += 36;
212*f6dc9357SAndroid Build Coastguard Worker   if (IsFat32())
213*f6dc9357SAndroid Build Coastguard Worker   {
214*f6dc9357SAndroid Build Coastguard Worker     if (NumFatSectors != 0)
215*f6dc9357SAndroid Build Coastguard Worker       return false;
216*f6dc9357SAndroid Build Coastguard Worker     NumFatSectors = Get32(p);
217*f6dc9357SAndroid Build Coastguard Worker     if (NumFatSectors >= (1 << 24))
218*f6dc9357SAndroid Build Coastguard Worker       return false;
219*f6dc9357SAndroid Build Coastguard Worker 
220*f6dc9357SAndroid Build Coastguard Worker     Flags = Get16(p + 4);
221*f6dc9357SAndroid Build Coastguard Worker     if (Get16(p + 6) != 0)
222*f6dc9357SAndroid Build Coastguard Worker       return false;
223*f6dc9357SAndroid Build Coastguard Worker     RootCluster = Get32(p + 8);
224*f6dc9357SAndroid Build Coastguard Worker     FsInfoSector = Get16(p + 12);
225*f6dc9357SAndroid Build Coastguard Worker     for (int i = 16; i < 28; i++)
226*f6dc9357SAndroid Build Coastguard Worker       if (p[i] != 0)
227*f6dc9357SAndroid Build Coastguard Worker         return false;
228*f6dc9357SAndroid Build Coastguard Worker     p += 28;
229*f6dc9357SAndroid Build Coastguard Worker     curOffset += 28;
230*f6dc9357SAndroid Build Coastguard Worker   }
231*f6dc9357SAndroid Build Coastguard Worker 
232*f6dc9357SAndroid Build Coastguard Worker   // DriveNumber = p[0];
233*f6dc9357SAndroid Build Coastguard Worker   VolFieldsDefined = false;
234*f6dc9357SAndroid Build Coastguard Worker   if (codeOffset >= curOffset + 3)
235*f6dc9357SAndroid Build Coastguard Worker   {
236*f6dc9357SAndroid Build Coastguard Worker     VolFieldsDefined = (p[2] == 0x29); // ExtendedBootSig
237*f6dc9357SAndroid Build Coastguard Worker     if (VolFieldsDefined)
238*f6dc9357SAndroid Build Coastguard Worker     {
239*f6dc9357SAndroid Build Coastguard Worker       if (codeOffset < curOffset + 26)
240*f6dc9357SAndroid Build Coastguard Worker         return false;
241*f6dc9357SAndroid Build Coastguard Worker       VolId = Get32(p + 3);
242*f6dc9357SAndroid Build Coastguard Worker       // memcpy(VolName, p + 7, 11);
243*f6dc9357SAndroid Build Coastguard Worker       // memcpy(FileSys, p + 18, 8);
244*f6dc9357SAndroid Build Coastguard Worker     }
245*f6dc9357SAndroid Build Coastguard Worker   }
246*f6dc9357SAndroid Build Coastguard Worker 
247*f6dc9357SAndroid Build Coastguard Worker   if (NumFatSectors == 0)
248*f6dc9357SAndroid Build Coastguard Worker     return false;
249*f6dc9357SAndroid Build Coastguard Worker   RootDirSector = NumReservedSectors + NumFatSectors * NumFats;
250*f6dc9357SAndroid Build Coastguard Worker   DataSector = RootDirSector + NumRootDirSectors;
251*f6dc9357SAndroid Build Coastguard Worker   if (NumSectors < DataSector)
252*f6dc9357SAndroid Build Coastguard Worker     return false;
253*f6dc9357SAndroid Build Coastguard Worker   const UInt32 numDataSectors = NumSectors - DataSector;
254*f6dc9357SAndroid Build Coastguard Worker   const UInt32 numClusters = numDataSectors >> SectorsPerClusterLog;
255*f6dc9357SAndroid Build Coastguard Worker 
256*f6dc9357SAndroid Build Coastguard Worker   BadCluster = 0x0FFFFFF7;
257*f6dc9357SAndroid Build Coastguard Worker   // v23: we support unusual (< 0xFFF5) numClusters values in fat32 systems
258*f6dc9357SAndroid Build Coastguard Worker   if (NumFatBits != 32)
259*f6dc9357SAndroid Build Coastguard Worker   {
260*f6dc9357SAndroid Build Coastguard Worker     if (numClusters >= 0xFFF5)
261*f6dc9357SAndroid Build Coastguard Worker       return false;
262*f6dc9357SAndroid Build Coastguard Worker     NumFatBits = (Byte)(numClusters < 0xFF5 ? 12 : 16);
263*f6dc9357SAndroid Build Coastguard Worker     BadCluster &= ((1 << NumFatBits) - 1);
264*f6dc9357SAndroid Build Coastguard Worker   }
265*f6dc9357SAndroid Build Coastguard Worker 
266*f6dc9357SAndroid Build Coastguard Worker   FatSize = numClusters + 2;
267*f6dc9357SAndroid Build Coastguard Worker   if (FatSize > BadCluster)
268*f6dc9357SAndroid Build Coastguard Worker     return false;
269*f6dc9357SAndroid Build Coastguard Worker   if (CalcFatSizeInSectors() > NumFatSectors)
270*f6dc9357SAndroid Build Coastguard Worker   {
271*f6dc9357SAndroid Build Coastguard Worker     /* some third-party program can create such FAT image, where
272*f6dc9357SAndroid Build Coastguard Worker        size of FAT table (NumFatSectors from headers) is smaller than
273*f6dc9357SAndroid Build Coastguard Worker        required value that is calculated from calculated (FatSize) value.
274*f6dc9357SAndroid Build Coastguard Worker        Another FAT unpackers probably ignore that error.
275*f6dc9357SAndroid Build Coastguard Worker        v23.02: we also ignore that error, and
276*f6dc9357SAndroid Build Coastguard Worker        we recalculate (FatSize) value from (NumFatSectors).
277*f6dc9357SAndroid Build Coastguard Worker        New (FatSize) will be smaller than original "full" (FatSize) value.
278*f6dc9357SAndroid Build Coastguard Worker        So we will have some unused clusters at the end of archive.
279*f6dc9357SAndroid Build Coastguard Worker     */
280*f6dc9357SAndroid Build Coastguard Worker     FatSize = (UInt32)(((UInt64)NumFatSectors << (3 + SectorSizeLog)) / NumFatBits);
281*f6dc9357SAndroid Build Coastguard Worker     HeadersWarning = true;
282*f6dc9357SAndroid Build Coastguard Worker   }
283*f6dc9357SAndroid Build Coastguard Worker   return true;
284*f6dc9357SAndroid Build Coastguard Worker }
285*f6dc9357SAndroid Build Coastguard Worker 
286*f6dc9357SAndroid Build Coastguard Worker struct CItem
287*f6dc9357SAndroid Build Coastguard Worker {
288*f6dc9357SAndroid Build Coastguard Worker   UString UName;
289*f6dc9357SAndroid Build Coastguard Worker   char DosName[11];
290*f6dc9357SAndroid Build Coastguard Worker   Byte CTime2;
291*f6dc9357SAndroid Build Coastguard Worker   UInt32 CTime;
292*f6dc9357SAndroid Build Coastguard Worker   UInt32 MTime;
293*f6dc9357SAndroid Build Coastguard Worker   UInt16 ADate;
294*f6dc9357SAndroid Build Coastguard Worker   Byte Attrib;
295*f6dc9357SAndroid Build Coastguard Worker   Byte Flags;
296*f6dc9357SAndroid Build Coastguard Worker   UInt32 Size;
297*f6dc9357SAndroid Build Coastguard Worker   UInt32 Cluster;
298*f6dc9357SAndroid Build Coastguard Worker   Int32 Parent;
299*f6dc9357SAndroid Build Coastguard Worker 
300*f6dc9357SAndroid Build Coastguard Worker   // NT uses Flags to store Low Case status
NameIsLowNArchive::NFat::CItem301*f6dc9357SAndroid Build Coastguard Worker   bool NameIsLow() const { return (Flags & 0x8) != 0; }
ExtIsLowNArchive::NFat::CItem302*f6dc9357SAndroid Build Coastguard Worker   bool ExtIsLow() const { return (Flags & 0x10) != 0; }
IsDirNArchive::NFat::CItem303*f6dc9357SAndroid Build Coastguard Worker   bool IsDir() const { return (Attrib & 0x10) != 0; }
304*f6dc9357SAndroid Build Coastguard Worker   UString GetShortName() const;
305*f6dc9357SAndroid Build Coastguard Worker   UString GetName() const;
306*f6dc9357SAndroid Build Coastguard Worker   UString GetVolName() const;
307*f6dc9357SAndroid Build Coastguard Worker };
308*f6dc9357SAndroid Build Coastguard Worker 
CopyAndTrim(char * dest,const char * src,unsigned size,bool toLower)309*f6dc9357SAndroid Build Coastguard Worker static unsigned CopyAndTrim(char *dest, const char *src, unsigned size, bool toLower)
310*f6dc9357SAndroid Build Coastguard Worker {
311*f6dc9357SAndroid Build Coastguard Worker   memcpy(dest, src, size);
312*f6dc9357SAndroid Build Coastguard Worker   if (toLower)
313*f6dc9357SAndroid Build Coastguard Worker   {
314*f6dc9357SAndroid Build Coastguard Worker     for (unsigned i = 0; i < size; i++)
315*f6dc9357SAndroid Build Coastguard Worker     {
316*f6dc9357SAndroid Build Coastguard Worker       char c = dest[i];
317*f6dc9357SAndroid Build Coastguard Worker       if (c >= 'A' && c <= 'Z')
318*f6dc9357SAndroid Build Coastguard Worker         dest[i] = (char)(c + 0x20);
319*f6dc9357SAndroid Build Coastguard Worker     }
320*f6dc9357SAndroid Build Coastguard Worker   }
321*f6dc9357SAndroid Build Coastguard Worker 
322*f6dc9357SAndroid Build Coastguard Worker   for (unsigned i = size;;)
323*f6dc9357SAndroid Build Coastguard Worker   {
324*f6dc9357SAndroid Build Coastguard Worker     if (i == 0)
325*f6dc9357SAndroid Build Coastguard Worker       return 0;
326*f6dc9357SAndroid Build Coastguard Worker     if (dest[i - 1] != ' ')
327*f6dc9357SAndroid Build Coastguard Worker       return i;
328*f6dc9357SAndroid Build Coastguard Worker     i--;
329*f6dc9357SAndroid Build Coastguard Worker   }
330*f6dc9357SAndroid Build Coastguard Worker }
331*f6dc9357SAndroid Build Coastguard Worker 
FatStringToUnicode(const char * s)332*f6dc9357SAndroid Build Coastguard Worker static UString FatStringToUnicode(const char *s)
333*f6dc9357SAndroid Build Coastguard Worker {
334*f6dc9357SAndroid Build Coastguard Worker   return MultiByteToUnicodeString(s, CP_OEMCP);
335*f6dc9357SAndroid Build Coastguard Worker }
336*f6dc9357SAndroid Build Coastguard Worker 
GetShortName() const337*f6dc9357SAndroid Build Coastguard Worker UString CItem::GetShortName() const
338*f6dc9357SAndroid Build Coastguard Worker {
339*f6dc9357SAndroid Build Coastguard Worker   char s[16];
340*f6dc9357SAndroid Build Coastguard Worker   unsigned i = CopyAndTrim(s, DosName, 8, NameIsLow());
341*f6dc9357SAndroid Build Coastguard Worker   s[i++] = '.';
342*f6dc9357SAndroid Build Coastguard Worker   unsigned j = CopyAndTrim(s + i, DosName + 8, 3, ExtIsLow());
343*f6dc9357SAndroid Build Coastguard Worker   if (j == 0)
344*f6dc9357SAndroid Build Coastguard Worker     i--;
345*f6dc9357SAndroid Build Coastguard Worker   s[i + j] = 0;
346*f6dc9357SAndroid Build Coastguard Worker   return FatStringToUnicode(s);
347*f6dc9357SAndroid Build Coastguard Worker }
348*f6dc9357SAndroid Build Coastguard Worker 
GetName() const349*f6dc9357SAndroid Build Coastguard Worker UString CItem::GetName() const
350*f6dc9357SAndroid Build Coastguard Worker {
351*f6dc9357SAndroid Build Coastguard Worker   if (!UName.IsEmpty())
352*f6dc9357SAndroid Build Coastguard Worker     return UName;
353*f6dc9357SAndroid Build Coastguard Worker   return GetShortName();
354*f6dc9357SAndroid Build Coastguard Worker }
355*f6dc9357SAndroid Build Coastguard Worker 
GetVolName() const356*f6dc9357SAndroid Build Coastguard Worker UString CItem::GetVolName() const
357*f6dc9357SAndroid Build Coastguard Worker {
358*f6dc9357SAndroid Build Coastguard Worker   if (!UName.IsEmpty())
359*f6dc9357SAndroid Build Coastguard Worker     return UName;
360*f6dc9357SAndroid Build Coastguard Worker   char s[12];
361*f6dc9357SAndroid Build Coastguard Worker   unsigned i = CopyAndTrim(s, DosName, 11, false);
362*f6dc9357SAndroid Build Coastguard Worker   s[i] = 0;
363*f6dc9357SAndroid Build Coastguard Worker   return FatStringToUnicode(s);
364*f6dc9357SAndroid Build Coastguard Worker }
365*f6dc9357SAndroid Build Coastguard Worker 
366*f6dc9357SAndroid Build Coastguard Worker struct CDatabase
367*f6dc9357SAndroid Build Coastguard Worker {
368*f6dc9357SAndroid Build Coastguard Worker   CHeader Header;
369*f6dc9357SAndroid Build Coastguard Worker   CObjectVector<CItem> Items;
370*f6dc9357SAndroid Build Coastguard Worker   UInt32 *Fat;
371*f6dc9357SAndroid Build Coastguard Worker   CMyComPtr<IInStream> InStream;
372*f6dc9357SAndroid Build Coastguard Worker   IArchiveOpenCallback *OpenCallback;
373*f6dc9357SAndroid Build Coastguard Worker 
374*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumFreeClusters;
375*f6dc9357SAndroid Build Coastguard Worker   bool VolItemDefined;
376*f6dc9357SAndroid Build Coastguard Worker   CItem VolItem;
377*f6dc9357SAndroid Build Coastguard Worker   UInt32 NumDirClusters;
378*f6dc9357SAndroid Build Coastguard Worker   CByteBuffer ByteBuf;
379*f6dc9357SAndroid Build Coastguard Worker   UInt64 NumCurUsedBytes;
380*f6dc9357SAndroid Build Coastguard Worker 
381*f6dc9357SAndroid Build Coastguard Worker   UInt64 PhySize;
382*f6dc9357SAndroid Build Coastguard Worker 
CDatabaseNArchive::NFat::CDatabase383*f6dc9357SAndroid Build Coastguard Worker   CDatabase(): Fat(NULL) {}
~CDatabaseNArchive::NFat::CDatabase384*f6dc9357SAndroid Build Coastguard Worker   ~CDatabase() { ClearAndClose(); }
385*f6dc9357SAndroid Build Coastguard Worker 
386*f6dc9357SAndroid Build Coastguard Worker   void Clear();
387*f6dc9357SAndroid Build Coastguard Worker   void ClearAndClose();
388*f6dc9357SAndroid Build Coastguard Worker   HRESULT OpenProgressFat(bool changeTotal = true);
389*f6dc9357SAndroid Build Coastguard Worker   HRESULT OpenProgress();
390*f6dc9357SAndroid Build Coastguard Worker 
391*f6dc9357SAndroid Build Coastguard Worker   UString GetItemPath(UInt32 index) const;
392*f6dc9357SAndroid Build Coastguard Worker   HRESULT Open();
393*f6dc9357SAndroid Build Coastguard Worker   HRESULT ReadDir(Int32 parent, UInt32 cluster, unsigned level);
394*f6dc9357SAndroid Build Coastguard Worker 
GetHeadersSizeNArchive::NFat::CDatabase395*f6dc9357SAndroid Build Coastguard Worker   UInt64 GetHeadersSize() const
396*f6dc9357SAndroid Build Coastguard Worker   {
397*f6dc9357SAndroid Build Coastguard Worker     return (UInt64)(Header.DataSector + (NumDirClusters << Header.SectorsPerClusterLog)) << Header.SectorSizeLog;
398*f6dc9357SAndroid Build Coastguard Worker   }
399*f6dc9357SAndroid Build Coastguard Worker   HRESULT SeekToSector(UInt32 sector);
SeekToClusterNArchive::NFat::CDatabase400*f6dc9357SAndroid Build Coastguard Worker   HRESULT SeekToCluster(UInt32 cluster) { return SeekToSector(Header.ClusterToSector(cluster)); }
401*f6dc9357SAndroid Build Coastguard Worker };
402*f6dc9357SAndroid Build Coastguard Worker 
SeekToSector(UInt32 sector)403*f6dc9357SAndroid Build Coastguard Worker HRESULT CDatabase::SeekToSector(UInt32 sector)
404*f6dc9357SAndroid Build Coastguard Worker {
405*f6dc9357SAndroid Build Coastguard Worker   return InStream_SeekSet(InStream, (UInt64)sector << Header.SectorSizeLog);
406*f6dc9357SAndroid Build Coastguard Worker }
407*f6dc9357SAndroid Build Coastguard Worker 
Clear()408*f6dc9357SAndroid Build Coastguard Worker void CDatabase::Clear()
409*f6dc9357SAndroid Build Coastguard Worker {
410*f6dc9357SAndroid Build Coastguard Worker   PhySize = 0;
411*f6dc9357SAndroid Build Coastguard Worker   VolItemDefined = false;
412*f6dc9357SAndroid Build Coastguard Worker   NumDirClusters = 0;
413*f6dc9357SAndroid Build Coastguard Worker   NumCurUsedBytes = 0;
414*f6dc9357SAndroid Build Coastguard Worker 
415*f6dc9357SAndroid Build Coastguard Worker   Items.Clear();
416*f6dc9357SAndroid Build Coastguard Worker   delete []Fat;
417*f6dc9357SAndroid Build Coastguard Worker   Fat = NULL;
418*f6dc9357SAndroid Build Coastguard Worker }
419*f6dc9357SAndroid Build Coastguard Worker 
ClearAndClose()420*f6dc9357SAndroid Build Coastguard Worker void CDatabase::ClearAndClose()
421*f6dc9357SAndroid Build Coastguard Worker {
422*f6dc9357SAndroid Build Coastguard Worker   Clear();
423*f6dc9357SAndroid Build Coastguard Worker   InStream.Release();
424*f6dc9357SAndroid Build Coastguard Worker }
425*f6dc9357SAndroid Build Coastguard Worker 
OpenProgressFat(bool changeTotal)426*f6dc9357SAndroid Build Coastguard Worker HRESULT CDatabase::OpenProgressFat(bool changeTotal)
427*f6dc9357SAndroid Build Coastguard Worker {
428*f6dc9357SAndroid Build Coastguard Worker   if (!OpenCallback)
429*f6dc9357SAndroid Build Coastguard Worker     return S_OK;
430*f6dc9357SAndroid Build Coastguard Worker   if (changeTotal)
431*f6dc9357SAndroid Build Coastguard Worker   {
432*f6dc9357SAndroid Build Coastguard Worker     const UInt64 numTotalBytes = (Header.CalcFatSizeInSectors() << Header.SectorSizeLog) +
433*f6dc9357SAndroid Build Coastguard Worker         ((UInt64)(Header.FatSize - NumFreeClusters) << Header.ClusterSizeLog);
434*f6dc9357SAndroid Build Coastguard Worker     RINOK(OpenCallback->SetTotal(NULL, &numTotalBytes))
435*f6dc9357SAndroid Build Coastguard Worker   }
436*f6dc9357SAndroid Build Coastguard Worker   return OpenCallback->SetCompleted(NULL, &NumCurUsedBytes);
437*f6dc9357SAndroid Build Coastguard Worker }
438*f6dc9357SAndroid Build Coastguard Worker 
OpenProgress()439*f6dc9357SAndroid Build Coastguard Worker HRESULT CDatabase::OpenProgress()
440*f6dc9357SAndroid Build Coastguard Worker {
441*f6dc9357SAndroid Build Coastguard Worker   if (!OpenCallback)
442*f6dc9357SAndroid Build Coastguard Worker     return S_OK;
443*f6dc9357SAndroid Build Coastguard Worker   UInt64 numItems = Items.Size();
444*f6dc9357SAndroid Build Coastguard Worker   return OpenCallback->SetCompleted(&numItems, &NumCurUsedBytes);
445*f6dc9357SAndroid Build Coastguard Worker }
446*f6dc9357SAndroid Build Coastguard Worker 
GetItemPath(UInt32 index) const447*f6dc9357SAndroid Build Coastguard Worker UString CDatabase::GetItemPath(UInt32 index) const
448*f6dc9357SAndroid Build Coastguard Worker {
449*f6dc9357SAndroid Build Coastguard Worker   const CItem *item = &Items[index];
450*f6dc9357SAndroid Build Coastguard Worker   UString name = item->GetName();
451*f6dc9357SAndroid Build Coastguard Worker   for (;;)
452*f6dc9357SAndroid Build Coastguard Worker   {
453*f6dc9357SAndroid Build Coastguard Worker     index = (UInt32)item->Parent;
454*f6dc9357SAndroid Build Coastguard Worker     if (item->Parent < 0)
455*f6dc9357SAndroid Build Coastguard Worker       return name;
456*f6dc9357SAndroid Build Coastguard Worker     item = &Items[index];
457*f6dc9357SAndroid Build Coastguard Worker     name.InsertAtFront(WCHAR_PATH_SEPARATOR);
458*f6dc9357SAndroid Build Coastguard Worker     if (item->UName.IsEmpty())
459*f6dc9357SAndroid Build Coastguard Worker       name.Insert(0, item->GetShortName());
460*f6dc9357SAndroid Build Coastguard Worker     else
461*f6dc9357SAndroid Build Coastguard Worker       name.Insert(0, item->UName);
462*f6dc9357SAndroid Build Coastguard Worker   }
463*f6dc9357SAndroid Build Coastguard Worker }
464*f6dc9357SAndroid Build Coastguard Worker 
AddSubStringToName(wchar_t * dest,const Byte * p,unsigned numChars)465*f6dc9357SAndroid Build Coastguard Worker static wchar_t *AddSubStringToName(wchar_t *dest, const Byte *p, unsigned numChars)
466*f6dc9357SAndroid Build Coastguard Worker {
467*f6dc9357SAndroid Build Coastguard Worker   for (unsigned i = 0; i < numChars; i++)
468*f6dc9357SAndroid Build Coastguard Worker   {
469*f6dc9357SAndroid Build Coastguard Worker     wchar_t c = Get16(p + i * 2);
470*f6dc9357SAndroid Build Coastguard Worker     if (c != 0 && c != 0xFFFF)
471*f6dc9357SAndroid Build Coastguard Worker       *dest++ = c;
472*f6dc9357SAndroid Build Coastguard Worker   }
473*f6dc9357SAndroid Build Coastguard Worker   *dest = 0;
474*f6dc9357SAndroid Build Coastguard Worker   return dest;
475*f6dc9357SAndroid Build Coastguard Worker }
476*f6dc9357SAndroid Build Coastguard Worker 
ReadDir(Int32 parent,UInt32 cluster,unsigned level)477*f6dc9357SAndroid Build Coastguard Worker HRESULT CDatabase::ReadDir(Int32 parent, UInt32 cluster, unsigned level)
478*f6dc9357SAndroid Build Coastguard Worker {
479*f6dc9357SAndroid Build Coastguard Worker   unsigned startIndex = Items.Size();
480*f6dc9357SAndroid Build Coastguard Worker   if (startIndex >= (1 << 30) || level > 256)
481*f6dc9357SAndroid Build Coastguard Worker     return S_FALSE;
482*f6dc9357SAndroid Build Coastguard Worker 
483*f6dc9357SAndroid Build Coastguard Worker   UInt32 sectorIndex = 0;
484*f6dc9357SAndroid Build Coastguard Worker   UInt32 blockSize = Header.ClusterSize();
485*f6dc9357SAndroid Build Coastguard Worker   bool clusterMode = (Header.IsFat32() || parent >= 0);
486*f6dc9357SAndroid Build Coastguard Worker   if (!clusterMode)
487*f6dc9357SAndroid Build Coastguard Worker   {
488*f6dc9357SAndroid Build Coastguard Worker     blockSize = Header.SectorSize();
489*f6dc9357SAndroid Build Coastguard Worker     RINOK(SeekToSector(Header.RootDirSector))
490*f6dc9357SAndroid Build Coastguard Worker   }
491*f6dc9357SAndroid Build Coastguard Worker 
492*f6dc9357SAndroid Build Coastguard Worker   ByteBuf.Alloc(blockSize);
493*f6dc9357SAndroid Build Coastguard Worker   UString curName;
494*f6dc9357SAndroid Build Coastguard Worker   int checkSum = -1;
495*f6dc9357SAndroid Build Coastguard Worker   int numLongRecords = -1;
496*f6dc9357SAndroid Build Coastguard Worker 
497*f6dc9357SAndroid Build Coastguard Worker   for (UInt32 pos = blockSize;; pos += 32)
498*f6dc9357SAndroid Build Coastguard Worker   {
499*f6dc9357SAndroid Build Coastguard Worker     if (pos == blockSize)
500*f6dc9357SAndroid Build Coastguard Worker     {
501*f6dc9357SAndroid Build Coastguard Worker       pos = 0;
502*f6dc9357SAndroid Build Coastguard Worker 
503*f6dc9357SAndroid Build Coastguard Worker       if ((NumDirClusters & 0xFF) == 0)
504*f6dc9357SAndroid Build Coastguard Worker       {
505*f6dc9357SAndroid Build Coastguard Worker         RINOK(OpenProgress())
506*f6dc9357SAndroid Build Coastguard Worker       }
507*f6dc9357SAndroid Build Coastguard Worker 
508*f6dc9357SAndroid Build Coastguard Worker       if (clusterMode)
509*f6dc9357SAndroid Build Coastguard Worker       {
510*f6dc9357SAndroid Build Coastguard Worker         if (Header.IsEoc(cluster))
511*f6dc9357SAndroid Build Coastguard Worker           break;
512*f6dc9357SAndroid Build Coastguard Worker         if (!Header.IsValidCluster(cluster))
513*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
514*f6dc9357SAndroid Build Coastguard Worker         PRF(printf("\nCluster = %4X", cluster));
515*f6dc9357SAndroid Build Coastguard Worker         RINOK(SeekToCluster(cluster))
516*f6dc9357SAndroid Build Coastguard Worker         const UInt32 newCluster = Fat[cluster];
517*f6dc9357SAndroid Build Coastguard Worker         if ((newCluster & kFatItemUsedByDirMask) != 0)
518*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
519*f6dc9357SAndroid Build Coastguard Worker         Fat[cluster] |= kFatItemUsedByDirMask;
520*f6dc9357SAndroid Build Coastguard Worker         cluster = newCluster;
521*f6dc9357SAndroid Build Coastguard Worker         NumDirClusters++;
522*f6dc9357SAndroid Build Coastguard Worker         NumCurUsedBytes += Header.ClusterSize();
523*f6dc9357SAndroid Build Coastguard Worker       }
524*f6dc9357SAndroid Build Coastguard Worker       else if (sectorIndex++ >= Header.NumRootDirSectors)
525*f6dc9357SAndroid Build Coastguard Worker         break;
526*f6dc9357SAndroid Build Coastguard Worker 
527*f6dc9357SAndroid Build Coastguard Worker       RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize))
528*f6dc9357SAndroid Build Coastguard Worker     }
529*f6dc9357SAndroid Build Coastguard Worker 
530*f6dc9357SAndroid Build Coastguard Worker     const Byte *p = ByteBuf + pos;
531*f6dc9357SAndroid Build Coastguard Worker 
532*f6dc9357SAndroid Build Coastguard Worker     if (p[0] == 0)
533*f6dc9357SAndroid Build Coastguard Worker     {
534*f6dc9357SAndroid Build Coastguard Worker       /*
535*f6dc9357SAndroid Build Coastguard Worker       // FreeDOS formats FAT partition with cluster chain longer than required.
536*f6dc9357SAndroid Build Coastguard Worker       if (clusterMode && !Header.IsEoc(cluster))
537*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
538*f6dc9357SAndroid Build Coastguard Worker       */
539*f6dc9357SAndroid Build Coastguard Worker       break;
540*f6dc9357SAndroid Build Coastguard Worker     }
541*f6dc9357SAndroid Build Coastguard Worker 
542*f6dc9357SAndroid Build Coastguard Worker     if (p[0] == 0xE5)
543*f6dc9357SAndroid Build Coastguard Worker     {
544*f6dc9357SAndroid Build Coastguard Worker       if (numLongRecords > 0)
545*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
546*f6dc9357SAndroid Build Coastguard Worker       continue;
547*f6dc9357SAndroid Build Coastguard Worker     }
548*f6dc9357SAndroid Build Coastguard Worker 
549*f6dc9357SAndroid Build Coastguard Worker     Byte attrib = p[11];
550*f6dc9357SAndroid Build Coastguard Worker     if ((attrib & 0x3F) == 0xF)
551*f6dc9357SAndroid Build Coastguard Worker     {
552*f6dc9357SAndroid Build Coastguard Worker       if (p[0] > 0x7F || Get16(p + 26) != 0)
553*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
554*f6dc9357SAndroid Build Coastguard Worker       int longIndex = p[0] & 0x3F;
555*f6dc9357SAndroid Build Coastguard Worker       if (longIndex == 0)
556*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
557*f6dc9357SAndroid Build Coastguard Worker       bool isLast = (p[0] & 0x40) != 0;
558*f6dc9357SAndroid Build Coastguard Worker       if (numLongRecords < 0)
559*f6dc9357SAndroid Build Coastguard Worker       {
560*f6dc9357SAndroid Build Coastguard Worker         if (!isLast)
561*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
562*f6dc9357SAndroid Build Coastguard Worker         numLongRecords = longIndex;
563*f6dc9357SAndroid Build Coastguard Worker       }
564*f6dc9357SAndroid Build Coastguard Worker       else if (isLast || numLongRecords != longIndex)
565*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
566*f6dc9357SAndroid Build Coastguard Worker 
567*f6dc9357SAndroid Build Coastguard Worker       numLongRecords--;
568*f6dc9357SAndroid Build Coastguard Worker 
569*f6dc9357SAndroid Build Coastguard Worker       if (p[12] == 0)
570*f6dc9357SAndroid Build Coastguard Worker       {
571*f6dc9357SAndroid Build Coastguard Worker         wchar_t nameBuf[14];
572*f6dc9357SAndroid Build Coastguard Worker         wchar_t *dest;
573*f6dc9357SAndroid Build Coastguard Worker 
574*f6dc9357SAndroid Build Coastguard Worker         dest = AddSubStringToName(nameBuf, p + 1, 5);
575*f6dc9357SAndroid Build Coastguard Worker         dest = AddSubStringToName(dest, p + 14, 6);
576*f6dc9357SAndroid Build Coastguard Worker         AddSubStringToName(dest, p + 28, 2);
577*f6dc9357SAndroid Build Coastguard Worker         curName = nameBuf + curName;
578*f6dc9357SAndroid Build Coastguard Worker         if (isLast)
579*f6dc9357SAndroid Build Coastguard Worker           checkSum = p[13];
580*f6dc9357SAndroid Build Coastguard Worker         if (checkSum != p[13])
581*f6dc9357SAndroid Build Coastguard Worker           return S_FALSE;
582*f6dc9357SAndroid Build Coastguard Worker       }
583*f6dc9357SAndroid Build Coastguard Worker     }
584*f6dc9357SAndroid Build Coastguard Worker     else
585*f6dc9357SAndroid Build Coastguard Worker     {
586*f6dc9357SAndroid Build Coastguard Worker       if (numLongRecords > 0)
587*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
588*f6dc9357SAndroid Build Coastguard Worker       CItem item;
589*f6dc9357SAndroid Build Coastguard Worker       memcpy(item.DosName, p, 11);
590*f6dc9357SAndroid Build Coastguard Worker 
591*f6dc9357SAndroid Build Coastguard Worker       if (checkSum >= 0)
592*f6dc9357SAndroid Build Coastguard Worker       {
593*f6dc9357SAndroid Build Coastguard Worker         Byte sum = 0;
594*f6dc9357SAndroid Build Coastguard Worker         for (unsigned i = 0; i < 11; i++)
595*f6dc9357SAndroid Build Coastguard Worker           sum = (Byte)(((sum & 1) ? 0x80 : 0) + (sum >> 1) + (Byte)item.DosName[i]);
596*f6dc9357SAndroid Build Coastguard Worker         if (sum == checkSum)
597*f6dc9357SAndroid Build Coastguard Worker           item.UName = curName;
598*f6dc9357SAndroid Build Coastguard Worker       }
599*f6dc9357SAndroid Build Coastguard Worker 
600*f6dc9357SAndroid Build Coastguard Worker       if (item.DosName[0] == 5)
601*f6dc9357SAndroid Build Coastguard Worker         item.DosName[0] = (char)(Byte)0xE5;
602*f6dc9357SAndroid Build Coastguard Worker       item.Attrib = attrib;
603*f6dc9357SAndroid Build Coastguard Worker       item.Flags = p[12];
604*f6dc9357SAndroid Build Coastguard Worker       item.Size = Get32(p + 28);
605*f6dc9357SAndroid Build Coastguard Worker       item.Cluster = Get16(p + 26);
606*f6dc9357SAndroid Build Coastguard Worker       if (Header.NumFatBits > 16)
607*f6dc9357SAndroid Build Coastguard Worker         item.Cluster |= ((UInt32)Get16(p + 20) << 16);
608*f6dc9357SAndroid Build Coastguard Worker       else
609*f6dc9357SAndroid Build Coastguard Worker       {
610*f6dc9357SAndroid Build Coastguard Worker         // OS/2 and WinNT probably can store EA (extended atributes) in that field.
611*f6dc9357SAndroid Build Coastguard Worker       }
612*f6dc9357SAndroid Build Coastguard Worker 
613*f6dc9357SAndroid Build Coastguard Worker       item.CTime = Get32(p + 14);
614*f6dc9357SAndroid Build Coastguard Worker       item.CTime2 = p[13];
615*f6dc9357SAndroid Build Coastguard Worker       item.ADate = Get16(p + 18);
616*f6dc9357SAndroid Build Coastguard Worker       item.MTime = Get32(p + 22);
617*f6dc9357SAndroid Build Coastguard Worker       item.Parent = parent;
618*f6dc9357SAndroid Build Coastguard Worker 
619*f6dc9357SAndroid Build Coastguard Worker       if (attrib == 8)
620*f6dc9357SAndroid Build Coastguard Worker       {
621*f6dc9357SAndroid Build Coastguard Worker         VolItem = item;
622*f6dc9357SAndroid Build Coastguard Worker         VolItemDefined = true;
623*f6dc9357SAndroid Build Coastguard Worker       }
624*f6dc9357SAndroid Build Coastguard Worker       else
625*f6dc9357SAndroid Build Coastguard Worker         if (memcmp(item.DosName, ".          ", 11) != 0 &&
626*f6dc9357SAndroid Build Coastguard Worker             memcmp(item.DosName, "..         ", 11) != 0)
627*f6dc9357SAndroid Build Coastguard Worker       {
628*f6dc9357SAndroid Build Coastguard Worker         if (!item.IsDir())
629*f6dc9357SAndroid Build Coastguard Worker           NumCurUsedBytes += Header.GetFilePackSize(item.Size);
630*f6dc9357SAndroid Build Coastguard Worker         Items.Add(item);
631*f6dc9357SAndroid Build Coastguard Worker         PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1)));
632*f6dc9357SAndroid Build Coastguard Worker       }
633*f6dc9357SAndroid Build Coastguard Worker       numLongRecords = -1;
634*f6dc9357SAndroid Build Coastguard Worker       curName.Empty();
635*f6dc9357SAndroid Build Coastguard Worker       checkSum = -1;
636*f6dc9357SAndroid Build Coastguard Worker     }
637*f6dc9357SAndroid Build Coastguard Worker   }
638*f6dc9357SAndroid Build Coastguard Worker 
639*f6dc9357SAndroid Build Coastguard Worker   unsigned finishIndex = Items.Size();
640*f6dc9357SAndroid Build Coastguard Worker   for (unsigned i = startIndex; i < finishIndex; i++)
641*f6dc9357SAndroid Build Coastguard Worker   {
642*f6dc9357SAndroid Build Coastguard Worker     const CItem &item = Items[i];
643*f6dc9357SAndroid Build Coastguard Worker     if (item.IsDir())
644*f6dc9357SAndroid Build Coastguard Worker     {
645*f6dc9357SAndroid Build Coastguard Worker       PRF(printf("\n%S", GetItemPath(i)));
646*f6dc9357SAndroid Build Coastguard Worker       RINOK(CDatabase::ReadDir((int)i, item.Cluster, level + 1))
647*f6dc9357SAndroid Build Coastguard Worker     }
648*f6dc9357SAndroid Build Coastguard Worker   }
649*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
650*f6dc9357SAndroid Build Coastguard Worker }
651*f6dc9357SAndroid Build Coastguard Worker 
Open()652*f6dc9357SAndroid Build Coastguard Worker HRESULT CDatabase::Open()
653*f6dc9357SAndroid Build Coastguard Worker {
654*f6dc9357SAndroid Build Coastguard Worker   Clear();
655*f6dc9357SAndroid Build Coastguard Worker   bool numFreeClustersDefined = false;
656*f6dc9357SAndroid Build Coastguard Worker   {
657*f6dc9357SAndroid Build Coastguard Worker     Byte buf[kHeaderSize];
658*f6dc9357SAndroid Build Coastguard Worker     RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize))
659*f6dc9357SAndroid Build Coastguard Worker     if (!Header.Parse(buf))
660*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
661*f6dc9357SAndroid Build Coastguard Worker     UInt64 fileSize;
662*f6dc9357SAndroid Build Coastguard Worker     RINOK(InStream_GetSize_SeekToEnd(InStream, fileSize))
663*f6dc9357SAndroid Build Coastguard Worker 
664*f6dc9357SAndroid Build Coastguard Worker     /* we comment that check to support truncated images */
665*f6dc9357SAndroid Build Coastguard Worker     /*
666*f6dc9357SAndroid Build Coastguard Worker     if (fileSize < Header.GetPhySize())
667*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
668*f6dc9357SAndroid Build Coastguard Worker     */
669*f6dc9357SAndroid Build Coastguard Worker 
670*f6dc9357SAndroid Build Coastguard Worker     if (Header.IsFat32())
671*f6dc9357SAndroid Build Coastguard Worker     {
672*f6dc9357SAndroid Build Coastguard Worker       if (((UInt32)Header.FsInfoSector << Header.SectorSizeLog) + kHeaderSize <= fileSize
673*f6dc9357SAndroid Build Coastguard Worker           && SeekToSector(Header.FsInfoSector) == S_OK
674*f6dc9357SAndroid Build Coastguard Worker           && ReadStream_FALSE(InStream, buf, kHeaderSize) == S_OK
675*f6dc9357SAndroid Build Coastguard Worker           && 0xaa550000 == Get32(buf + 508)
676*f6dc9357SAndroid Build Coastguard Worker           && 0x41615252 == Get32(buf)
677*f6dc9357SAndroid Build Coastguard Worker           && 0x61417272 == Get32(buf + 484))
678*f6dc9357SAndroid Build Coastguard Worker       {
679*f6dc9357SAndroid Build Coastguard Worker         NumFreeClusters = Get32(buf + 488);
680*f6dc9357SAndroid Build Coastguard Worker         numFreeClustersDefined = (NumFreeClusters <= Header.FatSize);
681*f6dc9357SAndroid Build Coastguard Worker       }
682*f6dc9357SAndroid Build Coastguard Worker       else
683*f6dc9357SAndroid Build Coastguard Worker         Header.HeadersWarning = true;
684*f6dc9357SAndroid Build Coastguard Worker     }
685*f6dc9357SAndroid Build Coastguard Worker   }
686*f6dc9357SAndroid Build Coastguard Worker 
687*f6dc9357SAndroid Build Coastguard Worker   // numFreeClustersDefined = false; // to recalculate NumFreeClusters
688*f6dc9357SAndroid Build Coastguard Worker   if (!numFreeClustersDefined)
689*f6dc9357SAndroid Build Coastguard Worker     NumFreeClusters = 0;
690*f6dc9357SAndroid Build Coastguard Worker 
691*f6dc9357SAndroid Build Coastguard Worker   CByteBuffer byteBuf;
692*f6dc9357SAndroid Build Coastguard Worker   Fat = new UInt32[Header.FatSize];
693*f6dc9357SAndroid Build Coastguard Worker 
694*f6dc9357SAndroid Build Coastguard Worker   RINOK(OpenProgressFat())
695*f6dc9357SAndroid Build Coastguard Worker   RINOK(SeekToSector(Header.GetFatSector()))
696*f6dc9357SAndroid Build Coastguard Worker   if (Header.NumFatBits == 32)
697*f6dc9357SAndroid Build Coastguard Worker   {
698*f6dc9357SAndroid Build Coastguard Worker     const UInt32 kBufSize = (1 << 15);
699*f6dc9357SAndroid Build Coastguard Worker     byteBuf.Alloc(kBufSize);
700*f6dc9357SAndroid Build Coastguard Worker     for (UInt32 i = 0;;)
701*f6dc9357SAndroid Build Coastguard Worker     {
702*f6dc9357SAndroid Build Coastguard Worker       UInt32 size = Header.FatSize - i;
703*f6dc9357SAndroid Build Coastguard Worker       if (size == 0)
704*f6dc9357SAndroid Build Coastguard Worker         break;
705*f6dc9357SAndroid Build Coastguard Worker       const UInt32 kBufSize32 = kBufSize / 4;
706*f6dc9357SAndroid Build Coastguard Worker       if (size > kBufSize32)
707*f6dc9357SAndroid Build Coastguard Worker         size = kBufSize32;
708*f6dc9357SAndroid Build Coastguard Worker       const UInt32 readSize = Header.SizeToSectors(size * 4) << Header.SectorSizeLog;
709*f6dc9357SAndroid Build Coastguard Worker       RINOK(ReadStream_FALSE(InStream, byteBuf, readSize))
710*f6dc9357SAndroid Build Coastguard Worker       NumCurUsedBytes += readSize;
711*f6dc9357SAndroid Build Coastguard Worker 
712*f6dc9357SAndroid Build Coastguard Worker       const UInt32 *src = (const UInt32 *)(const void *)(const Byte *)byteBuf;
713*f6dc9357SAndroid Build Coastguard Worker       UInt32 *dest = Fat + i;
714*f6dc9357SAndroid Build Coastguard Worker       const UInt32 *srcLim = src + size;
715*f6dc9357SAndroid Build Coastguard Worker       if (numFreeClustersDefined)
716*f6dc9357SAndroid Build Coastguard Worker         do
717*f6dc9357SAndroid Build Coastguard Worker           *dest++ = Get32a(src) & 0x0FFFFFFF;
718*f6dc9357SAndroid Build Coastguard Worker         while (++src != srcLim);
719*f6dc9357SAndroid Build Coastguard Worker       else
720*f6dc9357SAndroid Build Coastguard Worker       {
721*f6dc9357SAndroid Build Coastguard Worker         UInt32 numFreeClusters = 0;
722*f6dc9357SAndroid Build Coastguard Worker         do
723*f6dc9357SAndroid Build Coastguard Worker         {
724*f6dc9357SAndroid Build Coastguard Worker           const UInt32 v = Get32a(src) & 0x0FFFFFFF;
725*f6dc9357SAndroid Build Coastguard Worker           *dest++ = v;
726*f6dc9357SAndroid Build Coastguard Worker           numFreeClusters += (UInt32)(v - 1) >> 31;
727*f6dc9357SAndroid Build Coastguard Worker         }
728*f6dc9357SAndroid Build Coastguard Worker         while (++src != srcLim);
729*f6dc9357SAndroid Build Coastguard Worker         NumFreeClusters += numFreeClusters;
730*f6dc9357SAndroid Build Coastguard Worker       }
731*f6dc9357SAndroid Build Coastguard Worker       i += size;
732*f6dc9357SAndroid Build Coastguard Worker       if ((i & 0xFFFFF) == 0)
733*f6dc9357SAndroid Build Coastguard Worker       {
734*f6dc9357SAndroid Build Coastguard Worker         RINOK(OpenProgressFat(!numFreeClustersDefined))
735*f6dc9357SAndroid Build Coastguard Worker       }
736*f6dc9357SAndroid Build Coastguard Worker     }
737*f6dc9357SAndroid Build Coastguard Worker   }
738*f6dc9357SAndroid Build Coastguard Worker   else
739*f6dc9357SAndroid Build Coastguard Worker   {
740*f6dc9357SAndroid Build Coastguard Worker     const UInt32 kBufSize = (UInt32)Header.CalcFatSizeInSectors() << Header.SectorSizeLog;
741*f6dc9357SAndroid Build Coastguard Worker     NumCurUsedBytes += kBufSize;
742*f6dc9357SAndroid Build Coastguard Worker     byteBuf.Alloc(kBufSize);
743*f6dc9357SAndroid Build Coastguard Worker     Byte *p = byteBuf;
744*f6dc9357SAndroid Build Coastguard Worker     RINOK(ReadStream_FALSE(InStream, p, kBufSize))
745*f6dc9357SAndroid Build Coastguard Worker     const UInt32 fatSize = Header.FatSize;
746*f6dc9357SAndroid Build Coastguard Worker     UInt32 *fat = &Fat[0];
747*f6dc9357SAndroid Build Coastguard Worker     if (Header.NumFatBits == 16)
748*f6dc9357SAndroid Build Coastguard Worker       for (UInt32 j = 0; j < fatSize; j++)
749*f6dc9357SAndroid Build Coastguard Worker         fat[j] = Get16a(p + j * 2);
750*f6dc9357SAndroid Build Coastguard Worker     else
751*f6dc9357SAndroid Build Coastguard Worker       for (UInt32 j = 0; j < fatSize; j++)
752*f6dc9357SAndroid Build Coastguard Worker         fat[j] = (Get16(p + j * 3 / 2) >> ((j & 1) << 2)) & 0xFFF;
753*f6dc9357SAndroid Build Coastguard Worker 
754*f6dc9357SAndroid Build Coastguard Worker     if (!numFreeClustersDefined)
755*f6dc9357SAndroid Build Coastguard Worker     {
756*f6dc9357SAndroid Build Coastguard Worker       UInt32 numFreeClusters = 0;
757*f6dc9357SAndroid Build Coastguard Worker       for (UInt32 i = 0; i < fatSize; i++)
758*f6dc9357SAndroid Build Coastguard Worker         numFreeClusters += (UInt32)(fat[i] - 1) >> 31;
759*f6dc9357SAndroid Build Coastguard Worker       NumFreeClusters = numFreeClusters;
760*f6dc9357SAndroid Build Coastguard Worker     }
761*f6dc9357SAndroid Build Coastguard Worker   }
762*f6dc9357SAndroid Build Coastguard Worker 
763*f6dc9357SAndroid Build Coastguard Worker   RINOK(OpenProgressFat())
764*f6dc9357SAndroid Build Coastguard Worker 
765*f6dc9357SAndroid Build Coastguard Worker   if ((Fat[0] & 0xFF) != Header.MediaType)
766*f6dc9357SAndroid Build Coastguard Worker   {
767*f6dc9357SAndroid Build Coastguard Worker     // that case can mean error in FAT,
768*f6dc9357SAndroid Build Coastguard Worker     // but xdf file: (MediaType == 0xF0 && Fat[0] == 0xFF9)
769*f6dc9357SAndroid Build Coastguard Worker     // 19.00: so we use non-strict check
770*f6dc9357SAndroid Build Coastguard Worker     if ((Fat[0] & 0xFF) < 0xF0)
771*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
772*f6dc9357SAndroid Build Coastguard Worker   }
773*f6dc9357SAndroid Build Coastguard Worker 
774*f6dc9357SAndroid Build Coastguard Worker   RINOK(ReadDir(-1, Header.RootCluster, 0))
775*f6dc9357SAndroid Build Coastguard Worker 
776*f6dc9357SAndroid Build Coastguard Worker   PhySize = Header.GetPhySize();
777*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
778*f6dc9357SAndroid Build Coastguard Worker }
779*f6dc9357SAndroid Build Coastguard Worker 
780*f6dc9357SAndroid Build Coastguard Worker 
781*f6dc9357SAndroid Build Coastguard Worker 
782*f6dc9357SAndroid Build Coastguard Worker Z7_class_CHandler_final:
783*f6dc9357SAndroid Build Coastguard Worker   public IInArchive,
784*f6dc9357SAndroid Build Coastguard Worker   public IInArchiveGetStream,
785*f6dc9357SAndroid Build Coastguard Worker   public CMyUnknownImp,
786*f6dc9357SAndroid Build Coastguard Worker   CDatabase
787*f6dc9357SAndroid Build Coastguard Worker {
788*f6dc9357SAndroid Build Coastguard Worker   Z7_IFACES_IMP_UNK_2(IInArchive, IInArchiveGetStream)
789*f6dc9357SAndroid Build Coastguard Worker };
790*f6dc9357SAndroid Build Coastguard Worker 
791*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
792*f6dc9357SAndroid Build Coastguard Worker {
793*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
794*f6dc9357SAndroid Build Coastguard Worker   *stream = NULL;
795*f6dc9357SAndroid Build Coastguard Worker   const CItem &item = Items[index];
796*f6dc9357SAndroid Build Coastguard Worker   CClusterInStream *streamSpec = new CClusterInStream;
797*f6dc9357SAndroid Build Coastguard Worker   CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
798*f6dc9357SAndroid Build Coastguard Worker   streamSpec->Stream = InStream;
799*f6dc9357SAndroid Build Coastguard Worker   streamSpec->StartOffset = Header.DataSector << Header.SectorSizeLog;
800*f6dc9357SAndroid Build Coastguard Worker   streamSpec->BlockSizeLog = Header.ClusterSizeLog;
801*f6dc9357SAndroid Build Coastguard Worker   streamSpec->Size = item.Size;
802*f6dc9357SAndroid Build Coastguard Worker 
803*f6dc9357SAndroid Build Coastguard Worker   const UInt32 numClusters = Header.GetNumClusters(item.Size);
804*f6dc9357SAndroid Build Coastguard Worker   streamSpec->Vector.ClearAndReserve(numClusters);
805*f6dc9357SAndroid Build Coastguard Worker   UInt32 cluster = item.Cluster;
806*f6dc9357SAndroid Build Coastguard Worker   UInt32 size = item.Size;
807*f6dc9357SAndroid Build Coastguard Worker 
808*f6dc9357SAndroid Build Coastguard Worker   if (size == 0)
809*f6dc9357SAndroid Build Coastguard Worker   {
810*f6dc9357SAndroid Build Coastguard Worker     if (cluster != 0)
811*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
812*f6dc9357SAndroid Build Coastguard Worker   }
813*f6dc9357SAndroid Build Coastguard Worker   else
814*f6dc9357SAndroid Build Coastguard Worker   {
815*f6dc9357SAndroid Build Coastguard Worker     const UInt32 clusterSize = Header.ClusterSize();
816*f6dc9357SAndroid Build Coastguard Worker     for (;; size -= clusterSize)
817*f6dc9357SAndroid Build Coastguard Worker     {
818*f6dc9357SAndroid Build Coastguard Worker       if (!Header.IsValidCluster(cluster))
819*f6dc9357SAndroid Build Coastguard Worker         return S_FALSE;
820*f6dc9357SAndroid Build Coastguard Worker       streamSpec->Vector.AddInReserved(cluster - 2);
821*f6dc9357SAndroid Build Coastguard Worker       cluster = Fat[cluster];
822*f6dc9357SAndroid Build Coastguard Worker       if (size <= clusterSize)
823*f6dc9357SAndroid Build Coastguard Worker         break;
824*f6dc9357SAndroid Build Coastguard Worker     }
825*f6dc9357SAndroid Build Coastguard Worker     if (!Header.IsEocAndUnused(cluster))
826*f6dc9357SAndroid Build Coastguard Worker       return S_FALSE;
827*f6dc9357SAndroid Build Coastguard Worker   }
828*f6dc9357SAndroid Build Coastguard Worker   RINOK(streamSpec->InitAndSeek())
829*f6dc9357SAndroid Build Coastguard Worker   *stream = streamTemp.Detach();
830*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
831*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
832*f6dc9357SAndroid Build Coastguard Worker }
833*f6dc9357SAndroid Build Coastguard Worker 
834*f6dc9357SAndroid Build Coastguard Worker static const Byte kProps[] =
835*f6dc9357SAndroid Build Coastguard Worker {
836*f6dc9357SAndroid Build Coastguard Worker   kpidPath,
837*f6dc9357SAndroid Build Coastguard Worker   kpidIsDir,
838*f6dc9357SAndroid Build Coastguard Worker   kpidSize,
839*f6dc9357SAndroid Build Coastguard Worker   kpidPackSize,
840*f6dc9357SAndroid Build Coastguard Worker   kpidMTime,
841*f6dc9357SAndroid Build Coastguard Worker   kpidCTime,
842*f6dc9357SAndroid Build Coastguard Worker   kpidATime,
843*f6dc9357SAndroid Build Coastguard Worker   kpidAttrib,
844*f6dc9357SAndroid Build Coastguard Worker   kpidShortName
845*f6dc9357SAndroid Build Coastguard Worker };
846*f6dc9357SAndroid Build Coastguard Worker 
847*f6dc9357SAndroid Build Coastguard Worker enum
848*f6dc9357SAndroid Build Coastguard Worker {
849*f6dc9357SAndroid Build Coastguard Worker   kpidNumFats = kpidUserDefined
850*f6dc9357SAndroid Build Coastguard Worker   // kpidOemName,
851*f6dc9357SAndroid Build Coastguard Worker   // kpidVolName,
852*f6dc9357SAndroid Build Coastguard Worker   // kpidFileSysType
853*f6dc9357SAndroid Build Coastguard Worker };
854*f6dc9357SAndroid Build Coastguard Worker 
855*f6dc9357SAndroid Build Coastguard Worker static const CStatProp kArcProps[] =
856*f6dc9357SAndroid Build Coastguard Worker {
857*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidFileSystem, VT_BSTR},
858*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidClusterSize, VT_UI4},
859*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidFreeSpace, VT_UI8},
860*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidHeadersSize, VT_UI8},
861*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidMTime, VT_FILETIME},
862*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidVolumeName, VT_BSTR},
863*f6dc9357SAndroid Build Coastguard Worker 
864*f6dc9357SAndroid Build Coastguard Worker   { "FATs", kpidNumFats, VT_UI4},
865*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidSectorSize, VT_UI4},
866*f6dc9357SAndroid Build Coastguard Worker   { NULL, kpidId, VT_UI4},
867*f6dc9357SAndroid Build Coastguard Worker   // { "OEM Name", kpidOemName, VT_BSTR},
868*f6dc9357SAndroid Build Coastguard Worker   // { "Volume Name", kpidVolName, VT_BSTR},
869*f6dc9357SAndroid Build Coastguard Worker   // { "File System Type", kpidFileSysType, VT_BSTR}
870*f6dc9357SAndroid Build Coastguard Worker   // { NULL, kpidSectorsPerTrack, VT_UI4},
871*f6dc9357SAndroid Build Coastguard Worker   // { NULL, kpidNumHeads, VT_UI4},
872*f6dc9357SAndroid Build Coastguard Worker   // { NULL, kpidHiddenSectors, VT_UI4}
873*f6dc9357SAndroid Build Coastguard Worker };
874*f6dc9357SAndroid Build Coastguard Worker 
875*f6dc9357SAndroid Build Coastguard Worker IMP_IInArchive_Props
876*f6dc9357SAndroid Build Coastguard Worker IMP_IInArchive_ArcProps_WITH_NAME
877*f6dc9357SAndroid Build Coastguard Worker 
878*f6dc9357SAndroid Build Coastguard Worker 
879*f6dc9357SAndroid Build Coastguard Worker static void FatTimeToProp(UInt32 dosTime, UInt32 ms10, NWindows::NCOM::CPropVariant &prop)
880*f6dc9357SAndroid Build Coastguard Worker {
881*f6dc9357SAndroid Build Coastguard Worker   FILETIME localFileTime, utc;
882*f6dc9357SAndroid Build Coastguard Worker   if (NWindows::NTime::DosTime_To_FileTime(dosTime, localFileTime))
883*f6dc9357SAndroid Build Coastguard Worker     if (LocalFileTimeToFileTime(&localFileTime, &utc))
884*f6dc9357SAndroid Build Coastguard Worker     {
885*f6dc9357SAndroid Build Coastguard Worker       UInt64 t64 = (((UInt64)utc.dwHighDateTime) << 32) + utc.dwLowDateTime;
886*f6dc9357SAndroid Build Coastguard Worker       t64 += ms10 * 100000;
887*f6dc9357SAndroid Build Coastguard Worker       utc.dwLowDateTime = (DWORD)t64;
888*f6dc9357SAndroid Build Coastguard Worker       utc.dwHighDateTime = (DWORD)(t64 >> 32);
889*f6dc9357SAndroid Build Coastguard Worker       prop.SetAsTimeFrom_FT_Prec(utc, k_PropVar_TimePrec_Base + 2);
890*f6dc9357SAndroid Build Coastguard Worker     }
891*f6dc9357SAndroid Build Coastguard Worker }
892*f6dc9357SAndroid Build Coastguard Worker 
893*f6dc9357SAndroid Build Coastguard Worker /*
894*f6dc9357SAndroid Build Coastguard Worker static void StringToProp(const Byte *src, unsigned size, NWindows::NCOM::CPropVariant &prop)
895*f6dc9357SAndroid Build Coastguard Worker {
896*f6dc9357SAndroid Build Coastguard Worker   char dest[32];
897*f6dc9357SAndroid Build Coastguard Worker   memcpy(dest, src, size);
898*f6dc9357SAndroid Build Coastguard Worker   dest[size] = 0;
899*f6dc9357SAndroid Build Coastguard Worker   prop = FatStringToUnicode(dest);
900*f6dc9357SAndroid Build Coastguard Worker }
901*f6dc9357SAndroid Build Coastguard Worker 
902*f6dc9357SAndroid Build Coastguard Worker #define STRING_TO_PROP(s, p) StringToProp(s, sizeof(s), prop)
903*f6dc9357SAndroid Build Coastguard Worker */
904*f6dc9357SAndroid Build Coastguard Worker 
905*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
906*f6dc9357SAndroid Build Coastguard Worker {
907*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
908*f6dc9357SAndroid Build Coastguard Worker   NWindows::NCOM::CPropVariant prop;
909*f6dc9357SAndroid Build Coastguard Worker   switch (propID)
910*f6dc9357SAndroid Build Coastguard Worker   {
911*f6dc9357SAndroid Build Coastguard Worker     case kpidFileSystem:
912*f6dc9357SAndroid Build Coastguard Worker     {
913*f6dc9357SAndroid Build Coastguard Worker       char s[16];
914*f6dc9357SAndroid Build Coastguard Worker       s[0] = 'F';
915*f6dc9357SAndroid Build Coastguard Worker       s[1] = 'A';
916*f6dc9357SAndroid Build Coastguard Worker       s[2] = 'T';
917*f6dc9357SAndroid Build Coastguard Worker       ConvertUInt32ToString(Header.NumFatBits, s + 3);
918*f6dc9357SAndroid Build Coastguard Worker       prop = s;
919*f6dc9357SAndroid Build Coastguard Worker       break;
920*f6dc9357SAndroid Build Coastguard Worker     }
921*f6dc9357SAndroid Build Coastguard Worker     case kpidClusterSize: prop = Header.ClusterSize(); break;
922*f6dc9357SAndroid Build Coastguard Worker     case kpidPhySize: prop = PhySize; break;
923*f6dc9357SAndroid Build Coastguard Worker     case kpidFreeSpace: prop = (UInt64)NumFreeClusters << Header.ClusterSizeLog; break;
924*f6dc9357SAndroid Build Coastguard Worker     case kpidHeadersSize: prop = GetHeadersSize(); break;
925*f6dc9357SAndroid Build Coastguard Worker     case kpidMTime: if (VolItemDefined) PropVariant_SetFrom_DosTime(prop, VolItem.MTime); break;
926*f6dc9357SAndroid Build Coastguard Worker     case kpidShortComment:
927*f6dc9357SAndroid Build Coastguard Worker     case kpidVolumeName: if (VolItemDefined) prop = VolItem.GetVolName(); break;
928*f6dc9357SAndroid Build Coastguard Worker     case kpidNumFats: if (Header.NumFats != 2) prop = Header.NumFats; break;
929*f6dc9357SAndroid Build Coastguard Worker     case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
930*f6dc9357SAndroid Build Coastguard Worker     // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break;
931*f6dc9357SAndroid Build Coastguard Worker     // case kpidNumHeads: prop = Header.NumHeads; break;
932*f6dc9357SAndroid Build Coastguard Worker     // case kpidOemName: STRING_TO_PROP(Header.OemName, prop); break;
933*f6dc9357SAndroid Build Coastguard Worker     case kpidId: if (Header.VolFieldsDefined) prop = Header.VolId; break;
934*f6dc9357SAndroid Build Coastguard Worker     // case kpidVolName: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.VolName, prop); break;
935*f6dc9357SAndroid Build Coastguard Worker     // case kpidFileSysType: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.FileSys, prop); break;
936*f6dc9357SAndroid Build Coastguard Worker     // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break;
937*f6dc9357SAndroid Build Coastguard Worker     case kpidWarningFlags:
938*f6dc9357SAndroid Build Coastguard Worker     {
939*f6dc9357SAndroid Build Coastguard Worker       UInt32 v = 0;
940*f6dc9357SAndroid Build Coastguard Worker       if (Header.HeadersWarning) v |= kpv_ErrorFlags_HeadersError;
941*f6dc9357SAndroid Build Coastguard Worker       if (v != 0)
942*f6dc9357SAndroid Build Coastguard Worker         prop = v;
943*f6dc9357SAndroid Build Coastguard Worker       break;
944*f6dc9357SAndroid Build Coastguard Worker     }
945*f6dc9357SAndroid Build Coastguard Worker   }
946*f6dc9357SAndroid Build Coastguard Worker   prop.Detach(value);
947*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
948*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
949*f6dc9357SAndroid Build Coastguard Worker }
950*f6dc9357SAndroid Build Coastguard Worker 
951*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
952*f6dc9357SAndroid Build Coastguard Worker {
953*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
954*f6dc9357SAndroid Build Coastguard Worker   NWindows::NCOM::CPropVariant prop;
955*f6dc9357SAndroid Build Coastguard Worker   const CItem &item = Items[index];
956*f6dc9357SAndroid Build Coastguard Worker   switch (propID)
957*f6dc9357SAndroid Build Coastguard Worker   {
958*f6dc9357SAndroid Build Coastguard Worker     case kpidPath: prop = GetItemPath(index); break;
959*f6dc9357SAndroid Build Coastguard Worker     case kpidShortName: prop = item.GetShortName(); break;
960*f6dc9357SAndroid Build Coastguard Worker     case kpidIsDir: prop = item.IsDir(); break;
961*f6dc9357SAndroid Build Coastguard Worker     case kpidMTime: PropVariant_SetFrom_DosTime(prop, item.MTime); break;
962*f6dc9357SAndroid Build Coastguard Worker     case kpidCTime: FatTimeToProp(item.CTime, item.CTime2, prop); break;
963*f6dc9357SAndroid Build Coastguard Worker     case kpidATime: PropVariant_SetFrom_DosTime(prop, ((UInt32)item.ADate << 16)); break;
964*f6dc9357SAndroid Build Coastguard Worker     case kpidAttrib: prop = (UInt32)item.Attrib; break;
965*f6dc9357SAndroid Build Coastguard Worker     case kpidSize: if (!item.IsDir()) prop = item.Size; break;
966*f6dc9357SAndroid Build Coastguard Worker     case kpidPackSize: if (!item.IsDir()) prop = Header.GetFilePackSize(item.Size); break;
967*f6dc9357SAndroid Build Coastguard Worker   }
968*f6dc9357SAndroid Build Coastguard Worker   prop.Detach(value);
969*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
970*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
971*f6dc9357SAndroid Build Coastguard Worker }
972*f6dc9357SAndroid Build Coastguard Worker 
973*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback))
974*f6dc9357SAndroid Build Coastguard Worker {
975*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
976*f6dc9357SAndroid Build Coastguard Worker   {
977*f6dc9357SAndroid Build Coastguard Worker     OpenCallback = callback;
978*f6dc9357SAndroid Build Coastguard Worker     InStream = stream;
979*f6dc9357SAndroid Build Coastguard Worker     HRESULT res;
980*f6dc9357SAndroid Build Coastguard Worker     try
981*f6dc9357SAndroid Build Coastguard Worker     {
982*f6dc9357SAndroid Build Coastguard Worker       res = CDatabase::Open();
983*f6dc9357SAndroid Build Coastguard Worker       if (res == S_OK)
984*f6dc9357SAndroid Build Coastguard Worker         return S_OK;
985*f6dc9357SAndroid Build Coastguard Worker     }
986*f6dc9357SAndroid Build Coastguard Worker     catch(...)
987*f6dc9357SAndroid Build Coastguard Worker     {
988*f6dc9357SAndroid Build Coastguard Worker       Close();
989*f6dc9357SAndroid Build Coastguard Worker       throw;
990*f6dc9357SAndroid Build Coastguard Worker     }
991*f6dc9357SAndroid Build Coastguard Worker     Close();
992*f6dc9357SAndroid Build Coastguard Worker     return res;
993*f6dc9357SAndroid Build Coastguard Worker   }
994*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
995*f6dc9357SAndroid Build Coastguard Worker }
996*f6dc9357SAndroid Build Coastguard Worker 
997*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::Close())
998*f6dc9357SAndroid Build Coastguard Worker {
999*f6dc9357SAndroid Build Coastguard Worker   ClearAndClose();
1000*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
1001*f6dc9357SAndroid Build Coastguard Worker }
1002*f6dc9357SAndroid Build Coastguard Worker 
1003*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1004*f6dc9357SAndroid Build Coastguard Worker     Int32 testMode, IArchiveExtractCallback *extractCallback))
1005*f6dc9357SAndroid Build Coastguard Worker {
1006*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_BEGIN
1007*f6dc9357SAndroid Build Coastguard Worker   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1008*f6dc9357SAndroid Build Coastguard Worker   if (allFilesMode)
1009*f6dc9357SAndroid Build Coastguard Worker     numItems = Items.Size();
1010*f6dc9357SAndroid Build Coastguard Worker   if (numItems == 0)
1011*f6dc9357SAndroid Build Coastguard Worker     return S_OK;
1012*f6dc9357SAndroid Build Coastguard Worker   UInt32 i;
1013*f6dc9357SAndroid Build Coastguard Worker   UInt64 totalSize = 0;
1014*f6dc9357SAndroid Build Coastguard Worker   for (i = 0; i < numItems; i++)
1015*f6dc9357SAndroid Build Coastguard Worker   {
1016*f6dc9357SAndroid Build Coastguard Worker     const CItem &item = Items[allFilesMode ? i : indices[i]];
1017*f6dc9357SAndroid Build Coastguard Worker     if (!item.IsDir())
1018*f6dc9357SAndroid Build Coastguard Worker       totalSize += item.Size;
1019*f6dc9357SAndroid Build Coastguard Worker   }
1020*f6dc9357SAndroid Build Coastguard Worker   RINOK(extractCallback->SetTotal(totalSize))
1021*f6dc9357SAndroid Build Coastguard Worker 
1022*f6dc9357SAndroid Build Coastguard Worker   UInt64 totalPackSize;
1023*f6dc9357SAndroid Build Coastguard Worker   totalSize = totalPackSize = 0;
1024*f6dc9357SAndroid Build Coastguard Worker 
1025*f6dc9357SAndroid Build Coastguard Worker   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
1026*f6dc9357SAndroid Build Coastguard Worker   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1027*f6dc9357SAndroid Build Coastguard Worker 
1028*f6dc9357SAndroid Build Coastguard Worker   CLocalProgress *lps = new CLocalProgress;
1029*f6dc9357SAndroid Build Coastguard Worker   CMyComPtr<ICompressProgressInfo> progress = lps;
1030*f6dc9357SAndroid Build Coastguard Worker   lps->Init(extractCallback, false);
1031*f6dc9357SAndroid Build Coastguard Worker 
1032*f6dc9357SAndroid Build Coastguard Worker   CDummyOutStream *outStreamSpec = new CDummyOutStream;
1033*f6dc9357SAndroid Build Coastguard Worker   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
1034*f6dc9357SAndroid Build Coastguard Worker 
1035*f6dc9357SAndroid Build Coastguard Worker   for (i = 0;; i++)
1036*f6dc9357SAndroid Build Coastguard Worker   {
1037*f6dc9357SAndroid Build Coastguard Worker     lps->InSize = totalPackSize;
1038*f6dc9357SAndroid Build Coastguard Worker     lps->OutSize = totalSize;
1039*f6dc9357SAndroid Build Coastguard Worker     RINOK(lps->SetCur())
1040*f6dc9357SAndroid Build Coastguard Worker     if (i == numItems)
1041*f6dc9357SAndroid Build Coastguard Worker       break;
1042*f6dc9357SAndroid Build Coastguard Worker     CMyComPtr<ISequentialOutStream> realOutStream;
1043*f6dc9357SAndroid Build Coastguard Worker     const Int32 askMode = testMode ?
1044*f6dc9357SAndroid Build Coastguard Worker         NExtract::NAskMode::kTest :
1045*f6dc9357SAndroid Build Coastguard Worker         NExtract::NAskMode::kExtract;
1046*f6dc9357SAndroid Build Coastguard Worker     const UInt32 index = allFilesMode ? i : indices[i];
1047*f6dc9357SAndroid Build Coastguard Worker     const CItem &item = Items[index];
1048*f6dc9357SAndroid Build Coastguard Worker     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1049*f6dc9357SAndroid Build Coastguard Worker 
1050*f6dc9357SAndroid Build Coastguard Worker     if (item.IsDir())
1051*f6dc9357SAndroid Build Coastguard Worker     {
1052*f6dc9357SAndroid Build Coastguard Worker       RINOK(extractCallback->PrepareOperation(askMode))
1053*f6dc9357SAndroid Build Coastguard Worker       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
1054*f6dc9357SAndroid Build Coastguard Worker       continue;
1055*f6dc9357SAndroid Build Coastguard Worker     }
1056*f6dc9357SAndroid Build Coastguard Worker 
1057*f6dc9357SAndroid Build Coastguard Worker     totalPackSize += Header.GetFilePackSize(item.Size);
1058*f6dc9357SAndroid Build Coastguard Worker     totalSize += item.Size;
1059*f6dc9357SAndroid Build Coastguard Worker 
1060*f6dc9357SAndroid Build Coastguard Worker     if (!testMode && !realOutStream)
1061*f6dc9357SAndroid Build Coastguard Worker       continue;
1062*f6dc9357SAndroid Build Coastguard Worker     RINOK(extractCallback->PrepareOperation(askMode))
1063*f6dc9357SAndroid Build Coastguard Worker 
1064*f6dc9357SAndroid Build Coastguard Worker     outStreamSpec->SetStream(realOutStream);
1065*f6dc9357SAndroid Build Coastguard Worker     realOutStream.Release();
1066*f6dc9357SAndroid Build Coastguard Worker     outStreamSpec->Init();
1067*f6dc9357SAndroid Build Coastguard Worker 
1068*f6dc9357SAndroid Build Coastguard Worker     int res = NExtract::NOperationResult::kDataError;
1069*f6dc9357SAndroid Build Coastguard Worker     CMyComPtr<ISequentialInStream> inStream;
1070*f6dc9357SAndroid Build Coastguard Worker     HRESULT hres = GetStream(index, &inStream);
1071*f6dc9357SAndroid Build Coastguard Worker     if (hres != S_FALSE)
1072*f6dc9357SAndroid Build Coastguard Worker     {
1073*f6dc9357SAndroid Build Coastguard Worker       RINOK(hres)
1074*f6dc9357SAndroid Build Coastguard Worker       if (inStream)
1075*f6dc9357SAndroid Build Coastguard Worker       {
1076*f6dc9357SAndroid Build Coastguard Worker         RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
1077*f6dc9357SAndroid Build Coastguard Worker         if (copyCoderSpec->TotalSize == item.Size)
1078*f6dc9357SAndroid Build Coastguard Worker           res = NExtract::NOperationResult::kOK;
1079*f6dc9357SAndroid Build Coastguard Worker       }
1080*f6dc9357SAndroid Build Coastguard Worker     }
1081*f6dc9357SAndroid Build Coastguard Worker     outStreamSpec->ReleaseStream();
1082*f6dc9357SAndroid Build Coastguard Worker     RINOK(extractCallback->SetOperationResult(res))
1083*f6dc9357SAndroid Build Coastguard Worker   }
1084*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
1085*f6dc9357SAndroid Build Coastguard Worker   COM_TRY_END
1086*f6dc9357SAndroid Build Coastguard Worker }
1087*f6dc9357SAndroid Build Coastguard Worker 
1088*f6dc9357SAndroid Build Coastguard Worker Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1089*f6dc9357SAndroid Build Coastguard Worker {
1090*f6dc9357SAndroid Build Coastguard Worker   *numItems = Items.Size();
1091*f6dc9357SAndroid Build Coastguard Worker   return S_OK;
1092*f6dc9357SAndroid Build Coastguard Worker }
1093*f6dc9357SAndroid Build Coastguard Worker 
1094*f6dc9357SAndroid Build Coastguard Worker static const Byte k_Signature[] = { 0x55, 0xAA };
1095*f6dc9357SAndroid Build Coastguard Worker 
1096*f6dc9357SAndroid Build Coastguard Worker REGISTER_ARC_I(
1097*f6dc9357SAndroid Build Coastguard Worker   "FAT", "fat img", NULL, 0xDA,
1098*f6dc9357SAndroid Build Coastguard Worker   k_Signature,
1099*f6dc9357SAndroid Build Coastguard Worker   0x1FE,
1100*f6dc9357SAndroid Build Coastguard Worker   0,
1101*f6dc9357SAndroid Build Coastguard Worker   IsArc_Fat)
1102*f6dc9357SAndroid Build Coastguard Worker 
1103*f6dc9357SAndroid Build Coastguard Worker }}
1104