prevent buffer overflow, LoadString accepts the size of the buffer in TCHARs, not...
[reactos.git] / irc / TechBot / CHMLibrary / CHMDecoding / CHMUrltable.cs
1 using System;
2 using System.IO;
3 using System.Collections;
4
5 namespace HtmlHelp.ChmDecoding
6 {
7 /// <summary>
8 /// The class <c>CHMUrltable</c> implements methods to decode the #URLTBL internal file.
9 /// </summary>
10 internal sealed class CHMUrltable : IDisposable
11 {
12 /// <summary>
13 /// Constant specifying the size of the data blocks
14 /// </summary>
15 private const int BLOCK_SIZE = 0x1000;
16 /// <summary>
17 /// Constant specifying the number of records per block
18 /// </summary>
19 private const int RECORDS_PER_BLOCK = 341;
20 /// <summary>
21 /// Internal flag specifying if the object is going to be disposed
22 /// </summary>
23 private bool disposed = false;
24 /// <summary>
25 /// Internal member storing the binary file data
26 /// </summary>
27 private byte[] _binaryFileData = null;
28 /// <summary>
29 /// Internal member storing the url table
30 /// </summary>
31 private ArrayList _urlTable = new ArrayList();
32 /// <summary>
33 /// Internal member storing the associated chmfile object
34 /// </summary>
35 private CHMFile _associatedFile = null;
36
37 /// <summary>
38 /// Constructor of the class
39 /// </summary>
40 /// <param name="binaryFileData">binary file data of the #URLTBL file</param>
41 /// <param name="associatedFile">associated chm file</param>
42 public CHMUrltable(byte[] binaryFileData, CHMFile associatedFile)
43 {
44 _binaryFileData = binaryFileData;
45 _associatedFile = associatedFile;
46 DecodeData();
47
48 // clear internal binary data after extraction
49 _binaryFileData = null;
50 }
51
52 /// <summary>
53 /// Standard constructor
54 /// </summary>
55 internal CHMUrltable()
56 {
57 }
58
59 #region Data dumping
60 /// <summary>
61 /// Dump the class data to a binary writer
62 /// </summary>
63 /// <param name="writer">writer to write the data</param>
64 internal void Dump(ref BinaryWriter writer)
65 {
66 writer.Write( _urlTable.Count );
67 foreach(UrlTableEntry curItem in _urlTable)
68 {
69 curItem.Dump(ref writer);
70 }
71 }
72
73 /// <summary>
74 /// Reads the object data from a dump store
75 /// </summary>
76 /// <param name="reader">reader to read the data</param>
77 internal void ReadDump(ref BinaryReader reader)
78 {
79 int i=0;
80 int nCnt = reader.ReadInt32();
81
82 for(i=0; i<nCnt;i++)
83 {
84 UrlTableEntry newItem = new UrlTableEntry();
85 newItem.SetCHMFile(_associatedFile);
86 newItem.ReadDump(ref reader);
87 _urlTable.Add(newItem);
88 }
89 }
90
91 /// <summary>
92 /// Sets the associated CHMFile instance
93 /// </summary>
94 /// <param name="associatedFile">instance to set</param>
95 internal void SetCHMFile(CHMFile associatedFile)
96 {
97 _associatedFile = associatedFile;
98
99 foreach(UrlTableEntry curEntry in _urlTable)
100 {
101 curEntry.SetCHMFile(associatedFile);
102 }
103 }
104 #endregion
105
106 /// <summary>
107 /// Decodes the binary file data and fills the internal properties
108 /// </summary>
109 /// <returns>true if succeeded</returns>
110 private bool DecodeData()
111 {
112 bool bRet = true;
113
114 MemoryStream memStream = new MemoryStream(_binaryFileData);
115 BinaryReader binReader = new BinaryReader(memStream);
116
117 int nCurOffset = 0;
118
119 while( (memStream.Position < memStream.Length) && (bRet) )
120 {
121 nCurOffset = (int)memStream.Position;
122 byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);
123 bRet &= DecodeBlock(dataBlock, ref nCurOffset);
124 }
125
126 return bRet;
127 }
128
129 /// <summary>
130 /// Decodes a block of url-string data
131 /// </summary>
132 /// <param name="dataBlock">block of data</param>
133 /// <param name="nOffset">current file offset</param>
134 /// <returns>true if succeeded</returns>
135 private bool DecodeBlock( byte[] dataBlock, ref int nOffset )
136 {
137 bool bRet = true;
138 int blockOffset = nOffset;
139
140 MemoryStream memStream = new MemoryStream(dataBlock);
141 BinaryReader binReader = new BinaryReader(memStream);
142
143 for(int i=0; i < RECORDS_PER_BLOCK; i++)
144 {
145 int recordOffset = blockOffset + (int)memStream.Position;
146
147 uint nuniqueID = (uint) binReader.ReadInt32(); // unknown dword
148 int ntopicsIdx = binReader.ReadInt32();
149 int urlstrOffset = binReader.ReadInt32();
150
151 UrlTableEntry newEntry = new UrlTableEntry(nuniqueID, recordOffset, ntopicsIdx, urlstrOffset, _associatedFile);
152 _urlTable.Add(newEntry);
153
154 if( memStream.Position >= memStream.Length)
155 break;
156 }
157
158 if(dataBlock.Length == BLOCK_SIZE)
159 binReader.ReadInt32();
160
161 return bRet;
162 }
163
164 /// <summary>
165 /// Gets the arraylist containing all urltable entries.
166 /// </summary>
167 public ArrayList UrlTable
168 {
169 get
170 {
171 return _urlTable;
172 }
173 }
174
175 /// <summary>
176 /// Gets the urltable entry of a given offset
177 /// </summary>
178 public UrlTableEntry this[int offset]
179 {
180 get
181 {
182 foreach(UrlTableEntry curEntry in _urlTable)
183 if(curEntry.EntryOffset == offset)
184 return curEntry;
185
186 return null;
187 }
188 }
189
190 /// <summary>
191 /// Gets the urltable entry of a given uniqueID
192 /// </summary>
193 public UrlTableEntry GetByUniqueID(uint uniqueID)
194 {
195 foreach(UrlTableEntry curEntry in UrlTable)
196 {
197 if(curEntry.UniqueID == uniqueID)
198 return curEntry;
199 }
200
201 return null;
202 }
203
204 /// <summary>
205 /// Implement IDisposable.
206 /// </summary>
207 public void Dispose()
208 {
209 Dispose(true);
210 // This object will be cleaned up by the Dispose method.
211 // Therefore, you should call GC.SupressFinalize to
212 // take this object off the finalization queue
213 // and prevent finalization code for this object
214 // from executing a second time.
215 GC.SuppressFinalize(this);
216 }
217
218 /// <summary>
219 /// Dispose(bool disposing) executes in two distinct scenarios.
220 /// If disposing equals true, the method has been called directly
221 /// or indirectly by a user's code. Managed and unmanaged resources
222 /// can be disposed.
223 /// If disposing equals false, the method has been called by the
224 /// runtime from inside the finalizer and you should not reference
225 /// other objects. Only unmanaged resources can be disposed.
226 /// </summary>
227 /// <param name="disposing">disposing flag</param>
228 private void Dispose(bool disposing)
229 {
230 // Check to see if Dispose has already been called.
231 if(!this.disposed)
232 {
233 // If disposing equals true, dispose all managed
234 // and unmanaged resources.
235 if(disposing)
236 {
237 // Dispose managed resources.
238 _binaryFileData = null;
239 _urlTable = null;
240 }
241 }
242 disposed = true;
243 }
244 }
245 }