using System; using System.IO; using System.Collections; using System.Collections.Specialized; namespace HtmlHelp.ChmDecoding { /// /// The class CHMUrlstr implements a string collection storing the URL strings of the help file /// internal sealed class CHMUrlstr : IDisposable { /// /// Constant specifying the size of the string blocks /// private const int BLOCK_SIZE = 0x1000; /// /// Internal flag specifying if the object is going to be disposed /// private bool disposed = false; /// /// Internal member storing the binary file data /// private byte[] _binaryFileData = null; /// /// Internal member storing the url dictionary /// private Hashtable _urlDictionary = new Hashtable(); /// /// Internal member storing the framename dictionary /// private Hashtable _framenameDictionary = new Hashtable(); /// /// Internal member storing the associated chmfile object /// private CHMFile _associatedFile = null; /// /// Constructor of the class /// /// binary file data of the #URLSTR file /// associated chm file public CHMUrlstr(byte[] binaryFileData, CHMFile associatedFile) { _binaryFileData = binaryFileData; _associatedFile = associatedFile; DecodeData(); // clear internal binary data after extraction _binaryFileData = null; } /// /// Standard constructor /// internal CHMUrlstr() { } #region Data dumping /// /// Dump the class data to a binary writer /// /// writer to write the data internal void Dump(ref BinaryWriter writer) { writer.Write( _urlDictionary.Count ); if (_urlDictionary.Count != 0) { IDictionaryEnumerator iDictionaryEnumerator = _urlDictionary.GetEnumerator(); while (iDictionaryEnumerator.MoveNext()) { DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current; writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) ); writer.Write( dictionaryEntry.Value.ToString() ); } } writer.Write( _framenameDictionary.Count ); if (_framenameDictionary.Count != 0) { IDictionaryEnumerator iDictionaryEnumerator = _framenameDictionary.GetEnumerator(); while (iDictionaryEnumerator.MoveNext()) { DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current; writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) ); writer.Write( dictionaryEntry.Value.ToString() ); } } } /// /// Reads the object data from a dump store /// /// reader to read the data internal void ReadDump(ref BinaryReader reader) { int i=0; int nCnt = reader.ReadInt32(); for(i=0; i /// Sets the associated CHMFile instance /// /// instance to set internal void SetCHMFile(CHMFile associatedFile) { _associatedFile = associatedFile; } #endregion /// /// Decodes the binary file data and fills the internal properties /// /// true if succeeded private bool DecodeData() { bool bRet = true; MemoryStream memStream = new MemoryStream(_binaryFileData); BinaryReader binReader = new BinaryReader(memStream); int nCurOffset = 0; while( (memStream.Position < memStream.Length) && (bRet) ) { nCurOffset = (int)memStream.Position; byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE); bRet &= DecodeBlock(dataBlock, ref nCurOffset); } return bRet; } /// /// Decodes a block of url-string data /// /// block of data /// current file offset /// true if succeeded private bool DecodeBlock( byte[] dataBlock, ref int nOffset ) { bool bRet = true; int blockOffset = nOffset; MemoryStream memStream = new MemoryStream(dataBlock); BinaryReader binReader = new BinaryReader(memStream); if(nOffset==0) binReader.ReadByte(); // first block starts with an unknown byte while( (memStream.Position < (memStream.Length-8)) && (bRet) ) { int entryOffset = blockOffset + (int)memStream.Position; int urlOffset = binReader.ReadInt32(); int frameOffset = binReader.ReadInt32(); // There is one way to tell where the end of the URL/FrameName // pairs occurs: Repeat the following: read 2 DWORDs and if both // are less than the current offset then this is the start of the Local // strings else skip two NT strings. // if(( (urlOffset < (entryOffset+8)) && (frameOffset < (entryOffset+8)) )) // { // //TODO: add correct string reading if an offset has been found // /* // int curOffset = (int)memStream.Position; // // memStream.Seek( (long)(blockOffset-urlOffset), SeekOrigin.Begin); // string sTemp = CHMReader.ExtractString(ref binReader, 0, true); // // memStream.Seek( (long)(blockOffset-frameOffset), SeekOrigin.Begin); // sTemp = CHMReader.ExtractString(ref binReader, 0, true); // // memStream.Seek((long)curOffset, SeekOrigin.Begin); // */ // // // int curOffs = (int)memStream.Position; // BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding); // nOffset += (int)memStream.Position - curOffs; // // curOffs = (int)memStream.Position; // BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding); // nOffset += (int)memStream.Position - curOffs; // } // else { bool bFoundTerminator = false; string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding); if(sTemp == "") { //nOffset = nOffset + 1 + (int)memStream.Length - (int)memStream.Position; memStream.Seek(memStream.Length-1, SeekOrigin.Begin); } else { _urlDictionary[entryOffset.ToString()] = sTemp.ToString(); _framenameDictionary[ entryOffset.ToString() ] = sTemp.ToString() ; } } } return bRet; } /// /// Gets the url at a given offset /// /// offset of url /// the url at the given offset public string GetURLatOffset(int offset) { if(offset == -1) return String.Empty; string sTemp = (string)_urlDictionary[ offset.ToString() ]; if(sTemp == null) return String.Empty; return sTemp; } /// /// Gets the framename at a given offset /// /// offset of the framename /// the frame name at the given offset public string GetFrameNameatOffset(int offset) { if(offset == -1) return String.Empty; string sTemp = (string)_framenameDictionary[ offset.ToString() ]; if(sTemp == null) return String.Empty; return sTemp; } /// /// Implement IDisposable. /// public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } /// /// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly /// or indirectly by a user's code. Managed and unmanaged resources /// can be disposed. /// If disposing equals false, the method has been called by the /// runtime from inside the finalizer and you should not reference /// other objects. Only unmanaged resources can be disposed. /// /// disposing flag private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. _binaryFileData = null; _urlDictionary = null; _framenameDictionary = null; } } disposed = true; } } }