5 // #include <stdio.h>
\r
7 #include "../../../C/CpuArch.h"
\r
9 #include "Common/Buffer.h"
\r
10 #include "Common/ComTry.h"
\r
11 #include "Common/IntToString.h"
\r
12 #include "Common/MyCom.h"
\r
13 #include "Common/StringConvert.h"
\r
15 #include "Windows/PropVariant.h"
\r
16 #include "Windows/Time.h"
\r
18 #include "../Common/LimitedStreams.h"
\r
19 #include "../Common/ProgressUtils.h"
\r
20 #include "../Common/RegisterArc.h"
\r
21 #include "../Common/StreamUtils.h"
\r
23 #include "../Compress/CopyCoder.h"
\r
25 #include "Common/DummyOutStream.h"
\r
27 #define Get16(p) GetUi16(p)
\r
28 #define Get32(p) GetUi32(p)
\r
30 #define PRF(x) /* x */
\r
32 namespace NArchive {
\r
35 static const UInt32 kFatItemUsedByDirMask = (UInt32)1 << 31;
\r
40 UInt16 NumReservedSectors;
\r
42 UInt32 NumFatSectors;
\r
43 UInt32 RootDirSector;
\r
44 UInt32 NumRootDirSectors;
\r
52 Byte SectorsPerClusterLog;
\r
53 Byte ClusterSizeLog;
\r
55 UInt16 SectorsPerTrack;
\r
57 UInt32 NumHiddenSectors;
\r
59 bool VolFieldsDefined;
\r
62 // Byte VolName[11];
\r
70 UInt16 FsInfoSector;
\r
73 bool IsFat32() const { return NumFatBits == 32; }
\r
74 UInt64 GetPhySize() const { return (UInt64)NumSectors << SectorSizeLog; }
\r
75 UInt32 SectorSize() const { return (UInt32)1 << SectorSizeLog; }
\r
76 UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; }
\r
77 UInt32 ClusterToSector(UInt32 c) const { return DataSector + ((c - 2) << SectorsPerClusterLog); }
\r
78 UInt32 IsEoc(UInt32 c) const { return c > BadCluster; }
\r
79 UInt32 IsEocAndUnused(UInt32 c) const { return c > BadCluster && (c & kFatItemUsedByDirMask) == 0; }
\r
80 UInt32 IsValidCluster(UInt32 c) const { return c >= 2 && c < FatSize; }
\r
81 UInt32 SizeToSectors(UInt32 size) const { return (size + SectorSize() - 1) >> SectorSizeLog; }
\r
82 UInt32 CalcFatSizeInSectors() const { return SizeToSectors((FatSize * (NumFatBits / 4) + 1) / 2); }
\r
84 UInt32 GetFatSector() const
\r
86 UInt32 index = (IsFat32() && (Flags & 0x80) != 0) ? (Flags & 0xF) : 0;
\r
87 if (index > NumFats)
\r
89 return NumReservedSectors + index * NumFatSectors;
\r
92 UInt64 GetFilePackSize(UInt32 unpackSize) const
\r
94 UInt64 mask = ClusterSize() - 1;
\r
95 return (unpackSize + mask) & ~mask;
\r
98 UInt32 GetNumClusters(UInt32 size) const
\r
99 { return (UInt32)(((UInt64)size + ClusterSize() - 1) >> ClusterSizeLog); }
\r
101 bool Parse(const Byte *p);
\r
104 static int GetLog(UInt32 num)
\r
106 for (int i = 0; i < 31; i++)
\r
107 if (((UInt32)1 << i) == num)
\r
112 bool CHeader::Parse(const Byte *p)
\r
114 if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA)
\r
117 int codeOffset = 0;
\r
120 case 0xE9: codeOffset = 3 + (Int16)Get16(p + 1); break;
\r
121 case 0xEB: if (p[2] != 0x90) return false; codeOffset = 2 + (signed char)p[1]; break;
\r
122 default: return false;
\r
125 int s = GetLog(Get16(p + 11));
\r
126 if (s < 9 || s > 12)
\r
128 SectorSizeLog = (Byte)s;
\r
132 SectorsPerClusterLog = (Byte)s;
\r
133 ClusterSizeLog = SectorSizeLog + SectorsPerClusterLog;
\r
136 NumReservedSectors = Get16(p + 14);
\r
137 if (NumReservedSectors == 0)
\r
141 if (NumFats < 1 || NumFats > 4)
\r
144 UInt16 numRootDirEntries = Get16(p + 17);
\r
145 if (numRootDirEntries == 0)
\r
147 if (codeOffset < 90)
\r
150 NumRootDirSectors = 0;
\r
154 if (codeOffset < 62)
\r
157 UInt32 mask = (1 << (SectorSizeLog - 5)) - 1;
\r
158 if ((numRootDirEntries & mask) != 0)
\r
160 NumRootDirSectors = (numRootDirEntries + mask) >> (SectorSizeLog - 5);
\r
163 NumSectors = Get16(p + 19);
\r
164 if (NumSectors == 0)
\r
165 NumSectors = Get32(p + 32);
\r
166 else if (IsFat32())
\r
170 NumFatSectors = Get16(p + 22);
\r
171 SectorsPerTrack = Get16(p + 24);
\r
172 NumHeads = Get16(p + 26);
\r
173 NumHiddenSectors = Get32(p + 28);
\r
175 // memcpy(OemName, p + 3, 5);
\r
180 if (NumFatSectors != 0)
\r
182 NumFatSectors = Get32(p);
\r
183 if (NumFatSectors >= (1 << 24))
\r
186 Flags = Get16(p + 4);
\r
187 if (Get16(p + 6) != 0)
\r
189 RootCluster = Get32(p + 8);
\r
190 FsInfoSector = Get16(p + 12);
\r
191 for (int i = 16; i < 28; i++)
\r
197 // DriveNumber = p[0];
\r
198 VolFieldsDefined = (p[2] == 0x29); // ExtendedBootSig
\r
199 VolId = Get32(p + 3);
\r
200 // memcpy(VolName, p + 7, 11);
\r
201 // memcpy(FileSys, p + 18, 8);
\r
203 if (NumFatSectors == 0)
\r
205 RootDirSector = NumReservedSectors + NumFatSectors * NumFats;
\r
206 DataSector = RootDirSector + NumRootDirSectors;
\r
207 if (NumSectors < DataSector)
\r
209 UInt32 numDataSectors = NumSectors - DataSector;
\r
210 UInt32 numClusters = numDataSectors >> SectorsPerClusterLog;
\r
212 BadCluster = 0x0FFFFFF7;
\r
213 if (numClusters < 0xFFF5)
\r
215 if (NumFatBits == 32)
\r
217 NumFatBits = (numClusters < 0xFF5) ? 12: 16;
\r
218 BadCluster &= ((1 << NumFatBits) - 1);
\r
220 else if (NumFatBits != 32)
\r
223 FatSize = numClusters + 2;
\r
224 if (FatSize > BadCluster || CalcFatSizeInSectors() > NumFatSectors)
\r
243 // NT uses Flags to store Low Case status
\r
244 bool NameIsLow() const { return (Flags & 0x8) != 0; }
\r
245 bool ExtIsLow() const { return (Flags & 0x10) != 0; }
\r
246 bool IsDir() const { return (Attrib & 0x10) != 0; }
\r
247 UString GetShortName() const;
\r
248 UString GetName() const;
\r
249 UString GetVolName() const;
\r
252 static int CopyAndTrim(char *dest, const char *src, int size, bool toLower)
\r
255 memcpy(dest, src, size);
\r
257 for (i = 0; i < size; i++)
\r
260 if (c >= 'A' && c <= 'Z')
\r
261 dest[i] = c + 0x20;
\r
263 for (i = size - 1; i >= 0 && dest[i] == ' '; i--);
\r
267 static UString FatStringToUnicode(const char *s)
\r
269 return MultiByteToUnicodeString(s, CP_OEMCP);
\r
272 UString CItem::GetShortName() const
\r
275 int i = CopyAndTrim(s, DosName, 8, NameIsLow());
\r
277 int j = CopyAndTrim(s + i, DosName + 8, 3, ExtIsLow());
\r
281 return FatStringToUnicode(s);
\r
284 UString CItem::GetName() const
\r
286 if (!UName.IsEmpty())
\r
288 return GetShortName();
\r
291 UString CItem::GetVolName() const
\r
293 if (!UName.IsEmpty())
\r
296 int i = CopyAndTrim(s, DosName, 11, false);
\r
298 return FatStringToUnicode(s);
\r
304 CObjectVector<CItem> Items;
\r
306 CMyComPtr<IInStream> InStream;
\r
307 IArchiveOpenCallback *OpenCallback;
\r
309 UInt32 NumFreeClusters;
\r
310 bool VolItemDefined;
\r
312 UInt32 NumDirClusters;
\r
313 CByteBuffer ByteBuf;
\r
314 UInt64 NumCurUsedBytes;
\r
316 CDatabase(): Fat(0) {}
\r
317 ~CDatabase() { ClearAndClose(); }
\r
320 void ClearAndClose();
\r
321 HRESULT OpenProgressFat(bool changeTotal = true);
\r
322 HRESULT OpenProgress();
\r
324 UString GetItemPath(Int32 index) const;
\r
326 HRESULT ReadDir(Int32 parent, UInt32 cluster, int level);
\r
328 UInt64 GetHeadersSize() const
\r
330 return (UInt64)(Header.DataSector + (NumDirClusters << Header.SectorsPerClusterLog)) << Header.SectorSizeLog;
\r
332 HRESULT SeekToSector(UInt32 sector);
\r
333 HRESULT SeekToCluster(UInt32 cluster) { return SeekToSector(Header.ClusterToSector(cluster)); }
\r
336 HRESULT CDatabase::SeekToSector(UInt32 sector)
\r
338 return InStream->Seek((UInt64)sector << Header.SectorSizeLog, STREAM_SEEK_SET, NULL);
\r
341 void CDatabase::Clear()
\r
343 VolItemDefined = false;
\r
344 NumDirClusters = 0;
\r
345 NumCurUsedBytes = 0;
\r
352 void CDatabase::ClearAndClose()
\r
355 InStream.Release();
\r
358 HRESULT CDatabase::OpenProgressFat(bool changeTotal)
\r
364 UInt64 numTotalBytes = (Header.CalcFatSizeInSectors() << Header.SectorSizeLog) +
\r
365 ((UInt64)(Header.FatSize - NumFreeClusters) << Header.ClusterSizeLog);
\r
366 RINOK(OpenCallback->SetTotal(NULL, &numTotalBytes));
\r
368 return OpenCallback->SetCompleted(NULL, &NumCurUsedBytes);
\r
371 HRESULT CDatabase::OpenProgress()
\r
375 UInt64 numItems = Items.Size();
\r
376 return OpenCallback->SetCompleted(&numItems, &NumCurUsedBytes);
\r
379 UString CDatabase::GetItemPath(Int32 index) const
\r
381 const CItem *item = &Items[index];
\r
382 UString name = item->GetName();
\r
385 index = item->Parent;
\r
388 item = &Items[index];
\r
389 name = item->GetName() + WCHAR_PATH_SEPARATOR + name;
\r
393 static wchar_t *AddSubStringToName(wchar_t *dest, const Byte *p, int numChars)
\r
395 for (int i = 0; i < numChars; i++)
\r
397 wchar_t c = Get16(p + i * 2);
\r
398 if (c != 0 && c != 0xFFFF)
\r
405 HRESULT CDatabase::ReadDir(Int32 parent, UInt32 cluster, int level)
\r
407 int startIndex = Items.Size();
\r
408 if (startIndex >= (1 << 30) || level > 256)
\r
411 UInt32 sectorIndex = 0;
\r
412 UInt32 blockSize = Header.ClusterSize();
\r
413 bool clusterMode = (Header.IsFat32() || parent >= 0);
\r
416 blockSize = Header.SectorSize();
\r
417 RINOK(SeekToSector(Header.RootDirSector));
\r
420 ByteBuf.SetCapacity(blockSize);
\r
423 int numLongRecords = -1;
\r
424 for (UInt32 pos = blockSize;; pos += 32)
\r
426 if (pos == blockSize)
\r
430 if ((NumDirClusters & 0xFF) == 0)
\r
432 RINOK(OpenProgress());
\r
437 if (Header.IsEoc(cluster))
\r
439 if (!Header.IsValidCluster(cluster))
\r
441 PRF(printf("\nCluster = %4X", cluster));
\r
442 RINOK(SeekToCluster(cluster));
\r
443 UInt32 newCluster = Fat[cluster];
\r
444 if ((newCluster & kFatItemUsedByDirMask) != 0)
\r
446 Fat[cluster] |= kFatItemUsedByDirMask;
\r
447 cluster = newCluster;
\r
449 NumCurUsedBytes += Header.ClusterSize();
\r
451 else if (sectorIndex++ >= Header.NumRootDirSectors)
\r
454 RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize));
\r
456 const Byte *p = ByteBuf + pos;
\r
460 // FreeDOS formats FAT partition with cluster chain longer than required.
\r
461 if (clusterMode && !Header.IsEoc(cluster))
\r
468 if (numLongRecords > 0)
\r
473 Byte attrib = p[11];
\r
474 if ((attrib & 0x3F) == 0xF)
\r
476 if (p[0] > 0x7F || Get16(p + 26) != 0)
\r
478 int longIndex = p[0] & 0x3F;
\r
479 if (longIndex == 0)
\r
481 bool isLast = (p[0] & 0x40) != 0;
\r
482 if (numLongRecords < 0)
\r
486 numLongRecords = longIndex;
\r
488 else if (isLast || numLongRecords != longIndex)
\r
495 wchar_t nameBuf[14];
\r
498 dest = AddSubStringToName(nameBuf, p + 1, 5);
\r
499 dest = AddSubStringToName(dest, p + 14, 6);
\r
500 AddSubStringToName(dest, p + 28, 2);
\r
501 curName = nameBuf + curName;
\r
504 if (checkSum != p[13])
\r
510 if (numLongRecords > 0)
\r
513 memcpy(item.DosName, p, 11);
\r
518 for (int i = 0; i < 11; i++)
\r
519 sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + (Byte)item.DosName[i];
\r
520 if (sum == checkSum)
\r
521 item.UName = curName;
\r
524 if (item.DosName[0] == 5)
\r
525 item.DosName[0] = (char)(Byte)0xE5;
\r
526 item.Attrib = attrib;
\r
527 item.Flags = p[12];
\r
528 item.Size = Get32(p + 28);
\r
529 item.Cluster = Get16(p + 26);
\r
530 if (Header.NumFatBits > 16)
\r
531 item.Cluster |= ((UInt32)Get16(p + 20) << 16);
\r
534 // OS/2 and WinNT probably can store EA (extended atributes) in that field.
\r
537 item.CTime = Get32(p + 14);
\r
538 item.CTime2 = p[13];
\r
539 item.ADate = Get16(p + 18);
\r
540 item.MTime = Get32(p + 22);
\r
541 item.Parent = parent;
\r
546 VolItemDefined = true;
\r
549 if (memcmp(item.DosName, ". ", 11) != 0 &&
\r
550 memcmp(item.DosName, ".. ", 11) != 0)
\r
553 NumCurUsedBytes += Header.GetFilePackSize(item.Size);
\r
555 PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1)));
\r
557 numLongRecords = -1;
\r
563 int finishIndex = Items.Size();
\r
564 for (int i = startIndex; i < finishIndex; i++)
\r
566 const CItem &item = Items[i];
\r
569 PRF(printf("\n%S", GetItemPath(i)));
\r
570 RINOK(CDatabase::ReadDir(i, item.Cluster, level + 1));
\r
576 HRESULT CDatabase::Open()
\r
579 bool numFreeClustersDefined = false;
\r
581 static const UInt32 kHeaderSize = 512;
\r
582 Byte buf[kHeaderSize];
\r
583 RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize));
\r
584 if (!Header.Parse(buf))
\r
587 RINOK(InStream->Seek(0, STREAM_SEEK_END, &fileSize));
\r
589 /* we comment that check to support truncated images */
\r
591 if (fileSize < Header.GetPhySize())
\r
595 if (Header.IsFat32())
\r
597 SeekToSector(Header.FsInfoSector);
\r
598 RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize));
\r
599 if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
\r
601 if (Get32(buf) == 0x41615252 && Get32(buf + 484) == 0x61417272)
\r
603 NumFreeClusters = Get32(buf + 488);
\r
604 numFreeClustersDefined = (NumFreeClusters <= Header.FatSize);
\r
609 // numFreeClustersDefined = false; // to recalculate NumFreeClusters
\r
610 if (!numFreeClustersDefined)
\r
611 NumFreeClusters = 0;
\r
613 CByteBuffer byteBuf;
\r
614 Fat = new UInt32[Header.FatSize];
\r
616 RINOK(OpenProgressFat());
\r
617 RINOK(SeekToSector(Header.GetFatSector()));
\r
618 if (Header.NumFatBits == 32)
\r
620 const UInt32 kBufSize = (1 << 15);
\r
621 byteBuf.SetCapacity(kBufSize);
\r
622 for (UInt32 i = 0; i < Header.FatSize;)
\r
624 UInt32 size = Header.FatSize - i;
\r
625 const UInt32 kBufSize32 = kBufSize / 4;
\r
626 if (size > kBufSize32)
\r
628 UInt32 readSize = Header.SizeToSectors(size * 4) << Header.SectorSizeLog;
\r
629 RINOK(ReadStream_FALSE(InStream, byteBuf, readSize));
\r
630 NumCurUsedBytes += readSize;
\r
632 const UInt32 *src = (const UInt32 *)(const Byte *)byteBuf;
\r
633 UInt32 *dest = Fat + i;
\r
634 if (numFreeClustersDefined)
\r
635 for (UInt32 j = 0; j < size; j++)
\r
636 dest[j] = Get32(src + j) & 0x0FFFFFFF;
\r
639 UInt32 numFreeClusters = 0;
\r
640 for (UInt32 j = 0; j < size; j++)
\r
642 UInt32 v = Get32(src + j) & 0x0FFFFFFF;
\r
643 numFreeClusters += (UInt32)(v - 1) >> 31;
\r
646 NumFreeClusters += numFreeClusters;
\r
649 if ((i & 0xFFFFF) == 0)
\r
651 RINOK(OpenProgressFat(!numFreeClustersDefined));
\r
657 const UInt32 kBufSize = (UInt32)Header.CalcFatSizeInSectors() << Header.SectorSizeLog;
\r
658 NumCurUsedBytes += kBufSize;
\r
659 byteBuf.SetCapacity(kBufSize);
\r
661 RINOK(ReadStream_FALSE(InStream, p, kBufSize));
\r
662 UInt32 fatSize = Header.FatSize;
\r
663 UInt32 *fat = &Fat[0];
\r
664 if (Header.NumFatBits == 16)
\r
665 for (UInt32 j = 0; j < fatSize; j++)
\r
666 fat[j] = Get16(p + j * 2);
\r
668 for (UInt32 j = 0; j < fatSize; j++)
\r
669 fat[j] = (Get16(p + j * 3 / 2) >> ((j & 1) << 2)) & 0xFFF;
\r
671 if (!numFreeClustersDefined)
\r
673 UInt32 numFreeClusters = 0;
\r
674 for (UInt32 i = 0; i < fatSize; i++)
\r
675 numFreeClusters += (UInt32)(fat[i] - 1) >> 31;
\r
676 NumFreeClusters = numFreeClusters;
\r
680 RINOK(OpenProgressFat());
\r
682 if ((Fat[0] & 0xFF) != Header.MediaType)
\r
685 return ReadDir(-1, Header.RootCluster, 0);
\r
690 public IInArchiveGetStream,
\r
691 public CMyUnknownImp,
\r
695 MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
\r
696 INTERFACE_IInArchive(;)
\r
697 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
\r
700 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
\r
704 const CItem &item = Items[index];
\r
705 CClusterInStream *streamSpec = new CClusterInStream;
\r
706 CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
\r
707 streamSpec->Stream = InStream;
\r
708 streamSpec->StartOffset = Header.DataSector << Header.SectorSizeLog;
\r
709 streamSpec->BlockSizeLog = Header.ClusterSizeLog;
\r
710 streamSpec->Size = item.Size;
\r
712 UInt32 numClusters = Header.GetNumClusters(item.Size);
\r
713 streamSpec->Vector.Reserve(numClusters);
\r
714 UInt32 cluster = item.Cluster;
\r
715 UInt32 size = item.Size;
\r
724 UInt32 clusterSize = Header.ClusterSize();
\r
725 for (;; size -= clusterSize)
\r
727 if (!Header.IsValidCluster(cluster))
\r
729 streamSpec->Vector.Add(cluster - 2);
\r
730 cluster = Fat[cluster];
\r
731 if (size <= clusterSize)
\r
734 if (!Header.IsEocAndUnused(cluster))
\r
737 RINOK(streamSpec->InitAndSeek());
\r
738 *stream = streamTemp.Detach();
\r
743 STATPROPSTG kProps[] =
\r
745 { NULL, kpidPath, VT_BSTR},
\r
746 { NULL, kpidIsDir, VT_BOOL},
\r
747 { NULL, kpidSize, VT_UI8},
\r
748 { NULL, kpidPackSize, VT_UI8},
\r
749 { NULL, kpidMTime, VT_FILETIME},
\r
750 { NULL, kpidCTime, VT_FILETIME},
\r
751 { NULL, kpidATime, VT_FILETIME},
\r
752 { NULL, kpidAttrib, VT_UI8},
\r
753 { NULL, kpidShortName, VT_BSTR}
\r
758 kpidNumFats = kpidUserDefined
\r
764 STATPROPSTG kArcProps[] =
\r
766 { NULL, kpidFileSystem, VT_BSTR},
\r
767 { NULL, kpidClusterSize, VT_UI4},
\r
768 { NULL, kpidPhySize, VT_UI8},
\r
769 { NULL, kpidFreeSpace, VT_UI8},
\r
770 { NULL, kpidHeadersSize, VT_UI8},
\r
771 { NULL, kpidMTime, VT_FILETIME},
\r
772 { NULL, kpidVolumeName, VT_BSTR},
\r
774 { L"FATs", kpidNumFats, VT_UI4},
\r
775 { NULL, kpidSectorSize, VT_UI4},
\r
776 { NULL, kpidId, VT_UI4},
\r
777 // { L"OEM Name", kpidOemName, VT_BSTR},
\r
778 // { L"Volume Name", kpidVolName, VT_BSTR},
\r
779 // { L"File System Type", kpidFileSysType, VT_BSTR}
\r
780 // { NULL, kpidSectorsPerTrack, VT_UI4},
\r
781 // { NULL, kpidNumHeads, VT_UI4},
\r
782 // { NULL, kpidHiddenSectors, VT_UI4}
\r
785 IMP_IInArchive_Props
\r
786 IMP_IInArchive_ArcProps_WITH_NAME
\r
788 static void FatTimeToProp(UInt32 dosTime, UInt32 ms10, NWindows::NCOM::CPropVariant &prop)
\r
790 FILETIME localFileTime, utc;
\r
791 if (NWindows::NTime::DosTimeToFileTime(dosTime, localFileTime))
\r
792 if (LocalFileTimeToFileTime(&localFileTime, &utc))
\r
794 UInt64 t64 = (((UInt64)utc.dwHighDateTime) << 32) + utc.dwLowDateTime;
\r
795 t64 += ms10 * 100000;
\r
796 utc.dwLowDateTime = (DWORD)t64;
\r
797 utc.dwHighDateTime = (DWORD)(t64 >> 32);
\r
803 static void StringToProp(const Byte *src, int size, NWindows::NCOM::CPropVariant &prop)
\r
806 memcpy(dest, src, size);
\r
808 prop = FatStringToUnicode(dest);
\r
811 #define STRING_TO_PROP(s, p) StringToProp(s, sizeof(s), prop)
\r
814 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
\r
817 NWindows::NCOM::CPropVariant prop;
\r
820 case kpidFileSystem:
\r
822 wchar_t s[32] = { L'F', L'A', L'T' };
\r
823 ConvertUInt32ToString(Header.NumFatBits, s + 3);
\r
827 case kpidClusterSize: prop = Header.ClusterSize(); break;
\r
828 case kpidPhySize: prop = Header.GetPhySize(); break;
\r
829 case kpidFreeSpace: prop = (UInt64)NumFreeClusters << Header.ClusterSizeLog; break;
\r
830 case kpidHeadersSize: prop = GetHeadersSize(); break;
\r
831 case kpidMTime: if (VolItemDefined) FatTimeToProp(VolItem.MTime, 0, prop); break;
\r
832 case kpidVolumeName: if (VolItemDefined) prop = VolItem.GetVolName(); break;
\r
833 case kpidNumFats: if (Header.NumFats != 2) prop = Header.NumFats; break;
\r
834 case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
\r
835 // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break;
\r
836 // case kpidNumHeads: prop = Header.NumHeads; break;
\r
837 // case kpidOemName: STRING_TO_PROP(Header.OemName, prop); break;
\r
838 case kpidId: if (Header.VolFieldsDefined) prop = Header.VolId; break;
\r
839 // case kpidVolName: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.VolName, prop); break;
\r
840 // case kpidFileSysType: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.FileSys, prop); break;
\r
841 // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break;
\r
843 prop.Detach(value);
\r
848 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
\r
851 NWindows::NCOM::CPropVariant prop;
\r
852 const CItem &item = Items[index];
\r
855 case kpidPath: prop = GetItemPath(index); break;
\r
856 case kpidShortName: prop = item.GetShortName(); break;
\r
857 case kpidIsDir: prop = item.IsDir(); break;
\r
858 case kpidMTime: FatTimeToProp(item.MTime, 0, prop); break;
\r
859 case kpidCTime: FatTimeToProp(item.CTime, item.CTime2, prop); break;
\r
860 case kpidATime: FatTimeToProp(((UInt32)item.ADate << 16), 0, prop); break;
\r
861 case kpidAttrib: prop = (UInt32)item.Attrib; break;
\r
862 case kpidSize: if (!item.IsDir()) prop = item.Size; break;
\r
863 case kpidPackSize: if (!item.IsDir()) prop = Header.GetFilePackSize(item.Size); break;
\r
865 prop.Detach(value);
\r
870 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
\r
874 OpenCallback = callback;
\r
879 res = CDatabase::Open();
\r
894 STDMETHODIMP CHandler::Close()
\r
900 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
\r
901 Int32 testMode, IArchiveExtractCallback *extractCallback)
\r
904 bool allFilesMode = (numItems == (UInt32)-1);
\r
906 numItems = Items.Size();
\r
910 UInt64 totalSize = 0;
\r
911 for (i = 0; i < numItems; i++)
\r
913 const CItem &item = Items[allFilesMode ? i : indices[i]];
\r
915 totalSize += item.Size;
\r
917 RINOK(extractCallback->SetTotal(totalSize));
\r
919 UInt64 totalPackSize;
\r
920 totalSize = totalPackSize = 0;
\r
922 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
\r
923 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
\r
925 CLocalProgress *lps = new CLocalProgress;
\r
926 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
927 lps->Init(extractCallback, false);
\r
929 CDummyOutStream *outStreamSpec = new CDummyOutStream;
\r
930 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
\r
932 for (i = 0; i < numItems; i++)
\r
934 lps->InSize = totalPackSize;
\r
935 lps->OutSize = totalSize;
\r
936 RINOK(lps->SetCur());
\r
937 CMyComPtr<ISequentialOutStream> realOutStream;
\r
938 Int32 askMode = testMode ?
\r
939 NExtract::NAskMode::kTest :
\r
940 NExtract::NAskMode::kExtract;
\r
941 Int32 index = allFilesMode ? i : indices[i];
\r
942 const CItem &item = Items[index];
\r
943 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
\r
947 RINOK(extractCallback->PrepareOperation(askMode));
\r
948 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
\r
952 totalPackSize += Header.GetFilePackSize(item.Size);
\r
953 totalSize += item.Size;
\r
955 if (!testMode && !realOutStream)
\r
957 RINOK(extractCallback->PrepareOperation(askMode));
\r
959 outStreamSpec->SetStream(realOutStream);
\r
960 realOutStream.Release();
\r
961 outStreamSpec->Init();
\r
963 int res = NExtract::NOperationResult::kDataError;
\r
964 CMyComPtr<ISequentialInStream> inStream;
\r
965 HRESULT hres = GetStream(index, &inStream);
\r
966 if (hres != S_FALSE)
\r
971 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
\r
972 if (copyCoderSpec->TotalSize == item.Size)
\r
973 res = NExtract::NOperationResult::kOK;
\r
976 outStreamSpec->ReleaseStream();
\r
977 RINOK(extractCallback->SetOperationResult(res));
\r
983 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
\r
985 *numItems = Items.Size();
\r
989 static IInArchive *CreateArc() { return new CHandler; }
\r
991 static CArcInfo g_ArcInfo =
\r
992 { L"FAT", L"fat img", 0, 0xDA, { 0x55, 0xAA }, 2, false, CreateArc, 0 };
\r