// Archive/ChmIn.h #ifndef __ARCHIVE_CHM_IN_H #define __ARCHIVE_CHM_IN_H #include "../../../Common/MyBuffer.h" #include "../../../Common/MyString.h" #include "../../IStream.h" #include "../../Common/InBuffer.h" namespace NArchive { namespace NChm { struct CItem { UInt64 Section; UInt64 Offset; UInt64 Size; AString Name; bool IsFormatRelatedItem() const { if (Name.Len() < 2) return false; return Name[0] == ':' && Name[1] == ':'; } bool IsUserItem() const { if (Name.Len() < 2) return false; return Name[0] == '/'; } bool IsDir() const { if (Name.IsEmpty()) return false; return (Name.Back() == '/'); } }; struct CDatabase { UInt64 StartPosition; UInt64 ContentOffset; CObjectVector Items; AString NewFormatString; bool Help2Format; bool NewFormat; UInt64 PhySize; void UpdatePhySize(UInt64 v) { if (PhySize < v) PhySize = v; } int FindItem(const AString &name) const { FOR_VECTOR (i, Items) if (Items[i].Name == name) return i; return -1; } void Clear() { NewFormat = false; NewFormatString.Empty(); Help2Format = false; Items.Clear(); StartPosition = 0; PhySize = 0; } }; const UInt32 kBlockSize = 1 << 15; struct CResetTable { UInt64 UncompressedSize; UInt64 CompressedSize; // unsigned BlockSizeBits; CRecordVector ResetOffsets; bool GetCompressedSizeOfBlocks(UInt64 blockIndex, UInt32 numBlocks, UInt64 &size) const { if (blockIndex >= ResetOffsets.Size()) return false; UInt64 startPos = ResetOffsets[(unsigned)blockIndex]; if (blockIndex + numBlocks >= ResetOffsets.Size()) size = CompressedSize - startPos; else size = ResetOffsets[(unsigned)(blockIndex + numBlocks)] - startPos; return true; } bool GetCompressedSizeOfBlock(UInt64 blockIndex, UInt64 &size) const { return GetCompressedSizeOfBlocks(blockIndex, 1, size); } UInt64 GetNumBlocks(UInt64 size) const { return (size + kBlockSize - 1) / kBlockSize; } }; struct CLzxInfo { UInt32 Version; unsigned ResetIntervalBits; unsigned WindowSizeBits; UInt32 CacheSize; CResetTable ResetTable; unsigned GetNumDictBits() const { if (Version == 2 || Version == 3) return 15 + WindowSizeBits; return 0; } UInt64 GetFolderSize() const { return kBlockSize << ResetIntervalBits; } UInt64 GetFolder(UInt64 offset) const { return offset / GetFolderSize(); } UInt64 GetFolderPos(UInt64 folderIndex) const { return folderIndex * GetFolderSize(); } UInt64 GetBlockIndexFromFolderIndex(UInt64 folderIndex) const { return folderIndex << ResetIntervalBits; } bool GetOffsetOfFolder(UInt64 folderIndex, UInt64 &offset) const { UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex); if (blockIndex >= ResetTable.ResetOffsets.Size()) return false; offset = ResetTable.ResetOffsets[(unsigned)blockIndex]; return true; } bool GetCompressedSizeOfFolder(UInt64 folderIndex, UInt64 &size) const { UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex); return ResetTable.GetCompressedSizeOfBlocks(blockIndex, (UInt32)1 << ResetIntervalBits, size); } }; struct CMethodInfo { GUID Guid; CByteBuffer ControlData; CLzxInfo LzxInfo; bool IsLzx() const; bool IsDes() const; AString GetGuidString() const; UString GetName() const; }; struct CSectionInfo { UInt64 Offset; UInt64 CompressedSize; UInt64 UncompressedSize; AString Name; CObjectVector Methods; bool IsLzx() const; UString GetMethodName() const; }; class CFilesDatabase: public CDatabase { public: bool LowLevel; CUIntVector Indices; CObjectVector Sections; UInt64 GetFileSize(unsigned fileIndex) const { return Items[Indices[fileIndex]].Size; } UInt64 GetFileOffset(unsigned fileIndex) const { return Items[Indices[fileIndex]].Offset; } UInt64 GetFolder(unsigned fileIndex) const { const CItem &item = Items[Indices[fileIndex]]; if (item.Section < Sections.Size()) { const CSectionInfo §ion = Sections[(unsigned)item.Section]; if (section.IsLzx()) return section.Methods[0].LzxInfo.GetFolder(item.Offset); } return 0; } UInt64 GetLastFolder(unsigned fileIndex) const { const CItem &item = Items[Indices[fileIndex]]; if (item.Section < Sections.Size()) { const CSectionInfo §ion = Sections[(unsigned)item.Section]; if (section.IsLzx()) return section.Methods[0].LzxInfo.GetFolder(item.Offset + item.Size - 1); } return 0; } void HighLevelClear() { LowLevel = true; Indices.Clear(); Sections.Clear(); } void Clear() { CDatabase::Clear(); HighLevelClear(); } void SetIndices(); void Sort(); bool Check(); bool CheckSectionRefs(); }; class CInArchive { CMyComPtr m_InStreamRef; ::CInBuffer _inBuffer; UInt64 _chunkSize; bool _help2; Byte ReadByte(); void ReadBytes(Byte *data, UInt32 size); void Skip(size_t size); UInt16 ReadUInt16(); UInt32 ReadUInt32(); UInt64 ReadUInt64(); UInt64 ReadEncInt(); void ReadString(unsigned size, AString &s); void ReadUString(unsigned size, UString &s); void ReadGUID(GUID &g); HRESULT ReadChunk(IInStream *inStream, UInt64 pos, UInt64 size); HRESULT ReadDirEntry(CDatabase &database); HRESULT DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name); public: bool IsArc; bool HeadersError; bool UnexpectedEnd; bool UnsupportedFeature; CInArchive(bool help2) { _help2 = help2; } HRESULT OpenChm(IInStream *inStream, CDatabase &database); HRESULT OpenHelp2(IInStream *inStream, CDatabase &database); HRESULT OpenHighLevel(IInStream *inStream, CFilesDatabase &database); HRESULT Open2(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, CFilesDatabase &database); HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, CFilesDatabase &database); }; }} #endif