summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Archive/FatHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/FatHandler.cpp')
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/FatHandler.cpp996
1 files changed, 996 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/FatHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/FatHandler.cpp
new file mode 100644
index 000000000..1c374a444
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/FatHandler.cpp
@@ -0,0 +1,996 @@
+// FatHandler.cpp
+
+#include "StdAfx.h"
+
+// #include <stdio.h>
+
+#include "../../../C/CpuArch.h"
+
+#include "Common/Buffer.h"
+#include "Common/ComTry.h"
+#include "Common/IntToString.h"
+#include "Common/MyCom.h"
+#include "Common/StringConvert.h"
+
+#include "Windows/PropVariant.h"
+#include "Windows/Time.h"
+
+#include "../Common/LimitedStreams.h"
+#include "../Common/ProgressUtils.h"
+#include "../Common/RegisterArc.h"
+#include "../Common/StreamUtils.h"
+
+#include "../Compress/CopyCoder.h"
+
+#include "Common/DummyOutStream.h"
+
+#define Get16(p) GetUi16(p)
+#define Get32(p) GetUi32(p)
+
+#define PRF(x) /* x */
+
+namespace NArchive {
+namespace NFat {
+
+static const UInt32 kFatItemUsedByDirMask = (UInt32)1 << 31;
+
+struct CHeader
+{
+ UInt32 NumSectors;
+ UInt16 NumReservedSectors;
+ Byte NumFats;
+ UInt32 NumFatSectors;
+ UInt32 RootDirSector;
+ UInt32 NumRootDirSectors;
+ UInt32 DataSector;
+
+ UInt32 FatSize;
+ UInt32 BadCluster;
+
+ Byte NumFatBits;
+ Byte SectorSizeLog;
+ Byte SectorsPerClusterLog;
+ Byte ClusterSizeLog;
+
+ UInt16 SectorsPerTrack;
+ UInt16 NumHeads;
+ UInt32 NumHiddenSectors;
+
+ bool VolFieldsDefined;
+
+ UInt32 VolId;
+ // Byte VolName[11];
+ // Byte FileSys[8];
+
+ // Byte OemName[5];
+ Byte MediaType;
+
+ // 32-bit FAT
+ UInt16 Flags;
+ UInt16 FsInfoSector;
+ UInt32 RootCluster;
+
+ bool IsFat32() const { return NumFatBits == 32; }
+ UInt64 GetPhySize() const { return (UInt64)NumSectors << SectorSizeLog; }
+ UInt32 SectorSize() const { return (UInt32)1 << SectorSizeLog; }
+ UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; }
+ UInt32 ClusterToSector(UInt32 c) const { return DataSector + ((c - 2) << SectorsPerClusterLog); }
+ UInt32 IsEoc(UInt32 c) const { return c > BadCluster; }
+ UInt32 IsEocAndUnused(UInt32 c) const { return c > BadCluster && (c & kFatItemUsedByDirMask) == 0; }
+ UInt32 IsValidCluster(UInt32 c) const { return c >= 2 && c < FatSize; }
+ UInt32 SizeToSectors(UInt32 size) const { return (size + SectorSize() - 1) >> SectorSizeLog; }
+ UInt32 CalcFatSizeInSectors() const { return SizeToSectors((FatSize * (NumFatBits / 4) + 1) / 2); }
+
+ UInt32 GetFatSector() const
+ {
+ UInt32 index = (IsFat32() && (Flags & 0x80) != 0) ? (Flags & 0xF) : 0;
+ if (index > NumFats)
+ index = 0;
+ return NumReservedSectors + index * NumFatSectors;
+ }
+
+ UInt64 GetFilePackSize(UInt32 unpackSize) const
+ {
+ UInt64 mask = ClusterSize() - 1;
+ return (unpackSize + mask) & ~mask;
+ }
+
+ UInt32 GetNumClusters(UInt32 size) const
+ { return (UInt32)(((UInt64)size + ClusterSize() - 1) >> ClusterSizeLog); }
+
+ bool Parse(const Byte *p);
+};
+
+static int GetLog(UInt32 num)
+{
+ for (int i = 0; i < 31; i++)
+ if (((UInt32)1 << i) == num)
+ return i;
+ return -1;
+}
+
+bool CHeader::Parse(const Byte *p)
+{
+ if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA)
+ return false;
+
+ int codeOffset = 0;
+ switch (p[0])
+ {
+ case 0xE9: codeOffset = 3 + (Int16)Get16(p + 1); break;
+ case 0xEB: if (p[2] != 0x90) return false; codeOffset = 2 + (signed char)p[1]; break;
+ default: return false;
+ }
+ {
+ int s = GetLog(Get16(p + 11));
+ if (s < 9 || s > 12)
+ return false;
+ SectorSizeLog = (Byte)s;
+ s = GetLog(p[13]);
+ if (s < 0)
+ return false;
+ SectorsPerClusterLog = (Byte)s;
+ ClusterSizeLog = SectorSizeLog + SectorsPerClusterLog;
+ }
+
+ NumReservedSectors = Get16(p + 14);
+ if (NumReservedSectors == 0)
+ return false;
+
+ NumFats = p[16];
+ if (NumFats < 1 || NumFats > 4)
+ return false;
+
+ UInt16 numRootDirEntries = Get16(p + 17);
+ if (numRootDirEntries == 0)
+ {
+ if (codeOffset < 90)
+ return false;
+ NumFatBits = 32;
+ NumRootDirSectors = 0;
+ }
+ else
+ {
+ if (codeOffset < 62)
+ return false;
+ NumFatBits = 0;
+ UInt32 mask = (1 << (SectorSizeLog - 5)) - 1;
+ if ((numRootDirEntries & mask) != 0)
+ return false;
+ NumRootDirSectors = (numRootDirEntries + mask) >> (SectorSizeLog - 5);
+ }
+
+ NumSectors = Get16(p + 19);
+ if (NumSectors == 0)
+ NumSectors = Get32(p + 32);
+ else if (IsFat32())
+ return false;
+
+ MediaType = p[21];
+ NumFatSectors = Get16(p + 22);
+ SectorsPerTrack = Get16(p + 24);
+ NumHeads = Get16(p + 26);
+ NumHiddenSectors = Get32(p + 28);
+
+ // memcpy(OemName, p + 3, 5);
+
+ p += 36;
+ if (IsFat32())
+ {
+ if (NumFatSectors != 0)
+ return false;
+ NumFatSectors = Get32(p);
+ if (NumFatSectors >= (1 << 24))
+ return false;
+
+ Flags = Get16(p + 4);
+ if (Get16(p + 6) != 0)
+ return false;
+ RootCluster = Get32(p + 8);
+ FsInfoSector = Get16(p + 12);
+ for (int i = 16; i < 28; i++)
+ if (p[i] != 0)
+ return false;
+ p += 28;
+ }
+
+ // DriveNumber = p[0];
+ VolFieldsDefined = (p[2] == 0x29); // ExtendedBootSig
+ VolId = Get32(p + 3);
+ // memcpy(VolName, p + 7, 11);
+ // memcpy(FileSys, p + 18, 8);
+
+ if (NumFatSectors == 0)
+ return false;
+ RootDirSector = NumReservedSectors + NumFatSectors * NumFats;
+ DataSector = RootDirSector + NumRootDirSectors;
+ if (NumSectors < DataSector)
+ return false;
+ UInt32 numDataSectors = NumSectors - DataSector;
+ UInt32 numClusters = numDataSectors >> SectorsPerClusterLog;
+
+ BadCluster = 0x0FFFFFF7;
+ if (numClusters < 0xFFF5)
+ {
+ if (NumFatBits == 32)
+ return false;
+ NumFatBits = (numClusters < 0xFF5) ? 12: 16;
+ BadCluster &= ((1 << NumFatBits) - 1);
+ }
+ else if (NumFatBits != 32)
+ return false;
+
+ FatSize = numClusters + 2;
+ if (FatSize > BadCluster || CalcFatSizeInSectors() > NumFatSectors)
+ return false;
+ return true;
+}
+
+struct CItem
+{
+ UString UName;
+ char DosName[11];
+ Byte CTime2;
+ UInt32 CTime;
+ UInt32 MTime;
+ UInt16 ADate;
+ Byte Attrib;
+ Byte Flags;
+ UInt32 Size;
+ UInt32 Cluster;
+ Int32 Parent;
+
+ // NT uses Flags to store Low Case status
+ bool NameIsLow() const { return (Flags & 0x8) != 0; }
+ bool ExtIsLow() const { return (Flags & 0x10) != 0; }
+ bool IsDir() const { return (Attrib & 0x10) != 0; }
+ UString GetShortName() const;
+ UString GetName() const;
+ UString GetVolName() const;
+};
+
+static int CopyAndTrim(char *dest, const char *src, int size, bool toLower)
+{
+ int i;
+ memcpy(dest, src, size);
+ if (toLower)
+ for (i = 0; i < size; i++)
+ {
+ char c = dest[i];
+ if (c >= 'A' && c <= 'Z')
+ dest[i] = c + 0x20;
+ }
+ for (i = size - 1; i >= 0 && dest[i] == ' '; i--);
+ return i + 1;
+}
+
+static UString FatStringToUnicode(const char *s)
+{
+ return MultiByteToUnicodeString(s, CP_OEMCP);
+}
+
+UString CItem::GetShortName() const
+{
+ char s[16];
+ int i = CopyAndTrim(s, DosName, 8, NameIsLow());
+ s[i++] = '.';
+ int j = CopyAndTrim(s + i, DosName + 8, 3, ExtIsLow());
+ if (j == 0)
+ j--;
+ s[i + j] = 0;
+ return FatStringToUnicode(s);
+}
+
+UString CItem::GetName() const
+{
+ if (!UName.IsEmpty())
+ return UName;
+ return GetShortName();
+}
+
+UString CItem::GetVolName() const
+{
+ if (!UName.IsEmpty())
+ return UName;
+ char s[12];
+ int i = CopyAndTrim(s, DosName, 11, false);
+ s[i] = 0;
+ return FatStringToUnicode(s);
+}
+
+struct CDatabase
+{
+ CHeader Header;
+ CObjectVector<CItem> Items;
+ UInt32 *Fat;
+ CMyComPtr<IInStream> InStream;
+ IArchiveOpenCallback *OpenCallback;
+
+ UInt32 NumFreeClusters;
+ bool VolItemDefined;
+ CItem VolItem;
+ UInt32 NumDirClusters;
+ CByteBuffer ByteBuf;
+ UInt64 NumCurUsedBytes;
+
+ CDatabase(): Fat(0) {}
+ ~CDatabase() { ClearAndClose(); }
+
+ void Clear();
+ void ClearAndClose();
+ HRESULT OpenProgressFat(bool changeTotal = true);
+ HRESULT OpenProgress();
+
+ UString GetItemPath(Int32 index) const;
+ HRESULT Open();
+ HRESULT ReadDir(Int32 parent, UInt32 cluster, int level);
+
+ UInt64 GetHeadersSize() const
+ {
+ return (UInt64)(Header.DataSector + (NumDirClusters << Header.SectorsPerClusterLog)) << Header.SectorSizeLog;
+ }
+ HRESULT SeekToSector(UInt32 sector);
+ HRESULT SeekToCluster(UInt32 cluster) { return SeekToSector(Header.ClusterToSector(cluster)); }
+};
+
+HRESULT CDatabase::SeekToSector(UInt32 sector)
+{
+ return InStream->Seek((UInt64)sector << Header.SectorSizeLog, STREAM_SEEK_SET, NULL);
+}
+
+void CDatabase::Clear()
+{
+ VolItemDefined = false;
+ NumDirClusters = 0;
+ NumCurUsedBytes = 0;
+
+ Items.Clear();
+ delete []Fat;
+ Fat = 0;
+}
+
+void CDatabase::ClearAndClose()
+{
+ Clear();
+ InStream.Release();
+}
+
+HRESULT CDatabase::OpenProgressFat(bool changeTotal)
+{
+ if (!OpenCallback)
+ return S_OK;
+ if (changeTotal)
+ {
+ UInt64 numTotalBytes = (Header.CalcFatSizeInSectors() << Header.SectorSizeLog) +
+ ((UInt64)(Header.FatSize - NumFreeClusters) << Header.ClusterSizeLog);
+ RINOK(OpenCallback->SetTotal(NULL, &numTotalBytes));
+ }
+ return OpenCallback->SetCompleted(NULL, &NumCurUsedBytes);
+}
+
+HRESULT CDatabase::OpenProgress()
+{
+ if (!OpenCallback)
+ return S_OK;
+ UInt64 numItems = Items.Size();
+ return OpenCallback->SetCompleted(&numItems, &NumCurUsedBytes);
+}
+
+UString CDatabase::GetItemPath(Int32 index) const
+{
+ const CItem *item = &Items[index];
+ UString name = item->GetName();
+ for (;;)
+ {
+ index = item->Parent;
+ if (index < 0)
+ return name;
+ item = &Items[index];
+ name = item->GetName() + WCHAR_PATH_SEPARATOR + name;
+ }
+}
+
+static wchar_t *AddSubStringToName(wchar_t *dest, const Byte *p, int numChars)
+{
+ for (int i = 0; i < numChars; i++)
+ {
+ wchar_t c = Get16(p + i * 2);
+ if (c != 0 && c != 0xFFFF)
+ *dest++ = c;
+ }
+ *dest = 0;
+ return dest;
+}
+
+HRESULT CDatabase::ReadDir(Int32 parent, UInt32 cluster, int level)
+{
+ int startIndex = Items.Size();
+ if (startIndex >= (1 << 30) || level > 256)
+ return S_FALSE;
+
+ UInt32 sectorIndex = 0;
+ UInt32 blockSize = Header.ClusterSize();
+ bool clusterMode = (Header.IsFat32() || parent >= 0);
+ if (!clusterMode)
+ {
+ blockSize = Header.SectorSize();
+ RINOK(SeekToSector(Header.RootDirSector));
+ }
+
+ ByteBuf.SetCapacity(blockSize);
+ UString curName;
+ int checkSum = -1;
+ int numLongRecords = -1;
+ for (UInt32 pos = blockSize;; pos += 32)
+ {
+ if (pos == blockSize)
+ {
+ pos = 0;
+
+ if ((NumDirClusters & 0xFF) == 0)
+ {
+ RINOK(OpenProgress());
+ }
+
+ if (clusterMode)
+ {
+ if (Header.IsEoc(cluster))
+ break;
+ if (!Header.IsValidCluster(cluster))
+ return S_FALSE;
+ PRF(printf("\nCluster = %4X", cluster));
+ RINOK(SeekToCluster(cluster));
+ UInt32 newCluster = Fat[cluster];
+ if ((newCluster & kFatItemUsedByDirMask) != 0)
+ return S_FALSE;
+ Fat[cluster] |= kFatItemUsedByDirMask;
+ cluster = newCluster;
+ NumDirClusters++;
+ NumCurUsedBytes += Header.ClusterSize();
+ }
+ else if (sectorIndex++ >= Header.NumRootDirSectors)
+ break;
+
+ RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize));
+ }
+ const Byte *p = ByteBuf + pos;
+ if (p[0] == 0)
+ {
+ /*
+ // FreeDOS formats FAT partition with cluster chain longer than required.
+ if (clusterMode && !Header.IsEoc(cluster))
+ return S_FALSE;
+ */
+ break;
+ }
+ if (p[0] == 0xE5)
+ {
+ if (numLongRecords > 0)
+ return S_FALSE;
+ continue;
+ }
+
+ Byte attrib = p[11];
+ if ((attrib & 0x3F) == 0xF)
+ {
+ if (p[0] > 0x7F || Get16(p + 26) != 0)
+ return S_FALSE;
+ int longIndex = p[0] & 0x3F;
+ if (longIndex == 0)
+ return S_FALSE;
+ bool isLast = (p[0] & 0x40) != 0;
+ if (numLongRecords < 0)
+ {
+ if (!isLast)
+ return S_FALSE;
+ numLongRecords = longIndex;
+ }
+ else if (isLast || numLongRecords != longIndex)
+ return S_FALSE;
+
+ numLongRecords--;
+
+ if (p[12] == 0)
+ {
+ wchar_t nameBuf[14];
+ wchar_t *dest;
+
+ dest = AddSubStringToName(nameBuf, p + 1, 5);
+ dest = AddSubStringToName(dest, p + 14, 6);
+ AddSubStringToName(dest, p + 28, 2);
+ curName = nameBuf + curName;
+ if (isLast)
+ checkSum = p[13];
+ if (checkSum != p[13])
+ return S_FALSE;
+ }
+ }
+ else
+ {
+ if (numLongRecords > 0)
+ return S_FALSE;
+ CItem item;
+ memcpy(item.DosName, p, 11);
+
+ if (checkSum >= 0)
+ {
+ Byte sum = 0;
+ for (int i = 0; i < 11; i++)
+ sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + (Byte)item.DosName[i];
+ if (sum == checkSum)
+ item.UName = curName;
+ }
+
+ if (item.DosName[0] == 5)
+ item.DosName[0] = (char)(Byte)0xE5;
+ item.Attrib = attrib;
+ item.Flags = p[12];
+ item.Size = Get32(p + 28);
+ item.Cluster = Get16(p + 26);
+ if (Header.NumFatBits > 16)
+ item.Cluster |= ((UInt32)Get16(p + 20) << 16);
+ else
+ {
+ // OS/2 and WinNT probably can store EA (extended atributes) in that field.
+ }
+
+ item.CTime = Get32(p + 14);
+ item.CTime2 = p[13];
+ item.ADate = Get16(p + 18);
+ item.MTime = Get32(p + 22);
+ item.Parent = parent;
+
+ if (attrib == 8)
+ {
+ VolItem = item;
+ VolItemDefined = true;
+ }
+ else
+ if (memcmp(item.DosName, ". ", 11) != 0 &&
+ memcmp(item.DosName, ".. ", 11) != 0)
+ {
+ if (!item.IsDir())
+ NumCurUsedBytes += Header.GetFilePackSize(item.Size);
+ Items.Add(item);
+ PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1)));
+ }
+ numLongRecords = -1;
+ curName.Empty();
+ checkSum = -1;
+ }
+ }
+
+ int finishIndex = Items.Size();
+ for (int i = startIndex; i < finishIndex; i++)
+ {
+ const CItem &item = Items[i];
+ if (item.IsDir())
+ {
+ PRF(printf("\n%S", GetItemPath(i)));
+ RINOK(CDatabase::ReadDir(i, item.Cluster, level + 1));
+ }
+ }
+ return S_OK;
+}
+
+HRESULT CDatabase::Open()
+{
+ Clear();
+ bool numFreeClustersDefined = false;
+ {
+ static const UInt32 kHeaderSize = 512;
+ Byte buf[kHeaderSize];
+ RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize));
+ if (!Header.Parse(buf))
+ return S_FALSE;
+ UInt64 fileSize;
+ RINOK(InStream->Seek(0, STREAM_SEEK_END, &fileSize));
+
+ /* we comment that check to support truncated images */
+ /*
+ if (fileSize < Header.GetPhySize())
+ return S_FALSE;
+ */
+
+ if (Header.IsFat32())
+ {
+ SeekToSector(Header.FsInfoSector);
+ RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize));
+ if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA)
+ return S_FALSE;
+ if (Get32(buf) == 0x41615252 && Get32(buf + 484) == 0x61417272)
+ {
+ NumFreeClusters = Get32(buf + 488);
+ numFreeClustersDefined = (NumFreeClusters <= Header.FatSize);
+ }
+ }
+ }
+
+ // numFreeClustersDefined = false; // to recalculate NumFreeClusters
+ if (!numFreeClustersDefined)
+ NumFreeClusters = 0;
+
+ CByteBuffer byteBuf;
+ Fat = new UInt32[Header.FatSize];
+
+ RINOK(OpenProgressFat());
+ RINOK(SeekToSector(Header.GetFatSector()));
+ if (Header.NumFatBits == 32)
+ {
+ const UInt32 kBufSize = (1 << 15);
+ byteBuf.SetCapacity(kBufSize);
+ for (UInt32 i = 0; i < Header.FatSize;)
+ {
+ UInt32 size = Header.FatSize - i;
+ const UInt32 kBufSize32 = kBufSize / 4;
+ if (size > kBufSize32)
+ size = kBufSize32;
+ UInt32 readSize = Header.SizeToSectors(size * 4) << Header.SectorSizeLog;
+ RINOK(ReadStream_FALSE(InStream, byteBuf, readSize));
+ NumCurUsedBytes += readSize;
+
+ const UInt32 *src = (const UInt32 *)(const Byte *)byteBuf;
+ UInt32 *dest = Fat + i;
+ if (numFreeClustersDefined)
+ for (UInt32 j = 0; j < size; j++)
+ dest[j] = Get32(src + j) & 0x0FFFFFFF;
+ else
+ {
+ UInt32 numFreeClusters = 0;
+ for (UInt32 j = 0; j < size; j++)
+ {
+ UInt32 v = Get32(src + j) & 0x0FFFFFFF;
+ numFreeClusters += (UInt32)(v - 1) >> 31;
+ dest[j] = v;
+ }
+ NumFreeClusters += numFreeClusters;
+ }
+ i += size;
+ if ((i & 0xFFFFF) == 0)
+ {
+ RINOK(OpenProgressFat(!numFreeClustersDefined));
+ }
+ }
+ }
+ else
+ {
+ const UInt32 kBufSize = (UInt32)Header.CalcFatSizeInSectors() << Header.SectorSizeLog;
+ NumCurUsedBytes += kBufSize;
+ byteBuf.SetCapacity(kBufSize);
+ Byte *p = byteBuf;
+ RINOK(ReadStream_FALSE(InStream, p, kBufSize));
+ UInt32 fatSize = Header.FatSize;
+ UInt32 *fat = &Fat[0];
+ if (Header.NumFatBits == 16)
+ for (UInt32 j = 0; j < fatSize; j++)
+ fat[j] = Get16(p + j * 2);
+ else
+ for (UInt32 j = 0; j < fatSize; j++)
+ fat[j] = (Get16(p + j * 3 / 2) >> ((j & 1) << 2)) & 0xFFF;
+
+ if (!numFreeClustersDefined)
+ {
+ UInt32 numFreeClusters = 0;
+ for (UInt32 i = 0; i < fatSize; i++)
+ numFreeClusters += (UInt32)(fat[i] - 1) >> 31;
+ NumFreeClusters = numFreeClusters;
+ }
+ }
+
+ RINOK(OpenProgressFat());
+
+ if ((Fat[0] & 0xFF) != Header.MediaType)
+ return S_FALSE;
+
+ return ReadDir(-1, Header.RootCluster, 0);
+}
+
+class CHandler:
+ public IInArchive,
+ public IInArchiveGetStream,
+ public CMyUnknownImp,
+ CDatabase
+{
+public:
+ MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
+ INTERFACE_IInArchive(;)
+ STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
+};
+
+STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
+{
+ COM_TRY_BEGIN
+ *stream = 0;
+ const CItem &item = Items[index];
+ CClusterInStream *streamSpec = new CClusterInStream;
+ CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
+ streamSpec->Stream = InStream;
+ streamSpec->StartOffset = Header.DataSector << Header.SectorSizeLog;
+ streamSpec->BlockSizeLog = Header.ClusterSizeLog;
+ streamSpec->Size = item.Size;
+
+ UInt32 numClusters = Header.GetNumClusters(item.Size);
+ streamSpec->Vector.Reserve(numClusters);
+ UInt32 cluster = item.Cluster;
+ UInt32 size = item.Size;
+
+ if (size == 0)
+ {
+ if (cluster != 0)
+ return S_FALSE;
+ }
+ else
+ {
+ UInt32 clusterSize = Header.ClusterSize();
+ for (;; size -= clusterSize)
+ {
+ if (!Header.IsValidCluster(cluster))
+ return S_FALSE;
+ streamSpec->Vector.Add(cluster - 2);
+ cluster = Fat[cluster];
+ if (size <= clusterSize)
+ break;
+ }
+ if (!Header.IsEocAndUnused(cluster))
+ return S_FALSE;
+ }
+ RINOK(streamSpec->InitAndSeek());
+ *stream = streamTemp.Detach();
+ return S_OK;
+ COM_TRY_END
+}
+
+STATPROPSTG kProps[] =
+{
+ { NULL, kpidPath, VT_BSTR},
+ { NULL, kpidIsDir, VT_BOOL},
+ { NULL, kpidSize, VT_UI8},
+ { NULL, kpidPackSize, VT_UI8},
+ { NULL, kpidMTime, VT_FILETIME},
+ { NULL, kpidCTime, VT_FILETIME},
+ { NULL, kpidATime, VT_FILETIME},
+ { NULL, kpidAttrib, VT_UI8},
+ { NULL, kpidShortName, VT_BSTR}
+};
+
+enum
+{
+ kpidNumFats = kpidUserDefined
+ // kpidOemName,
+ // kpidVolName,
+ // kpidFileSysType
+};
+
+STATPROPSTG kArcProps[] =
+{
+ { NULL, kpidFileSystem, VT_BSTR},
+ { NULL, kpidClusterSize, VT_UI4},
+ { NULL, kpidPhySize, VT_UI8},
+ { NULL, kpidFreeSpace, VT_UI8},
+ { NULL, kpidHeadersSize, VT_UI8},
+ { NULL, kpidMTime, VT_FILETIME},
+ { NULL, kpidVolumeName, VT_BSTR},
+
+ { L"FATs", kpidNumFats, VT_UI4},
+ { NULL, kpidSectorSize, VT_UI4},
+ { NULL, kpidId, VT_UI4},
+ // { L"OEM Name", kpidOemName, VT_BSTR},
+ // { L"Volume Name", kpidVolName, VT_BSTR},
+ // { L"File System Type", kpidFileSysType, VT_BSTR}
+ // { NULL, kpidSectorsPerTrack, VT_UI4},
+ // { NULL, kpidNumHeads, VT_UI4},
+ // { NULL, kpidHiddenSectors, VT_UI4}
+};
+
+IMP_IInArchive_Props
+IMP_IInArchive_ArcProps_WITH_NAME
+
+static void FatTimeToProp(UInt32 dosTime, UInt32 ms10, NWindows::NCOM::CPropVariant &prop)
+{
+ FILETIME localFileTime, utc;
+ if (NWindows::NTime::DosTimeToFileTime(dosTime, localFileTime))
+ if (LocalFileTimeToFileTime(&localFileTime, &utc))
+ {
+ UInt64 t64 = (((UInt64)utc.dwHighDateTime) << 32) + utc.dwLowDateTime;
+ t64 += ms10 * 100000;
+ utc.dwLowDateTime = (DWORD)t64;
+ utc.dwHighDateTime = (DWORD)(t64 >> 32);
+ prop = utc;
+ }
+}
+
+/*
+static void StringToProp(const Byte *src, int size, NWindows::NCOM::CPropVariant &prop)
+{
+ char dest[32];
+ memcpy(dest, src, size);
+ dest[size] = 0;
+ prop = FatStringToUnicode(dest);
+}
+
+#define STRING_TO_PROP(s, p) StringToProp(s, sizeof(s), prop)
+*/
+
+STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
+{
+ COM_TRY_BEGIN
+ NWindows::NCOM::CPropVariant prop;
+ switch(propID)
+ {
+ case kpidFileSystem:
+ {
+ wchar_t s[32] = { L'F', L'A', L'T' };
+ ConvertUInt32ToString(Header.NumFatBits, s + 3);
+ prop = s;
+ break;
+ }
+ case kpidClusterSize: prop = Header.ClusterSize(); break;
+ case kpidPhySize: prop = Header.GetPhySize(); break;
+ case kpidFreeSpace: prop = (UInt64)NumFreeClusters << Header.ClusterSizeLog; break;
+ case kpidHeadersSize: prop = GetHeadersSize(); break;
+ case kpidMTime: if (VolItemDefined) FatTimeToProp(VolItem.MTime, 0, prop); break;
+ case kpidVolumeName: if (VolItemDefined) prop = VolItem.GetVolName(); break;
+ case kpidNumFats: if (Header.NumFats != 2) prop = Header.NumFats; break;
+ case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
+ // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break;
+ // case kpidNumHeads: prop = Header.NumHeads; break;
+ // case kpidOemName: STRING_TO_PROP(Header.OemName, prop); break;
+ case kpidId: if (Header.VolFieldsDefined) prop = Header.VolId; break;
+ // case kpidVolName: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.VolName, prop); break;
+ // case kpidFileSysType: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.FileSys, prop); break;
+ // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break;
+ }
+ prop.Detach(value);
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+ COM_TRY_BEGIN
+ NWindows::NCOM::CPropVariant prop;
+ const CItem &item = Items[index];
+ switch(propID)
+ {
+ case kpidPath: prop = GetItemPath(index); break;
+ case kpidShortName: prop = item.GetShortName(); break;
+ case kpidIsDir: prop = item.IsDir(); break;
+ case kpidMTime: FatTimeToProp(item.MTime, 0, prop); break;
+ case kpidCTime: FatTimeToProp(item.CTime, item.CTime2, prop); break;
+ case kpidATime: FatTimeToProp(((UInt32)item.ADate << 16), 0, prop); break;
+ case kpidAttrib: prop = (UInt32)item.Attrib; break;
+ case kpidSize: if (!item.IsDir()) prop = item.Size; break;
+ case kpidPackSize: if (!item.IsDir()) prop = Header.GetFilePackSize(item.Size); break;
+ }
+ prop.Detach(value);
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
+{
+ COM_TRY_BEGIN
+ {
+ OpenCallback = callback;
+ InStream = stream;
+ HRESULT res;
+ try
+ {
+ res = CDatabase::Open();
+ if (res == S_OK)
+ return S_OK;
+ }
+ catch(...)
+ {
+ Close();
+ throw;
+ }
+ Close();
+ return res;
+ }
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Close()
+{
+ ClearAndClose();
+ return S_OK;
+}
+
+STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
+ Int32 testMode, IArchiveExtractCallback *extractCallback)
+{
+ COM_TRY_BEGIN
+ bool allFilesMode = (numItems == (UInt32)-1);
+ if (allFilesMode)
+ numItems = Items.Size();
+ if (numItems == 0)
+ return S_OK;
+ UInt32 i;
+ UInt64 totalSize = 0;
+ for (i = 0; i < numItems; i++)
+ {
+ const CItem &item = Items[allFilesMode ? i : indices[i]];
+ if (!item.IsDir())
+ totalSize += item.Size;
+ }
+ RINOK(extractCallback->SetTotal(totalSize));
+
+ UInt64 totalPackSize;
+ totalSize = totalPackSize = 0;
+
+ NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
+ CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
+
+ CLocalProgress *lps = new CLocalProgress;
+ CMyComPtr<ICompressProgressInfo> progress = lps;
+ lps->Init(extractCallback, false);
+
+ CDummyOutStream *outStreamSpec = new CDummyOutStream;
+ CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
+
+ for (i = 0; i < numItems; i++)
+ {
+ lps->InSize = totalPackSize;
+ lps->OutSize = totalSize;
+ RINOK(lps->SetCur());
+ CMyComPtr<ISequentialOutStream> realOutStream;
+ Int32 askMode = testMode ?
+ NExtract::NAskMode::kTest :
+ NExtract::NAskMode::kExtract;
+ Int32 index = allFilesMode ? i : indices[i];
+ const CItem &item = Items[index];
+ RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
+
+ if (item.IsDir())
+ {
+ RINOK(extractCallback->PrepareOperation(askMode));
+ RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
+ continue;
+ }
+
+ totalPackSize += Header.GetFilePackSize(item.Size);
+ totalSize += item.Size;
+
+ if (!testMode && !realOutStream)
+ continue;
+ RINOK(extractCallback->PrepareOperation(askMode));
+
+ outStreamSpec->SetStream(realOutStream);
+ realOutStream.Release();
+ outStreamSpec->Init();
+
+ int res = NExtract::NOperationResult::kDataError;
+ CMyComPtr<ISequentialInStream> inStream;
+ HRESULT hres = GetStream(index, &inStream);
+ if (hres != S_FALSE)
+ {
+ RINOK(hres);
+ if (inStream)
+ {
+ RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
+ if (copyCoderSpec->TotalSize == item.Size)
+ res = NExtract::NOperationResult::kOK;
+ }
+ }
+ outStreamSpec->ReleaseStream();
+ RINOK(extractCallback->SetOperationResult(res));
+ }
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+ *numItems = Items.Size();
+ return S_OK;
+}
+
+static IInArchive *CreateArc() { return new CHandler; }
+
+static CArcInfo g_ArcInfo =
+ { L"FAT", L"fat img", 0, 0xDA, { 0x55, 0xAA }, 2, false, CreateArc, 0 };
+
+REGISTER_ARC(Fat)
+
+}}