mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 15:14:59 -06:00
9.04 beta
This commit is contained in:
committed by
Kornel Lesiński
parent
8874e4fbc9
commit
829409452d
983
CPP/7zip/Archive/FatHandler.cpp
Executable file
983
CPP/7zip/Archive/FatHandler.cpp
Executable file
@@ -0,0 +1,983 @@
|
||||
// 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)
|
||||
{
|
||||
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) | ((UInt32)Get16(p + 20) << 16);
|
||||
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));
|
||||
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 _aTestMode, IArchiveExtractCallback *extractCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
bool testMode = (_aTestMode != 0);
|
||||
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 ?
|
||||
NArchive::NExtract::NAskMode::kTest :
|
||||
NArchive::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(NArchive::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 = NArchive::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 = NArchive::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)
|
||||
|
||||
}}
|
||||
Reference in New Issue
Block a user