1 // Archive/ChmIn.h
2 
3 #ifndef __ARCHIVE_CHM_IN_H
4 #define __ARCHIVE_CHM_IN_H
5 
6 #include "../../../Common/MyBuffer.h"
7 #include "../../../Common/MyString.h"
8 
9 #include "../../IStream.h"
10 
11 #include "../../Common/InBuffer.h"
12 
13 namespace NArchive {
14 namespace NChm {
15 
16 struct CItem
17 {
18   UInt64 Section;
19   UInt64 Offset;
20   UInt64 Size;
21   AString Name;
22 
IsFormatRelatedItemCItem23   bool IsFormatRelatedItem() const
24   {
25     if (Name.Len() < 2)
26       return false;
27     return Name[0] == ':' && Name[1] == ':';
28   }
29 
IsUserItemCItem30   bool IsUserItem() const
31   {
32     if (Name.Len() < 2)
33       return false;
34     return Name[0] == '/';
35   }
36 
IsDirCItem37   bool IsDir() const
38   {
39     if (Name.IsEmpty())
40       return false;
41     return (Name.Back() == '/');
42   }
43 };
44 
45 
46 struct CDatabase
47 {
48   UInt64 StartPosition;
49   UInt64 ContentOffset;
50   CObjectVector<CItem> Items;
51   AString NewFormatString;
52   bool Help2Format;
53   bool NewFormat;
54   UInt64 PhySize;
55 
UpdatePhySizeCDatabase56   void UpdatePhySize(UInt64 v) { if (PhySize < v) PhySize = v; }
57 
FindItemCDatabase58   int FindItem(const AString &name) const
59   {
60     FOR_VECTOR (i, Items)
61       if (Items[i].Name == name)
62         return i;
63     return -1;
64   }
65 
ClearCDatabase66   void Clear()
67   {
68     NewFormat = false;
69     NewFormatString.Empty();
70     Help2Format = false;
71     Items.Clear();
72     StartPosition = 0;
73     PhySize = 0;
74   }
75 };
76 
77 
78 const UInt32 kBlockSize = 1 << 15;
79 
80 struct CResetTable
81 {
82   UInt64 UncompressedSize;
83   UInt64 CompressedSize;
84   // unsigned BlockSizeBits;
85   CRecordVector<UInt64> ResetOffsets;
86 
GetCompressedSizeOfBlocksCResetTable87   bool GetCompressedSizeOfBlocks(UInt64 blockIndex, UInt32 numBlocks, UInt64 &size) const
88   {
89     if (blockIndex >= ResetOffsets.Size())
90       return false;
91     UInt64 startPos = ResetOffsets[(unsigned)blockIndex];
92     if (blockIndex + numBlocks >= ResetOffsets.Size())
93       size = CompressedSize - startPos;
94     else
95       size = ResetOffsets[(unsigned)(blockIndex + numBlocks)] - startPos;
96     return true;
97   }
98 
GetCompressedSizeOfBlockCResetTable99   bool GetCompressedSizeOfBlock(UInt64 blockIndex, UInt64 &size) const
100   {
101     return GetCompressedSizeOfBlocks(blockIndex, 1, size);
102   }
103 
GetNumBlocksCResetTable104   UInt64 GetNumBlocks(UInt64 size) const
105   {
106     return (size + kBlockSize - 1) / kBlockSize;
107   }
108 };
109 
110 
111 struct CLzxInfo
112 {
113   UInt32 Version;
114 
115   unsigned ResetIntervalBits;
116   unsigned WindowSizeBits;
117   UInt32 CacheSize;
118 
119   CResetTable ResetTable;
120 
GetNumDictBitsCLzxInfo121   unsigned GetNumDictBits() const
122   {
123     if (Version == 2 || Version == 3)
124       return 15 + WindowSizeBits;
125     return 0;
126   }
127 
GetFolderSizeCLzxInfo128   UInt64 GetFolderSize() const { return kBlockSize << ResetIntervalBits; }
GetFolderCLzxInfo129   UInt64 GetFolder(UInt64 offset) const { return offset / GetFolderSize(); }
GetFolderPosCLzxInfo130   UInt64 GetFolderPos(UInt64 folderIndex) const { return folderIndex * GetFolderSize(); }
GetBlockIndexFromFolderIndexCLzxInfo131   UInt64 GetBlockIndexFromFolderIndex(UInt64 folderIndex) const { return folderIndex << ResetIntervalBits; }
132 
GetOffsetOfFolderCLzxInfo133   bool GetOffsetOfFolder(UInt64 folderIndex, UInt64 &offset) const
134   {
135     UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex);
136     if (blockIndex >= ResetTable.ResetOffsets.Size())
137       return false;
138     offset = ResetTable.ResetOffsets[(unsigned)blockIndex];
139     return true;
140   }
141 
GetCompressedSizeOfFolderCLzxInfo142   bool GetCompressedSizeOfFolder(UInt64 folderIndex, UInt64 &size) const
143   {
144     UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex);
145     return ResetTable.GetCompressedSizeOfBlocks(blockIndex, (UInt32)1 << ResetIntervalBits, size);
146   }
147 };
148 
149 
150 struct CMethodInfo
151 {
152   Byte Guid[16];
153   CByteBuffer ControlData;
154   CLzxInfo LzxInfo;
155 
156   bool IsLzx() const;
157   bool IsDes() const;
158   AString GetGuidString() const;
159   AString GetName() const;
160 };
161 
162 
163 struct CSectionInfo
164 {
165   UInt64 Offset;
166   UInt64 CompressedSize;
167   UInt64 UncompressedSize;
168 
169   AString Name;
170   CObjectVector<CMethodInfo> Methods;
171 
172   bool IsLzx() const;
173   UString GetMethodName() const;
174 };
175 
176 class CFilesDatabase: public CDatabase
177 {
178 public:
179   bool LowLevel;
180   CUIntVector Indices;
181   CObjectVector<CSectionInfo> Sections;
182 
GetFileSize(unsigned fileIndex)183   UInt64 GetFileSize(unsigned fileIndex) const { return Items[Indices[fileIndex]].Size; }
GetFileOffset(unsigned fileIndex)184   UInt64 GetFileOffset(unsigned fileIndex) const { return Items[Indices[fileIndex]].Offset; }
185 
GetFolder(unsigned fileIndex)186   UInt64 GetFolder(unsigned fileIndex) const
187   {
188     const CItem &item = Items[Indices[fileIndex]];
189     if (item.Section < Sections.Size())
190     {
191       const CSectionInfo &section = Sections[(unsigned)item.Section];
192       if (section.IsLzx())
193         return section.Methods[0].LzxInfo.GetFolder(item.Offset);
194     }
195     return 0;
196   }
197 
GetLastFolder(unsigned fileIndex)198   UInt64 GetLastFolder(unsigned fileIndex) const
199   {
200     const CItem &item = Items[Indices[fileIndex]];
201     if (item.Section < Sections.Size())
202     {
203       const CSectionInfo &section = Sections[(unsigned)item.Section];
204       if (section.IsLzx())
205         return section.Methods[0].LzxInfo.GetFolder(item.Offset + item.Size - 1);
206     }
207     return 0;
208   }
209 
HighLevelClear()210   void HighLevelClear()
211   {
212     LowLevel = true;
213     Indices.Clear();
214     Sections.Clear();
215   }
216 
Clear()217   void Clear()
218   {
219     CDatabase::Clear();
220     HighLevelClear();
221   }
222 
223   void SetIndices();
224   void Sort();
225   bool Check();
226   bool CheckSectionRefs();
227 };
228 
229 
230 class CInArchive
231 {
232   CMyComPtr<ISequentialInStream> m_InStreamRef;
233   ::CInBuffer _inBuffer;
234   UInt64 _chunkSize;
235   bool _help2;
236 
237   Byte ReadByte();
238   void ReadBytes(Byte *data, UInt32 size);
239   void Skip(size_t size);
240   UInt16 ReadUInt16();
241   UInt32 ReadUInt32();
242   UInt64 ReadUInt64();
243   UInt64 ReadEncInt();
244   void ReadString(unsigned size, AString &s);
245   void ReadUString(unsigned size, UString &s);
246   void ReadGUID(Byte *g);
247 
248   HRESULT ReadChunk(IInStream *inStream, UInt64 pos, UInt64 size);
249 
250   HRESULT ReadDirEntry(CDatabase &database);
251   HRESULT DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name);
252 
253 public:
254   bool IsArc;
255   bool HeadersError;
256   bool UnexpectedEnd;
257   bool UnsupportedFeature;
258 
CInArchive(bool help2)259   CInArchive(bool help2) { _help2 = help2; }
260 
261   HRESULT OpenChm(IInStream *inStream, CDatabase &database);
262   HRESULT OpenHelp2(IInStream *inStream, CDatabase &database);
263   HRESULT OpenHighLevel(IInStream *inStream, CFilesDatabase &database);
264   HRESULT Open2(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, CFilesDatabase &database);
265   HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, CFilesDatabase &database);
266 };
267 
268 }}
269 
270 #endif
271