diff options
author | kh1 <karsten.heimrich@nokia.com> | 2012-03-15 14:53:47 +0100 |
---|---|---|
committer | Karsten Heimrich <karsten.heimrich@nokia.com> | 2012-03-19 16:14:04 +0100 |
commit | be3b47d0d504a3409ce66bd77bb8c0acff87c4f5 (patch) | |
tree | 09dfb02d484a4f395991972b828da71400fb761a /src/libs/7zip/win/CPP/7zip/Archive/Chm/ChmIn.cpp | |
parent | 9fd62353cf7f973d78cd2093328ac15b5c4980b6 (diff) |
Reorganize the tree, have better ifw.pri. Shadow build support.
Change-Id: I01fb12537f863ed0744979973c7e4153889cc5cb
Reviewed-by: Tim Jenssen <tim.jenssen@nokia.com>
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/Chm/ChmIn.cpp')
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Chm/ChmIn.cpp | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Chm/ChmIn.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Chm/ChmIn.cpp new file mode 100644 index 000000000..d52b9ba6c --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Chm/ChmIn.cpp @@ -0,0 +1,937 @@ +// Archive/ChmIn.cpp + +#include "StdAfx.h" + +#include "Common/IntToString.h" +#include "Common/UTFConvert.h" + +#include "../../Common/LimitedStreams.h" + +#include "ChmIn.h" + +namespace NArchive { +namespace NChm { + +// define CHM_LOW, if you want to see low level items +// #define CHM_LOW + +static const GUID kChmLzxGuid = { 0x7FC28940, 0x9D31, 0x11D0, { 0x9B, 0x27, 0x00, 0xA0, 0xC9, 0x1E, 0x9C, 0x7C } }; +static const GUID kHelp2LzxGuid = { 0x0A9007C6, 0x4076, 0x11D3, { 0x87, 0x89, 0x00, 0x00, 0xF8, 0x10, 0x57, 0x54 } }; +static const GUID kDesGuid = { 0x67F6E4A2, 0x60BF, 0x11D3, { 0x85, 0x40, 0x00, 0xC0, 0x4F, 0x58, 0xC3, 0xCF } }; + +static bool AreGuidsEqual(REFGUID g1, REFGUID g2) +{ + if (g1.Data1 != g2.Data1 || + g1.Data2 != g2.Data2 || + g1.Data3 != g2.Data3) + return false; + for (int i = 0; i < 8; i++) + if (g1.Data4[i] != g2.Data4[i]) + return false; + return true; +} + +static char GetHex(Byte value) +{ + return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); +} + +static void PrintByte(Byte b, AString &s) +{ + s += GetHex(b >> 4); + s += GetHex(b & 0xF); +} + +static void PrintUInt16(UInt16 v, AString &s) +{ + PrintByte((Byte)(v >> 8), s); + PrintByte((Byte)v, s); +} + +static void PrintUInt32(UInt32 v, AString &s) +{ + PrintUInt16((UInt16)(v >> 16), s); + PrintUInt16((UInt16)v, s); +} + +AString CMethodInfo::GetGuidString() const +{ + AString s; + s += '{'; + PrintUInt32(Guid.Data1, s); + s += '-'; + PrintUInt16(Guid.Data2, s); + s += '-'; + PrintUInt16(Guid.Data3, s); + s += '-'; + PrintByte(Guid.Data4[0], s); + PrintByte(Guid.Data4[1], s); + s += '-'; + for (int i = 2; i < 8; i++) + PrintByte(Guid.Data4[i], s); + s += '}'; + return s; +} + +bool CMethodInfo::IsLzx() const +{ + if (AreGuidsEqual(Guid, kChmLzxGuid)) + return true; + return AreGuidsEqual(Guid, kHelp2LzxGuid); +} + +bool CMethodInfo::IsDes() const +{ + return AreGuidsEqual(Guid, kDesGuid); +} + +UString CMethodInfo::GetName() const +{ + UString s; + if (IsLzx()) + { + s = L"LZX:"; + wchar_t temp[16]; + ConvertUInt32ToString(LzxInfo.GetNumDictBits(), temp); + s += temp; + } + else + { + AString s2; + if (IsDes()) + s2 = "DES"; + else + { + s2 = GetGuidString(); + if (ControlData.GetCapacity() > 0) + { + s2 += ':'; + for (size_t i = 0; i < ControlData.GetCapacity(); i++) + PrintByte(ControlData[i], s2); + } + } + ConvertUTF8ToUnicode(s2, s); + } + return s; +} + +bool CSectionInfo::IsLzx() const +{ + if (Methods.Size() != 1) + return false; + return Methods[0].IsLzx(); +} + +UString CSectionInfo::GetMethodName() const +{ + UString s; + if (!IsLzx()) + { + UString temp; + if (ConvertUTF8ToUnicode(Name, temp)) + s += temp; + s += L": "; + } + for (int i = 0; i < Methods.Size(); i++) + { + if (i != 0) + s += L' '; + s += Methods[i].GetName(); + } + return s; +} + +Byte CInArchive::ReadByte() +{ + Byte b; + if (!_inBuffer.ReadByte(b)) + throw 1; + return b; +} + +void CInArchive::Skip(size_t size) +{ + while (size-- != 0) + ReadByte(); +} + +void CInArchive::ReadBytes(Byte *data, UInt32 size) +{ + for (UInt32 i = 0; i < size; i++) + data[i] = ReadByte(); +} + +UInt16 CInArchive::ReadUInt16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + value |= ((UInt16)(ReadByte()) << (8 * i)); + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + value |= ((UInt32)(ReadByte()) << (8 * i)); + return value; +} + +UInt64 CInArchive::ReadUInt64() +{ + UInt64 value = 0; + for (int i = 0; i < 8; i++) + value |= ((UInt64)(ReadByte()) << (8 * i)); + return value; +} + +UInt64 CInArchive::ReadEncInt() +{ + UInt64 val = 0;; + for (int i = 0; i < 10; i++) + { + Byte b = ReadByte(); + val |= (b & 0x7F); + if (b < 0x80) + return val; + val <<= 7; + } + throw 1; +} + +void CInArchive::ReadGUID(GUID &g) +{ + g.Data1 = ReadUInt32(); + g.Data2 = ReadUInt16(); + g.Data3 = ReadUInt16(); + ReadBytes(g.Data4, 8); +} + +void CInArchive::ReadString(int size, AString &s) +{ + s.Empty(); + while(size-- != 0) + { + char c = (char)ReadByte(); + if (c == 0) + { + Skip(size); + return; + } + s += c; + } +} + +void CInArchive::ReadUString(int size, UString &s) +{ + s.Empty(); + while(size-- != 0) + { + wchar_t c = ReadUInt16(); + if (c == 0) + { + Skip(2 * size); + return; + } + s += c; + } +} + +HRESULT CInArchive::ReadChunk(IInStream *inStream, UInt64 pos, UInt64 size) +{ + RINOK(inStream->Seek(pos, STREAM_SEEK_SET, NULL)); + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> limitedStream(streamSpec); + streamSpec->SetStream(inStream); + streamSpec->Init(size); + _inBuffer.SetStream(limitedStream); + _inBuffer.Init(); + return S_OK; +} + +HRESULT CInArchive::ReadDirEntry(CDatabase &database) +{ + CItem item; + UInt64 nameLength = ReadEncInt(); + if (nameLength == 0 || nameLength >= 0x10000000) + return S_FALSE; + ReadString((int)nameLength, item.Name); + item.Section = ReadEncInt(); + item.Offset = ReadEncInt(); + item.Size = ReadEncInt(); + database.Items.Add(item); + return S_OK; +} + +HRESULT CInArchive::OpenChm(IInStream *inStream, CDatabase &database) +{ + UInt32 headerSize = ReadUInt32(); + if (headerSize != 0x60) + return S_FALSE; + UInt32 unknown1 = ReadUInt32(); + if (unknown1 != 0 && unknown1 != 1) // it's 0 in one .sll file + return S_FALSE; + /* UInt32 timeStamp = */ ReadUInt32(); + // Considered as a big-endian DWORD, it appears to contain seconds (MSB) and + // fractional seconds (second byte). + // The third and fourth bytes may contain even more fractional bits. + // The 4 least significant bits in the last byte are constant. + /* UInt32 lang = */ ReadUInt32(); + GUID g; + ReadGUID(g); // {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC} + ReadGUID(g); // {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC} + const int kNumSections = 2; + UInt64 sectionOffsets[kNumSections]; + UInt64 sectionSizes[kNumSections]; + int i; + for (i = 0; i < kNumSections; i++) + { + sectionOffsets[i] = ReadUInt64(); + sectionSizes[i] = ReadUInt64(); + } + // if (chmVersion == 3) + database.ContentOffset = ReadUInt64(); + /* + else + database.ContentOffset = _startPosition + 0x58 + */ + + /* + // Section 0 + ReadChunk(inStream, sectionOffsets[0], sectionSizes[0]); + if (sectionSizes[0] != 0x18) + return S_FALSE; + ReadUInt32(); // unknown: 01FE + ReadUInt32(); // unknown: 0 + UInt64 fileSize = ReadUInt64(); + ReadUInt32(); // unknown: 0 + ReadUInt32(); // unknown: 0 + */ + + // Section 1: The Directory Listing + ReadChunk(inStream, sectionOffsets[1], sectionSizes[1]); + if (ReadUInt32() != NHeader::kItspSignature) + return S_FALSE; + if (ReadUInt32() != 1) // version + return S_FALSE; + /* UInt32 dirHeaderSize = */ ReadUInt32(); + ReadUInt32(); // 0x0A (unknown) + UInt32 dirChunkSize = ReadUInt32(); // $1000 + if (dirChunkSize < 32) + return S_FALSE; + /* UInt32 density = */ ReadUInt32(); // "Density" of quickref section, usually 2. + /* UInt32 depth = */ ReadUInt32(); // Depth of the index tree: 1 there is no index, + // 2 if there is one level of PMGI chunks. + + /* UInt32 chunkNumber = */ ReadUInt32(); // Chunk number of root index chunk, -1 if there is none + // (though at least one file has 0 despite there being no + // index chunk, probably a bug.) + /* UInt32 firstPmglChunkNumber = */ ReadUInt32(); // Chunk number of first PMGL (listing) chunk + /* UInt32 lastPmglChunkNumber = */ ReadUInt32(); // Chunk number of last PMGL (listing) chunk + ReadUInt32(); // -1 (unknown) + UInt32 numDirChunks = ReadUInt32(); // Number of directory chunks (total) + /* UInt32 windowsLangId = */ ReadUInt32(); + ReadGUID(g); // {5D02926A-212E-11D0-9DF9-00A0C922E6EC} + ReadUInt32(); // 0x54 (This is the length again) + ReadUInt32(); // -1 (unknown) + ReadUInt32(); // -1 (unknown) + ReadUInt32(); // -1 (unknown) + + for (UInt32 ci = 0; ci < numDirChunks; ci++) + { + UInt64 chunkPos = _inBuffer.GetProcessedSize(); + if (ReadUInt32() == NHeader::kPmglSignature) + { + // The quickref area is written backwards from the end of the chunk. + // One quickref entry exists for every n entries in the file, where n + // is calculated as 1 + (1 << quickref density). So for density = 2, n = 5. + + UInt32 quickrefLength = ReadUInt32(); // Length of free space and/or quickref area at end of directory chunk + if (quickrefLength > dirChunkSize || quickrefLength < 2) + return S_FALSE; + ReadUInt32(); // Always 0 + ReadUInt32(); // Chunk number of previous listing chunk when reading + // directory in sequence (-1 if this is the first listing chunk) + ReadUInt32(); // Chunk number of next listing chunk when reading + // directory in sequence (-1 if this is the last listing chunk) + int numItems = 0; + for (;;) + { + UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos; + UInt32 offsetLimit = dirChunkSize - quickrefLength; + if (offset > offsetLimit) + return S_FALSE; + if (offset == offsetLimit) + break; + RINOK(ReadDirEntry(database)); + numItems++; + } + Skip(quickrefLength - 2); + if (ReadUInt16() != numItems) + return S_FALSE; + } + else + Skip(dirChunkSize - 4); + } + return S_OK; +} + +HRESULT CInArchive::OpenHelp2(IInStream *inStream, CDatabase &database) +{ + if (ReadUInt32() != 1) // version + return S_FALSE; + if (ReadUInt32() != 0x28) // Location of header section table + return S_FALSE; + UInt32 numHeaderSections = ReadUInt32(); + const int kNumHeaderSectionsMax = 5; + if (numHeaderSections != kNumHeaderSectionsMax) + return S_FALSE; + ReadUInt32(); // Length of post-header table + GUID g; + ReadGUID(g); // {0A9007C1-4076-11D3-8789-0000F8105754} + + // header section table + UInt64 sectionOffsets[kNumHeaderSectionsMax]; + UInt64 sectionSizes[kNumHeaderSectionsMax]; + UInt32 i; + for (i = 0; i < numHeaderSections; i++) + { + sectionOffsets[i] = ReadUInt64(); + sectionSizes[i] = ReadUInt64(); + } + + // Post-Header + ReadUInt32(); // 2 + ReadUInt32(); // 0x98: offset to CAOL from beginning of post-header) + // ----- Directory information + ReadUInt64(); // Chunk number of top-level AOLI chunk in directory, or -1 + ReadUInt64(); // Chunk number of first AOLL chunk in directory + ReadUInt64(); // Chunk number of last AOLL chunk in directory + ReadUInt64(); // 0 (unknown) + ReadUInt32(); // $2000 (Directory chunk size of directory) + ReadUInt32(); // Quickref density for main directory, usually 2 + ReadUInt32(); // 0 (unknown) + ReadUInt32(); // Depth of main directory index tree + // 1 there is no index, 2 if there is one level of AOLI chunks. + ReadUInt64(); // 0 (unknown) + UInt64 numDirEntries = ReadUInt64(); // Number of directory entries + // ----- Directory Index Information + ReadUInt64(); // -1 (unknown, probably chunk number of top-level AOLI in directory index) + ReadUInt64(); // Chunk number of first AOLL chunk in directory index + ReadUInt64(); // Chunk number of last AOLL chunk in directory index + ReadUInt64(); // 0 (unknown) + ReadUInt32(); // $200 (Directory chunk size of directory index) + ReadUInt32(); // Quickref density for directory index, usually 2 + ReadUInt32(); // 0 (unknown) + ReadUInt32(); // Depth of directory index index tree. + ReadUInt64(); // Possibly flags -- sometimes 1, sometimes 0. + ReadUInt64(); // Number of directory index entries (same as number of AOLL + // chunks in main directory) + + // (The obvious guess for the following two fields, which recur in a number + // of places, is they are maximum sizes for the directory and directory index. + // However, I have seen no direct evidence that this is the case.) + + ReadUInt32(); // $100000 (Same as field following chunk size in directory) + ReadUInt32(); // $20000 (Same as field following chunk size in directory index) + + ReadUInt64(); // 0 (unknown) + if (ReadUInt32() != NHeader::kCaolSignature) + return S_FALSE; + if (ReadUInt32() != 2) // (Most likely a version number) + return S_FALSE; + UInt32 caolLength = ReadUInt32(); // $50 (Length of the CAOL section, which includes the ITSF section) + if (caolLength >= 0x2C) + { + /* UInt32 c7 = */ ReadUInt16(); // Unknown. Remains the same when identical files are built. + // Does not appear to be a checksum. Many files have + // 'HH' (HTML Help?) here, indicating this may be a compiler ID + // field. But at least one ITOL/ITLS compiler does not set this + // field to a constant value. + ReadUInt16(); // 0 (Unknown. Possibly part of 00A4 field) + ReadUInt32(); // Unknown. Two values have been seen -- $43ED, and 0. + ReadUInt32(); // $2000 (Directory chunk size of directory) + ReadUInt32(); // $200 (Directory chunk size of directory index) + ReadUInt32(); // $100000 (Same as field following chunk size in directory) + ReadUInt32(); // $20000 (Same as field following chunk size in directory index) + ReadUInt32(); // 0 (unknown) + ReadUInt32(); // 0 (Unknown) + if (caolLength == 0x2C) + { + database.ContentOffset = 0; + database.NewFormat = true; + } + else if (caolLength == 0x50) + { + ReadUInt32(); // 0 (Unknown) + if (ReadUInt32() != NHeader::kItsfSignature) + return S_FALSE; + if (ReadUInt32() != 4) // $4 (Version number -- CHM uses 3) + return S_FALSE; + if (ReadUInt32() != 0x20) // $20 (length of ITSF) + return S_FALSE; + UInt32 unknown = ReadUInt32(); + if (unknown != 0 && unknown != 1) // = 0 for some HxW files, 1 in other cases; + return S_FALSE; + database.ContentOffset = _startPosition + ReadUInt64(); + /* UInt32 timeStamp = */ ReadUInt32(); + // A timestamp of some sort. + // Considered as a big-endian DWORD, it appears to contain + // seconds (MSB) and fractional seconds (second byte). + // The third and fourth bytes may contain even more fractional + // bits. The 4 least significant bits in the last byte are constant. + /* UInt32 lang = */ ReadUInt32(); // BE? + } + else + return S_FALSE; + } + + /* + // Section 0 + ReadChunk(inStream, _startPosition + sectionOffsets[0], sectionSizes[0]); + if (sectionSizes[0] != 0x18) + return S_FALSE; + ReadUInt32(); // unknown: 01FE + ReadUInt32(); // unknown: 0 + UInt64 fileSize = ReadUInt64(); + ReadUInt32(); // unknown: 0 + ReadUInt32(); // unknown: 0 + */ + + // Section 1: The Directory Listing + ReadChunk(inStream, _startPosition + sectionOffsets[1], sectionSizes[1]); + if (ReadUInt32() != NHeader::kIfcmSignature) + return S_FALSE; + if (ReadUInt32() != 1) // (probably a version number) + return S_FALSE; + UInt32 dirChunkSize = ReadUInt32(); // $2000 + if (dirChunkSize < 64) + return S_FALSE; + ReadUInt32(); // $100000 (unknown) + ReadUInt32(); // -1 (unknown) + ReadUInt32(); // -1 (unknown) + UInt32 numDirChunks = ReadUInt32(); + ReadUInt32(); // 0 (unknown, probably high word of above) + + for (UInt32 ci = 0; ci < numDirChunks; ci++) + { + UInt64 chunkPos = _inBuffer.GetProcessedSize(); + if (ReadUInt32() == NHeader::kAollSignature) + { + UInt32 quickrefLength = ReadUInt32(); // Length of quickref area at end of directory chunk + if (quickrefLength > dirChunkSize || quickrefLength < 2) + return S_FALSE; + ReadUInt64(); // Directory chunk number + // This must match physical position in file, that is + // the chunk size times the chunk number must be the + // offset from the end of the directory header. + ReadUInt64(); // Chunk number of previous listing chunk when reading + // directory in sequence (-1 if first listing chunk) + ReadUInt64(); // Chunk number of next listing chunk when reading + // directory in sequence (-1 if last listing chunk) + ReadUInt64(); // Number of first listing entry in this chunk + ReadUInt32(); // 1 (unknown -- other values have also been seen here) + ReadUInt32(); // 0 (unknown) + + int numItems = 0; + for (;;) + { + UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos; + UInt32 offsetLimit = dirChunkSize - quickrefLength; + if (offset > offsetLimit) + return S_FALSE; + if (offset == offsetLimit) + break; + if (database.NewFormat) + { + UInt16 nameLength = ReadUInt16(); + if (nameLength == 0) + return S_FALSE; + UString name; + ReadUString((int)nameLength, name); + AString s; + ConvertUnicodeToUTF8(name, s); + Byte b = ReadByte(); + s += ' '; + PrintByte(b, s); + s += ' '; + UInt64 len = ReadEncInt(); + // then number of items ? + // then length ? + // then some data (binary encoding?) + while (len-- != 0) + { + b = ReadByte(); + PrintByte(b, s); + } + database.NewFormatString += s; + database.NewFormatString += "\r\n"; + } + else + { + RINOK(ReadDirEntry(database)); + } + numItems++; + } + Skip(quickrefLength - 2); + if (ReadUInt16() != numItems) + return S_FALSE; + if (numItems > numDirEntries) + return S_FALSE; + numDirEntries -= numItems; + } + else + Skip(dirChunkSize - 4); + } + return numDirEntries == 0 ? S_OK : S_FALSE; +} + +HRESULT CInArchive::DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name) +{ + int index = database.FindItem(name); + if (index < 0) + return S_FALSE; + const CItem &item = database.Items[index]; + _chunkSize = item.Size; + return ReadChunk(inStream, database.ContentOffset + item.Offset, item.Size); +} + + +#define DATA_SPACE "::DataSpace/" +static const char *kNameList = DATA_SPACE "NameList"; +static const char *kStorage = DATA_SPACE "Storage/"; +static const char *kContent = "Content"; +static const char *kControlData = "ControlData"; +static const char *kSpanInfo = "SpanInfo"; +static const char *kTransform = "Transform/"; +static const char *kResetTable = "/InstanceData/ResetTable"; +static const char *kTransformList = "List"; + +static AString GetSectionPrefix(const AString &name) +{ + return AString(kStorage) + name + AString("/"); +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareFiles(const int *p1, const int *p2, void *param) +{ + const CObjectVector<CItem> &items = *(const CObjectVector<CItem> *)param; + const CItem &item1 = items[*p1]; + const CItem &item2 = items[*p2]; + bool isDir1 = item1.IsDir(); + bool isDir2 = item2.IsDir(); + if (isDir1 && !isDir2) + return -1; + if (isDir2) + { + if (isDir1) + return MyCompare(*p1, *p2); + return 1; + } + RINOZ(MyCompare(item1.Section, item2.Section)); + RINOZ(MyCompare(item1.Offset, item2.Offset)); + RINOZ(MyCompare(item1.Size, item2.Size)); + return MyCompare(*p1, *p2); +} + +void CFilesDatabase::SetIndices() +{ + for (int i = 0; i < Items.Size(); i++) + { + const CItem &item = Items[i]; + if (item.IsUserItem() && item.Name.Length() != 1) + Indices.Add(i); + } +} + +void CFilesDatabase::Sort() +{ + Indices.Sort(CompareFiles, (void *)&Items); +} + +bool CFilesDatabase::Check() +{ + UInt64 maxPos = 0; + UInt64 prevSection = 0; + for(int i = 0; i < Indices.Size(); i++) + { + const CItem &item = Items[Indices[i]]; + if (item.Section == 0 || item.IsDir()) + continue; + if (item.Section != prevSection) + { + prevSection = item.Section; + maxPos = 0; + continue; + } + if (item.Offset < maxPos) + return false; + maxPos = item.Offset + item.Size; + if (maxPos < item.Offset) + return false; + } + return true; +} + +HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) +{ + { + // The NameList file + RINOK(DecompressStream(inStream, database, kNameList)); + /* UInt16 length = */ ReadUInt16(); + UInt16 numSections = ReadUInt16(); + for (int i = 0; i < numSections; i++) + { + CSectionInfo section; + UInt16 nameLength = ReadUInt16(); + UString name; + ReadUString(nameLength, name); + if (ReadUInt16() != 0) + return S_FALSE; + if (!ConvertUnicodeToUTF8(name, section.Name)) + return S_FALSE; + database.Sections.Add(section); + } + } + + int i; + for (i = 1; i < database.Sections.Size(); i++) + { + CSectionInfo §ion = database.Sections[i]; + AString sectionPrefix = GetSectionPrefix(section.Name); + { + // Content + int index = database.FindItem(sectionPrefix + kContent); + if (index < 0) + return S_FALSE; + const CItem &item = database.Items[index]; + section.Offset = item.Offset; + section.CompressedSize = item.Size; + } + AString transformPrefix = sectionPrefix + kTransform; + if (database.Help2Format) + { + // Transform List + RINOK(DecompressStream(inStream, database, transformPrefix + kTransformList)); + if ((_chunkSize & 0xF) != 0) + return S_FALSE; + int numGuids = (int)(_chunkSize / 0x10); + if (numGuids < 1) + return S_FALSE; + for (int i = 0; i < numGuids; i++) + { + CMethodInfo method; + ReadGUID(method.Guid); + section.Methods.Add(method); + } + } + else + { + CMethodInfo method; + method.Guid = kChmLzxGuid; + section.Methods.Add(method); + } + + { + // Control Data + RINOK(DecompressStream(inStream, database, sectionPrefix + kControlData)); + for (int mi = 0; mi < section.Methods.Size(); mi++) + { + CMethodInfo &method = section.Methods[mi]; + UInt32 numDWORDS = ReadUInt32(); + if (method.IsLzx()) + { + if (numDWORDS < 5) + return S_FALSE; + if (ReadUInt32() != NHeader::kLzxcSignature) + return S_FALSE; + CLzxInfo &li = method.LzxInfo; + li.Version = ReadUInt32(); + if (li.Version != 2 && li.Version != 3) + return S_FALSE; + li.ResetInterval = ReadUInt32(); + li.WindowSize = ReadUInt32(); + li.CacheSize = ReadUInt32(); + if ( + li.ResetInterval != 1 && + li.ResetInterval != 2 && + li.ResetInterval != 4 && + li.ResetInterval != 8 && + li.ResetInterval != 16 && + li.ResetInterval != 32 && + li.ResetInterval != 64) + return S_FALSE; + if ( + li.WindowSize != 1 && + li.WindowSize != 2 && + li.WindowSize != 4 && + li.WindowSize != 8 && + li.WindowSize != 16 && + li.WindowSize != 32 && + li.WindowSize != 64) + return S_FALSE; + numDWORDS -= 5; + while (numDWORDS-- != 0) + ReadUInt32(); + } + else + { + UInt32 numBytes = numDWORDS * 4; + method.ControlData.SetCapacity(numBytes); + ReadBytes(method.ControlData, numBytes); + } + } + } + + { + // SpanInfo + RINOK(DecompressStream(inStream, database, sectionPrefix + kSpanInfo)); + section.UncompressedSize = ReadUInt64(); + } + + // read ResetTable for LZX + for (int mi = 0; mi < section.Methods.Size(); mi++) + { + CMethodInfo &method = section.Methods[mi]; + if (method.IsLzx()) + { + // ResetTable; + RINOK(DecompressStream(inStream, database, transformPrefix + + method.GetGuidString() + kResetTable)); + CResetTable &rt = method.LzxInfo.ResetTable; + if (_chunkSize < 4) + { + if (_chunkSize != 0) + return S_FALSE; + // ResetTable is empty in .chw files + if (section.UncompressedSize != 0) + return S_FALSE; + rt.UncompressedSize = 0; + rt.CompressedSize = 0; + rt.BlockSize = 0; + } + else + { + UInt32 ver = ReadUInt32(); // 2 unknown (possibly a version number) + if (ver != 2 && ver != 3) + return S_FALSE; + UInt32 numEntries = ReadUInt32(); + if (ReadUInt32() != 8) // Size of table entry (bytes) + return S_FALSE; + if (ReadUInt32() != 0x28) // Length of table header + return S_FALSE; + rt.UncompressedSize = ReadUInt64(); + rt.CompressedSize = ReadUInt64(); + rt.BlockSize = ReadUInt64(); // 0x8000 block size for locations below + if (rt.BlockSize != 0x8000) + return S_FALSE; + rt.ResetOffsets.Reserve(numEntries); + for (UInt32 i = 0; i < numEntries; i++) + rt.ResetOffsets.Add(ReadUInt64()); + } + } + } + } + + database.SetIndices(); + database.Sort(); + return database.Check() ? S_OK : S_FALSE; +} + +HRESULT CInArchive::Open2(IInStream *inStream, + const UInt64 *searchHeaderSizeLimit, + CFilesDatabase &database) +{ + database.Clear(); + + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_startPosition)); + + database.Help2Format = false; + const UInt32 chmVersion = 3; + { + if (!_inBuffer.Create(1 << 14)) + return E_OUTOFMEMORY; + _inBuffer.SetStream(inStream); + _inBuffer.Init(); + UInt64 value = 0; + const int kSignatureSize = 8; + UInt64 hxsSignature = NHeader::GetHxsSignature(); + UInt64 chmSignature = ((UInt64)chmVersion << 32)| NHeader::kItsfSignature; + UInt64 limit = 1 << 18; + if (searchHeaderSizeLimit) + if (limit > *searchHeaderSizeLimit) + limit = *searchHeaderSizeLimit; + + for (;;) + { + Byte b; + if (!_inBuffer.ReadByte(b)) + return S_FALSE; + value >>= 8; + value |= ((UInt64)b) << ((kSignatureSize - 1) * 8); + if (_inBuffer.GetProcessedSize() >= kSignatureSize) + { + if (value == chmSignature) + break; + if (value == hxsSignature) + { + database.Help2Format = true; + break; + } + if (_inBuffer.GetProcessedSize() > limit) + return S_FALSE; + } + } + _startPosition += _inBuffer.GetProcessedSize() - kSignatureSize; + } + + if (database.Help2Format) + { + RINOK(OpenHelp2(inStream, database)); + if (database.NewFormat) + return S_OK; + } + else + { + RINOK(OpenChm(inStream, database)); + } + + #ifndef CHM_LOW + try + { + HRESULT res = OpenHighLevel(inStream, database); + if (res == S_FALSE) + { + database.HighLevelClear(); + return S_OK; + } + RINOK(res); + database.LowLevel = false; + } + catch(...) + { + return S_OK; + } + #endif + return S_OK; +} + +HRESULT CInArchive::Open(IInStream *inStream, + const UInt64 *searchHeaderSizeLimit, + CFilesDatabase &database) +{ + try + { + HRESULT res = Open2(inStream, searchHeaderSizeLimit, database); + _inBuffer.ReleaseStream(); + return res; + } + catch(...) + { + _inBuffer.ReleaseStream(); + throw; + } +} + +}} |