Merging r37048, r37051, r37052, r37055 from the-real-msvc branch
[reactos.git] / irc / TechBot / CHMLibrary / CHMDecoding / CHMUrlstr.cs
1 using System;
2 using System.IO;
3 using System.Collections;
4 using System.Collections.Specialized;
5
6 namespace HtmlHelp.ChmDecoding
7 {
8 /// <summary>
9 /// The class <c>CHMUrlstr</c> implements a string collection storing the URL strings of the help file
10 /// </summary>
11 internal sealed class CHMUrlstr : IDisposable
12 {
13 /// <summary>
14 /// Constant specifying the size of the string blocks
15 /// </summary>
16 private const int BLOCK_SIZE = 0x1000;
17 /// <summary>
18 /// Internal flag specifying if the object is going to be disposed
19 /// </summary>
20 private bool disposed = false;
21 /// <summary>
22 /// Internal member storing the binary file data
23 /// </summary>
24 private byte[] _binaryFileData = null;
25 /// <summary>
26 /// Internal member storing the url dictionary
27 /// </summary>
28 private Hashtable _urlDictionary = new Hashtable();
29 /// <summary>
30 /// Internal member storing the framename dictionary
31 /// </summary>
32 private Hashtable _framenameDictionary = new Hashtable();
33 /// <summary>
34 /// Internal member storing the associated chmfile object
35 /// </summary>
36 private CHMFile _associatedFile = null;
37
38 /// <summary>
39 /// Constructor of the class
40 /// </summary>
41 /// <param name="binaryFileData">binary file data of the #URLSTR file</param>
42 /// <param name="associatedFile">associated chm file</param>
43 public CHMUrlstr(byte[] binaryFileData, CHMFile associatedFile)
44 {
45 _binaryFileData = binaryFileData;
46 _associatedFile = associatedFile;
47 DecodeData();
48
49 // clear internal binary data after extraction
50 _binaryFileData = null;
51 }
52
53 /// <summary>
54 /// Standard constructor
55 /// </summary>
56 internal CHMUrlstr()
57 {
58 }
59
60 #region Data dumping
61 /// <summary>
62 /// Dump the class data to a binary writer
63 /// </summary>
64 /// <param name="writer">writer to write the data</param>
65 internal void Dump(ref BinaryWriter writer)
66 {
67 writer.Write( _urlDictionary.Count );
68
69 if (_urlDictionary.Count != 0)
70 {
71 IDictionaryEnumerator iDictionaryEnumerator = _urlDictionary.GetEnumerator();
72 while (iDictionaryEnumerator.MoveNext())
73 {
74 DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;
75 writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );
76 writer.Write( dictionaryEntry.Value.ToString() );
77 }
78 }
79
80 writer.Write( _framenameDictionary.Count );
81
82 if (_framenameDictionary.Count != 0)
83 {
84 IDictionaryEnumerator iDictionaryEnumerator = _framenameDictionary.GetEnumerator();
85 while (iDictionaryEnumerator.MoveNext())
86 {
87 DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;
88 writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );
89 writer.Write( dictionaryEntry.Value.ToString() );
90 }
91 }
92 }
93
94 /// <summary>
95 /// Reads the object data from a dump store
96 /// </summary>
97 /// <param name="reader">reader to read the data</param>
98 internal void ReadDump(ref BinaryReader reader)
99 {
100 int i=0;
101 int nCnt = reader.ReadInt32();
102
103 for(i=0; i<nCnt;i++)
104 {
105 int nKey = reader.ReadInt32();
106 string sValue = reader.ReadString();
107
108 _urlDictionary[nKey.ToString()] = sValue;
109 }
110
111 nCnt = reader.ReadInt32();
112
113 for(i=0; i<nCnt;i++)
114 {
115 int nKey = reader.ReadInt32();
116 string sValue = reader.ReadString();
117
118 _framenameDictionary[nKey.ToString()] = sValue;
119 }
120 }
121
122 /// <summary>
123 /// Sets the associated CHMFile instance
124 /// </summary>
125 /// <param name="associatedFile">instance to set</param>
126 internal void SetCHMFile(CHMFile associatedFile)
127 {
128 _associatedFile = associatedFile;
129 }
130 #endregion
131
132 /// <summary>
133 /// Decodes the binary file data and fills the internal properties
134 /// </summary>
135 /// <returns>true if succeeded</returns>
136 private bool DecodeData()
137 {
138 bool bRet = true;
139
140 MemoryStream memStream = new MemoryStream(_binaryFileData);
141 BinaryReader binReader = new BinaryReader(memStream);
142
143 int nCurOffset = 0;
144
145 while( (memStream.Position < memStream.Length) && (bRet) )
146 {
147 nCurOffset = (int)memStream.Position;
148 byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);
149 bRet &= DecodeBlock(dataBlock, ref nCurOffset);
150 }
151
152 return bRet;
153 }
154
155 /// <summary>
156 /// Decodes a block of url-string data
157 /// </summary>
158 /// <param name="dataBlock">block of data</param>
159 /// <param name="nOffset">current file offset</param>
160 /// <returns>true if succeeded</returns>
161 private bool DecodeBlock( byte[] dataBlock, ref int nOffset )
162 {
163 bool bRet = true;
164 int blockOffset = nOffset;
165
166 MemoryStream memStream = new MemoryStream(dataBlock);
167 BinaryReader binReader = new BinaryReader(memStream);
168
169 if(nOffset==0)
170 binReader.ReadByte(); // first block starts with an unknown byte
171
172 while( (memStream.Position < (memStream.Length-8)) && (bRet) )
173 {
174 int entryOffset = blockOffset + (int)memStream.Position;
175
176 int urlOffset = binReader.ReadInt32();
177 int frameOffset = binReader.ReadInt32();
178
179
180 // There is one way to tell where the end of the URL/FrameName
181 // pairs occurs: Repeat the following: read 2 DWORDs and if both
182 // are less than the current offset then this is the start of the Local
183 // strings else skip two NT strings.
184 // if(( (urlOffset < (entryOffset+8)) && (frameOffset < (entryOffset+8)) ))
185 // {
186 // //TODO: add correct string reading if an offset has been found
187 // /*
188 // int curOffset = (int)memStream.Position;
189 //
190 // memStream.Seek( (long)(blockOffset-urlOffset), SeekOrigin.Begin);
191 // string sTemp = CHMReader.ExtractString(ref binReader, 0, true);
192 //
193 // memStream.Seek( (long)(blockOffset-frameOffset), SeekOrigin.Begin);
194 // sTemp = CHMReader.ExtractString(ref binReader, 0, true);
195 //
196 // memStream.Seek((long)curOffset, SeekOrigin.Begin);
197 // */
198 //
199 //
200 // int curOffs = (int)memStream.Position;
201 // BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding);
202 // nOffset += (int)memStream.Position - curOffs;
203 //
204 // curOffs = (int)memStream.Position;
205 // BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding);
206 // nOffset += (int)memStream.Position - curOffs;
207 // }
208 // else
209 {
210 bool bFoundTerminator = false;
211
212 string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);
213
214 if(sTemp == "")
215 {
216 //nOffset = nOffset + 1 + (int)memStream.Length - (int)memStream.Position;
217 memStream.Seek(memStream.Length-1, SeekOrigin.Begin);
218 }
219 else
220 {
221 _urlDictionary[entryOffset.ToString()] = sTemp.ToString();
222 _framenameDictionary[ entryOffset.ToString() ] = sTemp.ToString() ;
223 }
224 }
225 }
226
227 return bRet;
228 }
229
230 /// <summary>
231 /// Gets the url at a given offset
232 /// </summary>
233 /// <param name="offset">offset of url</param>
234 /// <returns>the url at the given offset</returns>
235 public string GetURLatOffset(int offset)
236 {
237 if(offset == -1)
238 return String.Empty;
239
240 string sTemp = (string)_urlDictionary[ offset.ToString() ];
241
242 if(sTemp == null)
243 return String.Empty;
244
245 return sTemp;
246 }
247
248 /// <summary>
249 /// Gets the framename at a given offset
250 /// </summary>
251 /// <param name="offset">offset of the framename</param>
252 /// <returns>the frame name at the given offset</returns>
253 public string GetFrameNameatOffset(int offset)
254 {
255 if(offset == -1)
256 return String.Empty;
257
258 string sTemp = (string)_framenameDictionary[ offset.ToString() ];
259
260 if(sTemp == null)
261 return String.Empty;
262
263 return sTemp;
264 }
265
266 /// <summary>
267 /// Implement IDisposable.
268 /// </summary>
269 public void Dispose()
270 {
271 Dispose(true);
272 // This object will be cleaned up by the Dispose method.
273 // Therefore, you should call GC.SupressFinalize to
274 // take this object off the finalization queue
275 // and prevent finalization code for this object
276 // from executing a second time.
277 GC.SuppressFinalize(this);
278 }
279
280 /// <summary>
281 /// Dispose(bool disposing) executes in two distinct scenarios.
282 /// If disposing equals true, the method has been called directly
283 /// or indirectly by a user's code. Managed and unmanaged resources
284 /// can be disposed.
285 /// If disposing equals false, the method has been called by the
286 /// runtime from inside the finalizer and you should not reference
287 /// other objects. Only unmanaged resources can be disposed.
288 /// </summary>
289 /// <param name="disposing">disposing flag</param>
290 private void Dispose(bool disposing)
291 {
292 // Check to see if Dispose has already been called.
293 if(!this.disposed)
294 {
295 // If disposing equals true, dispose all managed
296 // and unmanaged resources.
297 if(disposing)
298 {
299 // Dispose managed resources.
300 _binaryFileData = null;
301 _urlDictionary = null;
302 _framenameDictionary = null;
303 }
304 }
305 disposed = true;
306 }
307 }
308 }