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