- Fix KiDispatchException to unmask KI_EXCEPTION_INTERNAL when setting the exception...
[reactos.git] / irc / TechBot / CHMLibrary / CHMDecoding / CHMStrings.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>CHMStrings</c> implements a string collection read from the #STRINGS file
10 /// </summary>
11 internal sealed class CHMStrings : IDisposable
12 {
13 /// <summary>
14 /// Constant specifying the size of the string blocks
15 /// </summary>
16 private const int STRING_BLOCK_SIZE = 4096;
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 string dictionary
27 /// </summary>
28 private Hashtable _stringDictionary = new Hashtable();
29 /// <summary>
30 /// Internal member storing the associated chmfile object
31 /// </summary>
32 private CHMFile _associatedFile = null;
33
34 /// <summary>
35 /// Constructor of the class
36 /// </summary>
37 /// <param name="binaryFileData">binary file data of the #STRINGS file</param>
38 /// <param name="associatedFile">associated chm file</param>
39 public CHMStrings(byte[] binaryFileData, CHMFile associatedFile)
40 {
41 _binaryFileData = binaryFileData;
42 _associatedFile = associatedFile;
43 DecodeData();
44 }
45
46
47 /// <summary>
48 /// Standard constructor
49 /// </summary>
50 internal CHMStrings()
51 {
52 }
53
54 #region Data dumping
55 /// <summary>
56 /// Dump the class data to a binary writer
57 /// </summary>
58 /// <param name="writer">writer to write the data</param>
59 internal void Dump(ref BinaryWriter writer)
60 {
61 writer.Write( _stringDictionary.Count );
62
63 if (_stringDictionary.Count != 0)
64 {
65 IDictionaryEnumerator iDictionaryEnumerator = _stringDictionary.GetEnumerator();
66 while (iDictionaryEnumerator.MoveNext())
67 {
68 DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;
69 writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );
70 writer.Write( dictionaryEntry.Value.ToString() );
71 }
72 }
73 }
74
75 /// <summary>
76 /// Reads the object data from a dump store
77 /// </summary>
78 /// <param name="reader">reader to read the data</param>
79 internal void ReadDump(ref BinaryReader reader)
80 {
81 int nCnt = reader.ReadInt32();
82
83 for(int i=0; i<nCnt;i++)
84 {
85 int nKey = reader.ReadInt32();
86 string sValue = reader.ReadString();
87
88 _stringDictionary[nKey.ToString()] = sValue;
89 }
90 }
91
92 /// <summary>
93 /// Sets the associated CHMFile instance
94 /// </summary>
95 /// <param name="associatedFile">instance to set</param>
96 internal void SetCHMFile(CHMFile associatedFile)
97 {
98 _associatedFile = associatedFile;
99 }
100 #endregion
101
102 /// <summary>
103 /// Decodes the binary file data and fills the internal properties
104 /// </summary>
105 /// <returns>true if succeeded</returns>
106 private bool DecodeData()
107 {
108 bool bRet = true;
109
110 MemoryStream memStream = new MemoryStream(_binaryFileData);
111 BinaryReader binReader = new BinaryReader(memStream);
112
113 //binReader.ReadByte(); // file starts with a NULL character for 0-based offset indexing
114
115 int nStringOffset = 0;
116 int nSubsetOffset = 0;
117
118 while( (memStream.Position < memStream.Length) && (bRet) )
119 {
120 nStringOffset = (int)memStream.Position;
121 byte [] stringBlock = binReader.ReadBytes(STRING_BLOCK_SIZE);
122 bRet &= DecodeBlock(stringBlock, ref nStringOffset, ref nSubsetOffset);
123 }
124
125 return bRet;
126 }
127
128 /// <summary>
129 /// Decodes a string block
130 /// </summary>
131 /// <param name="stringBlock">byte array which represents the string block</param>
132 /// <param name="nStringOffset">current string offset number</param>
133 /// <param name="nSubsetOffset">reference to a subset variable</param>
134 /// <returns>true if succeeded</returns>
135 /// <remarks>If a string crosses the end of a block then it will be cut off
136 /// without a NT and repeated in full, with a NT, at the start of the next block.
137 /// For eg "To customize the appearance of a contents file" might become
138 /// "To customize the (block ending)To customize the appearance of a contents file"
139 /// when there are 17 bytes left at the end of the block. </remarks>
140 private bool DecodeBlock( byte[] stringBlock, ref int nStringOffset, ref int nSubsetOffset)
141 {
142 bool bRet = true;
143
144 MemoryStream memStream = new MemoryStream(stringBlock);
145 BinaryReader binReader = new BinaryReader(memStream);
146
147 while( (memStream.Position < memStream.Length) && (bRet) )
148 {
149 bool bFoundTerminator = false;
150
151 int nCurOffset = nStringOffset + (int)memStream.Position;
152
153 string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);
154
155 if(nSubsetOffset != 0)
156 {
157 _stringDictionary[nSubsetOffset.ToString()] = sTemp.ToString();
158 }
159 else
160 {
161 _stringDictionary[nCurOffset.ToString()] = sTemp.ToString();
162 }
163
164 if( bFoundTerminator )
165 {
166 nSubsetOffset = 0;
167 }
168 else
169 {
170 nSubsetOffset = nCurOffset;
171 }
172 }
173
174 return bRet;
175 }
176
177 /// <summary>
178 /// Indexer which returns the string at a given offset
179 /// </summary>
180 public string this[int offset]
181 {
182 get
183 {
184 if(offset == -1)
185 return String.Empty;
186
187 string sTemp = (string)_stringDictionary[ offset.ToString() ];
188
189 if(sTemp == null)
190 return String.Empty;
191
192 return sTemp;
193 }
194 }
195
196 /// <summary>
197 /// Indexer which returns the string at a given offset
198 /// </summary>
199 public string this[string offset]
200 {
201 get
202 {
203 if(offset == "-1")
204 return String.Empty;
205
206 string sTemp = (string)_stringDictionary[ offset ];
207
208 if(sTemp == null)
209 return String.Empty;
210
211 return sTemp;
212 }
213 }
214
215 /// <summary>
216 /// Implement IDisposable.
217 /// </summary>
218 public void Dispose()
219 {
220 Dispose(true);
221 // This object will be cleaned up by the Dispose method.
222 // Therefore, you should call GC.SupressFinalize to
223 // take this object off the finalization queue
224 // and prevent finalization code for this object
225 // from executing a second time.
226 GC.SuppressFinalize(this);
227 }
228
229 /// <summary>
230 /// Dispose(bool disposing) executes in two distinct scenarios.
231 /// If disposing equals true, the method has been called directly
232 /// or indirectly by a user's code. Managed and unmanaged resources
233 /// can be disposed.
234 /// If disposing equals false, the method has been called by the
235 /// runtime from inside the finalizer and you should not reference
236 /// other objects. Only unmanaged resources can be disposed.
237 /// </summary>
238 /// <param name="disposing">disposing flag</param>
239 private void Dispose(bool disposing)
240 {
241 // Check to see if Dispose has already been called.
242 if(!this.disposed)
243 {
244 // If disposing equals true, dispose all managed
245 // and unmanaged resources.
246 if(disposing)
247 {
248 // Dispose managed resources.
249 _binaryFileData = null;
250 _stringDictionary = null;
251 }
252 }
253 disposed = true;
254 }
255 }
256 }