using System; using System.IO; using System.Collections; using System.Collections.Specialized; namespace HtmlHelp.ChmDecoding { /// /// The class CHMTopics implements functionality to decode the #TOPICS internal file /// internal sealed class CHMTopics : IDisposable { /// /// 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 associated chmfile object /// private CHMFile _associatedFile = null; /// /// Internal member storing the topic list /// private ArrayList _topicTable = new ArrayList(); /// /// Constructor of the class /// /// binary file data of the #TOPICS file /// associated chm file public CHMTopics(byte[] binaryFileData, CHMFile associatedFile) { _binaryFileData = binaryFileData; _associatedFile = associatedFile; DecodeData(); // clear internal binary data after extraction _binaryFileData = null; } /// /// Standard constructor /// internal CHMTopics() { } #region Data dumping /// /// Dump the class data to a binary writer /// /// writer to write the data internal void Dump(ref BinaryWriter writer) { writer.Write( _topicTable.Count ); foreach(TopicEntry curItem in _topicTable) { curItem.Dump(ref writer); } } /// /// 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; foreach(TopicEntry curEntry in _topicTable) { curEntry.SetCHMFile(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) ) { int entryOffset = nCurOffset; int tocIdx = binReader.ReadInt32(); int titleOffset = binReader.ReadInt32(); int urltablOffset = binReader.ReadInt32(); int visibilityMode = binReader.ReadInt16(); int unknownMode = binReader.ReadInt16(); TopicEntry newEntry = new TopicEntry(entryOffset, tocIdx, titleOffset, urltablOffset, visibilityMode, unknownMode, _associatedFile); _topicTable.Add( newEntry ); nCurOffset = (int)memStream.Position; } return bRet; } /// /// Gets the arraylist containing all topic entries. /// public ArrayList TopicTable { get { return _topicTable; } } /// /// Gets the topic entry of a given offset /// public TopicEntry this[int offset] { get { foreach(TopicEntry curEntry in _topicTable) if(curEntry.EntryOffset == offset) return curEntry; return null; } } /// /// Searches a topic by the locale name /// /// locale name to search /// The topicentry instance if found, otherwise null public TopicEntry GetByLocale(string locale) { foreach(TopicEntry curEntry in TopicTable) { if(curEntry.Locale.ToLower() == locale.ToLower()) return curEntry; } return null; } /// /// Searches the topics for all files with a given file extension /// /// extension to search /// An arraylist of TopicEntry instances or null if no topic was found public ArrayList GetByExtension(string fileExtension) { ArrayList arrRet = new ArrayList(); foreach(TopicEntry curEntry in TopicTable) { if(curEntry.Locale.ToLower().EndsWith(fileExtension.ToLower())) arrRet.Add(curEntry); } if(arrRet.Count > 0) return arrRet; return null; } /// /// 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; _topicTable=null; } } disposed = true; } } }