using System; using System.Diagnostics; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Text; using System.Runtime.InteropServices; using System.Globalization; // using HtmlHelp.Storage; namespace HtmlHelp.ChmDecoding { /// <summary> /// Internal enumeration for specifying the type of the html-help file /// </summary> internal enum HtmlHelpFileType { /// <summary> /// CHM - compiled contents file /// </summary> /// <remarks>A file with this extension must always exist. If the file would be too long, some parts /// can be splitted into the filestypes below.</remarks> CHM = 0, /// <summary> /// CHI - compiled system file /// </summary> CHI = 1, /// <summary> /// CHQ - compiled fulltext search file /// </summary> CHQ = 2, /// <summary> /// CHW - compiled index file /// </summary> CHW = 3 } /// <summary> /// The class <c>CHMFile</c> implemts methods and properties for handling a single chmfile. /// </summary> public sealed class CHMFile : IDisposable { /// <summary> /// Internal member storing a reference to the hosting HtmlHelpSystem instance /// </summary> private HtmlHelpSystem _systemInstance = null; /// <summary> /// Internal flag specifying if only system data has been loaded /// </summary> private bool _onlySystem = false; /// <summary> /// Internal flag specifying if the object is going to be disposed /// </summary> private bool disposed = false; /// <summary> /// Internal arraylist containing the table of contents /// </summary> private ArrayList _toc = new ArrayList(); /// <summary> /// Internal arraylist containing items of the toc which are merge-Links /// </summary> private ArrayList _mergeLinks = new ArrayList(); /// <summary> /// Internal member storing the read information types /// </summary> private ArrayList _informationTypes = new ArrayList(); /// <summary> /// Internal member storing the read categories /// </summary> private ArrayList _categories = new ArrayList(); /// <summary> /// Internal arraylist containing the index (klinks) /// </summary> private ArrayList _indexKLinks = new ArrayList(); /// <summary> /// Internal arraylist containing the index (alinks) /// </summary> private ArrayList _indexALinks = new ArrayList(); /// <summary> /// Internal member storing the full filename /// </summary> private string _chmFileName = ""; /// <summary> /// Internal member storing the full filename of the chi-file (includes all system files) /// The file name is zero-length if there is no chi-file /// </summary> private string _chiFileName = ""; /// <summary> /// Internal member storing the full filename of the chw-file (includes the help index) /// The file name is zero-length if there is no chw-file /// </summary> private string _chwFileName = ""; /// <summary> /// Internal member storing the full filename of the chq-file (includes the fulltext contents) /// The file name is zero-length if there is no chq-file /// </summary> private string _chqFileName = ""; /// <summary> /// Internal member storing the decoded information from the internal #SYSTEM file /// </summary> private CHMSystem _systemFile = null; /// <summary> /// Internal member storing the decoded information from the internal #IDXHDR file /// </summary> private CHMIdxhdr _idxhdrFile = null; /// <summary> /// Internal member storing the decoded information from the internal #STRINGS file /// </summary> private CHMStrings _stringsFile = null; /// <summary> /// Internal member storing the decoded information from the internal #URLSTR file /// </summary> private CHMUrlstr _urlstrFile = null; /// <summary> /// Internal member storing the decoded information from the internal #URLTBL file /// </summary> private CHMUrltable _urltblFile = null; /// <summary> /// Internal member storing the decoded information from the internal #TOPICS file /// </summary> private CHMTopics _topicsFile = null; /// <summary> /// Internal member storing the decoded information from the internal #TOCIDX file /// </summary> private CHMTocidx _tocidxFile = null; /// <summary> /// Internal member storing the decoded information from the internal binary index file (KLinks). /// </summary> private CHMBtree _kLinks = null; /// <summary> /// Internal member storing the decoded information from the internal binary index file (ALinks). /// </summary> private CHMBtree _aLinks = null; /// <summary> /// Internal member storing the fulltext searcher for this file /// </summary> private FullTextEngine _ftSearcher = null; /// <summary> /// Internal member storing the default encoder /// </summary> private Encoding _textEncoding = Encoding.GetEncoding(1252); // standard windows-1252 encoder /// <summary> /// Internal memebr storing the chm file info /// </summary> private ChmFileInfo _chmFileInfo = null; /// <summary> /// Internal flag specifying if the dump must be written (if enabled) /// </summary> private bool _mustWriteDump = false; /// <summary> /// Internal flag specifying if data was read using the dump /// </summary> private bool _dumpRead = false; /// <summary> /// Internal member for specifying the number of dump-reading trys. /// If dump-reading fails, this is used that it will not be opened a second time /// (in CHM-Systems with CHM, CHI, etc. files) /// </summary> private int _dumpReadTrys = 0; /// <summary> /// Internal member storing the dumping info instance /// </summary> private DumpingInfo _dmpInfo = null; private CHMStream.CHMStream _currentWrapper; private CHMStream.CHMStream _baseStream=null; public CHMStream.CHMStream BaseStream { get { if (_baseStream==null) _baseStream=new CHMStream.CHMStream(this.ChmFilePath); return _baseStream; } } /// <summary> /// Creates a new instance of the class /// </summary> /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param> /// <param name="chmFile">chm file to read</param> public CHMFile(HtmlHelpSystem systemInstance, string chmFile) : this(systemInstance, chmFile, false, null) { } /// <summary> /// Creates a new instance of the class /// </summary> /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param> /// <param name="chmFile">chm file to read</param> /// <param name="dmpInfo">A dumping info class</param> public CHMFile(HtmlHelpSystem systemInstance, string chmFile, DumpingInfo dmpInfo) : this(systemInstance, chmFile, false, dmpInfo) { } /// <summary> /// Creates a new instance of the class /// </summary> /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param> /// <param name="chmFile">chm file to read</param> /// <param name="onlySystemData">true if only system data should be extracted (no index or toc)</param> internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData) : this(systemInstance, chmFile, onlySystemData, null) { } /// <summary> /// Creates a new instance of the class /// </summary> /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param> /// <param name="chmFile">chm file to read</param> /// <param name="onlySystemData">true if only system data should be extracted (no index or toc)</param> /// <param name="dmpInfo">A dumping info class</param> internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData, DumpingInfo dmpInfo) { _systemInstance = systemInstance; _dumpReadTrys=0; _dmpInfo = dmpInfo; if(dmpInfo != null) { dmpInfo.ChmFile = this; } if( ! chmFile.ToLower().EndsWith(".chm") ) { throw new ArgumentException("HtmlHelp file must have the extension .chm !", "chmFile"); } _chmFileName = chmFile; _chiFileName = ""; // Read the IStorage file system if( File.Exists(chmFile) ) { _onlySystem = onlySystemData; DateTime dtStartHH = DateTime.Now; string sCHIName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chi"; string sCHQName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chq"; string sCHWName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chw"; // If there is a CHI file present (this file includes the internal system files for the current chm file) if( File.Exists(sCHIName) ) { _chiFileName = sCHIName; ReadFile(_chiFileName, HtmlHelpFileType.CHI); } // If there is a CHW file present (this file includes the internal binary index of the help) if(( File.Exists(sCHWName) ) && (!_onlySystem) ) { _chwFileName = sCHWName; ReadFile(_chwFileName, HtmlHelpFileType.CHW); } // If there is a CHQ file present (this file includes the fulltext-search data) if(( File.Exists(sCHQName) ) && (!_onlySystem) ) { _chqFileName = sCHQName; ReadFile(_chqFileName, HtmlHelpFileType.CHQ); } ReadFile(chmFile, HtmlHelpFileType.CHM); if(_mustWriteDump) { _mustWriteDump = !SaveDump(dmpInfo); } // check the default-topic setting if(_systemFile.DefaultTopic.Length > 0) { CHMStream.CHMStream iw=null; iw = new CHMStream.CHMStream(chmFile); _currentWrapper=iw; // tryo to open the topic file MemoryStream fileObject = iw.OpenStream( _systemFile.DefaultTopic); if( fileObject != null) { // if succeed, the topic default topic is OK fileObject.Close(); } else { // set the first topic of the toc-tree as default topic if(_toc.Count > 0) { _systemFile.SetDefaultTopic( ((TOCItem) _toc[0]).Local ); } } _currentWrapper=null; } else { // set the first topic of the toc-tree as default topic if(_toc.Count > 0) { _systemFile.SetDefaultTopic( ((TOCItem) _toc[0]).Local ); } } _chmFileInfo = new ChmFileInfo(this); } else { throw new ArgumentException("File '" + chmFile + "' not found !", "chmFile"); } } /// <summary> /// Read a IStorage file /// </summary> /// <param name="fname">filename</param> /// <param name="type">type of file</param> private void ReadFile(string fname, HtmlHelpFileType type) { CHMStream.CHMStream iw=null; iw=new CHMStream.CHMStream(); iw.OpenCHM(fname); _currentWrapper=iw; MemoryStream fileObject=null; // ITStorageWrapper iw = null; // Open the internal chm system files and parse their content // FileObject fileObject = null; // iw = new ITStorageWrapper(fname, false); if( (type != HtmlHelpFileType.CHQ) && (type != HtmlHelpFileType.CHW) ) { fileObject = iw.OpenStream("#SYSTEM"); if ((fileObject != null) && (fileObject.Length>0)) _systemFile = new CHMSystem(fileObject.ToArray(), this); fileObject = iw.OpenStream("#IDXHDR"); if ((fileObject != null) && (fileObject.Length>0)) _idxhdrFile = new CHMIdxhdr(fileObject.ToArray(), this); // try to read Dump if((!_dumpRead)&&(CheckDump(_dmpInfo))&&(_dumpReadTrys==0)) { _dumpReadTrys++; _dumpRead = LoadDump(_dmpInfo); } if( (!_dumpRead)||(!_dmpInfo.DumpStrings) ) { fileObject = iw.OpenStream( "#STRINGS"); if ((fileObject != null) && (fileObject.Length>0)) _stringsFile = new CHMStrings(fileObject.ToArray(), this); } if( (!_dumpRead)||(!_dmpInfo.DumpUrlStr) ) { fileObject = iw.OpenStream( "#URLSTR"); if ((fileObject != null) && (fileObject.Length>0)) _urlstrFile = new CHMUrlstr(fileObject.ToArray(), this); } if( (!_dumpRead)||(!_dmpInfo.DumpUrlTbl) ) { fileObject = iw.OpenStream( "#URLTBL"); if ((fileObject != null) && (fileObject.Length>0)) _urltblFile = new CHMUrltable(fileObject.ToArray(), this); } if( (!_dumpRead)||(!_dmpInfo.DumpTopics) ) { fileObject = iw.OpenStream( "#TOPICS"); if ((fileObject != null) && (fileObject.Length>0)) _topicsFile = new CHMTopics(fileObject.ToArray(), this); } if(!_onlySystem) { if( (!_dumpRead)||(!_dmpInfo.DumpBinaryTOC) ) { fileObject = iw.OpenStream( "#TOCIDX"); if( (fileObject != null) && (fileObject.Length>0) && (_systemFile.BinaryTOC) ) { _tocidxFile = new CHMTocidx(fileObject.ToArray(), this); _toc = _tocidxFile.TOC; } } } if( (_systemFile != null) && (!_onlySystem) ) { if(!_systemFile.BinaryTOC) { if( (!_dumpRead)||(!_dmpInfo.DumpTextTOC) ) { CHMStream.chmUnitInfo HHCInfo=iw.GetFileInfoByExtension(".hhc"); if (HHCInfo!=null) { fileObject = iw.OpenStream(HHCInfo); if ((fileObject != null) && (fileObject.Length>0)) { ASCIIEncoding ascii=new ASCIIEncoding(); string fileString =ascii.GetString(fileObject.ToArray(),0,(int)fileObject.Length); _toc = HHCParser.ParseHHC(fileString, this); } if(HHCParser.HasMergeLinks) _mergeLinks = HHCParser.MergeItems; } } } } } if( type != HtmlHelpFileType.CHQ ) // no index information in CHQ files (only fulltext search) { if( (_systemFile != null) && (!_onlySystem) ) { if( ! _systemFile.BinaryIndex ) { if( (!_dumpRead)||(!_dmpInfo.DumpTextIndex) ) { fileObject = iw.OpenStream( _systemFile.IndexFile); if ((fileObject != null) && (fileObject.Length>0)) { string fileString = this.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length); // string fileString = fileObject.ReadFromFile(this.TextEncoding); fileObject.Close(); _indexKLinks = HHKParser.ParseHHK(fileString, this); _indexKLinks.Sort(); } } } else { if( (!_dumpRead)||(!_dmpInfo.DumpBinaryIndex) ) { fileObject=iw.OpenStream(@"$WWKeywordLinks\BTree"); if ((fileObject != null) && (fileObject.Length>0)) { _kLinks = new CHMBtree(fileObject.ToArray(), this); _indexKLinks = _kLinks.IndexList; } fileObject =iw.OpenStream(@"$WWAssociativeLinks\BTree"); if ((fileObject != null) && (fileObject.Length>0)) { _aLinks = new CHMBtree(fileObject.ToArray(), this); _indexALinks = _aLinks.IndexList; _indexALinks.Sort(); } } } } } if( (type != HtmlHelpFileType.CHI) && (type != HtmlHelpFileType.CHW) ) { if( (_systemFile != null) && (!_onlySystem) ) { if( _systemFile.FullTextSearch) { if( (!_dumpRead)||(!_dmpInfo.DumpFullText) ) { fileObject = iw.OpenStream("$FIftiMain"); if(( fileObject != null) && (fileObject .Length>0)) _ftSearcher = new FullTextEngine(fileObject .ToArray(), this); } } } } _currentWrapper=null; } /// <summary> /// Enumerates the files in the chm storage and gets all files matching a given extension. /// </summary> /// <param name="extension">extension to return</param> /// <returns>Returns an arraylist of filenames or null if nothing found</returns> /// <remarks>On large CHMs, enumerations are very slow. Only use it if necessary !</remarks> internal ArrayList EnumFilesByExtension(string extension) { ArrayList arrRet = new ArrayList(); CHMStream.CHMStream iw = null; if(_currentWrapper == null) iw = new CHMStream.CHMStream(_chmFileName); else iw = _currentWrapper; arrRet=iw.GetFileListByExtenstion(extension); if(arrRet.Count > 0) return arrRet; return null; } /// <summary> /// Searches an TOC entry using the local /// </summary> /// <param name="local">local to search</param> /// <returns>Returns the TOC item</returns> internal TOCItem GetTOCItemByLocal(string local) { return GetTOCItemByLocal(this.TOC, local); } /// <summary> /// Recursively searches an TOC entry using its local /// </summary> /// <param name="arrTOC">toc level list</param> /// <param name="local">local to search</param> /// <returns>Returns the TOC item</returns> private TOCItem GetTOCItemByLocal(ArrayList arrTOC, string local) { TOCItem ret = null; foreach(TOCItem curItem in arrTOC) { string scL = curItem.Local.ToLower(); string sL = local.ToLower(); while(scL[0]=='/') // delete prefixing '/' scL = scL.Substring(1); while(sL[0]=='/') // delete prefixing '/' sL = sL.Substring(1); if(scL == sL) return curItem; if(curItem.Children.Count > 0) { ret = GetTOCItemByLocal(curItem.Children, local); if(ret != null) return ret; } } return ret; } /// <summary> /// Removes a TOCItem from the toc /// </summary> /// <param name="rem">item to remove</param> /// <returns>Returns true if removed</returns> internal bool RemoveTOCItem(TOCItem rem) { if(rem == null) return false; return RemoveTOCItem(this.TOC, rem); } /// <summary> /// Recursively searches a TOCItem and removes it if found /// </summary> /// <param name="arrTOC">toc level list</param> /// <param name="rem">item to remove</param> /// <returns>Returns true if removed</returns> private bool RemoveTOCItem(ArrayList arrTOC, TOCItem rem) { for(int i=0; i<arrTOC.Count;i++) { TOCItem curItem = arrTOC[i] as TOCItem; if(curItem == rem) { arrTOC.RemoveAt(i); return true; } if(curItem.Children.Count > 0) { bool bRem = RemoveTOCItem(curItem.Children, rem); if(bRem) return true; } } return false; } /// <summary> /// Returns true if the HtmlHelpSystem instance contains 1 or more information types /// </summary> public bool HasInformationTypes { get { return (_informationTypes.Count>0); } } /// <summary> /// Returns true if the HtmlHelpSystem instance contains 1 or more categories /// </summary> public bool HasCategories { get { return (_categories.Count>0); } } /// <summary> /// Gets an ArrayList of <see cref="InformationType">InformationType</see> items /// </summary> public ArrayList InformationTypes { get { return _informationTypes; } } /// <summary> /// Gets an ArrayList of <see cref="Category">Category</see> items /// </summary> public ArrayList Categories { get { return _categories; } } /// <summary> /// Gets the information type specified by its name /// </summary> /// <param name="name">name of the information type to receive</param> /// <returns>Returns the Instance for the name or null if not found</returns> public InformationType GetInformationType(string name) { if(HasInformationTypes) { for(int i=0; i<_informationTypes.Count;i++) { InformationType iT = _informationTypes[i] as InformationType; if(iT.Name == name) return iT; } } return null; } /// <summary> /// Gets the category specifiyd by its name /// </summary> /// <param name="name">name of the category</param> /// <returns>Returns the Instance for the name or null if not found</returns> public Category GetCategory(string name) { if(HasCategories) { for(int i=0; i<_categories.Count;i++) { Category cat = _categories[i] as Category; if(cat.Name == name) return cat; } } return null; } #region Dumping methods /// <summary> /// Checks if a dump for this file exists and if it can be read /// </summary> /// <param name="dmpInfo">dumping info class</param> /// <returns>true if it can be read</returns> private bool CheckDump(DumpingInfo dmpInfo) { if(_dumpReadTrys<=0) _mustWriteDump = false; if(_onlySystem) return false; if( dmpInfo != null ) { if(_dumpReadTrys > 0) return _mustWriteDump; _mustWriteDump = !dmpInfo.DumpExists; return !_mustWriteDump; } return false; } /// <summary> /// Saves the the toc and index into a data dump /// </summary> /// <param name="dmpInfo">dumping info</param> /// <returns>true if succeed</returns> private bool SaveDump(DumpingInfo dmpInfo) { if(dmpInfo == null) return false; bool bRet = false; BinaryWriter writer = dmpInfo.Writer; int nCnt = 0; try { Debug.WriteLine("writing dump-file header"); FileInfo fi = new FileInfo(_chmFileName); string ftime = fi.LastWriteTime.ToString("dd.MM.yyyy HH:mm:ss.ffffff"); writer.Write("HtmlHelpSystem dump file 1.0"); writer.Write(ftime); writer.Write(_textEncoding.CodePage); // strings dumping if(dmpInfo.DumpStrings) { writer.Write(true); // data should be in dump if(_stringsFile==null) { writer.Write(false); // data not supported by the chm } else { Debug.WriteLine("writing #STRINGS"); writer.Write(true); // data supported and following _stringsFile.Dump(ref writer); } } else { writer.Write(false); // data is not in dump } // urlstr dumping if(dmpInfo.DumpUrlStr) { writer.Write(true); if(_urlstrFile==null) { writer.Write(false); } else { Debug.WriteLine("writing #URLSTR"); writer.Write(true); _urlstrFile.Dump(ref writer); } } else { writer.Write(false); } // urltbl dumping if(dmpInfo.DumpUrlTbl) { writer.Write(true); if(_urltblFile==null) { writer.Write(false); } else { Debug.WriteLine("writing #URLTBL"); writer.Write(true); _urltblFile.Dump(ref writer); } } else { writer.Write(false); } // topics dumping if(dmpInfo.DumpTopics) { writer.Write(true); if(_topicsFile==null) { writer.Write(false); } else { Debug.WriteLine("writing #TOPICS"); writer.Write(true); _topicsFile.Dump(ref writer); } } else { writer.Write(false); } // ftsearch dumping if(dmpInfo.DumpFullText) { writer.Write(true); if(_ftSearcher==null) { writer.Write(false); } else { Debug.WriteLine("writing $FIftiMain"); writer.Write(true); _ftSearcher.Dump(ref writer); } } else { writer.Write(false); } // TOC dumping bool bWriteTOC = false; if( (_systemFile.BinaryTOC) && (dmpInfo.DumpBinaryTOC) ) { Debug.WriteLine("writing binary TOC"); bWriteTOC = true; } if( (!_systemFile.BinaryTOC) && (dmpInfo.DumpTextTOC) ) { Debug.WriteLine("writing text-based TOC"); bWriteTOC = true; } writer.Write(bWriteTOC); if(bWriteTOC) { // write table of contents writer.Write( _toc.Count ); for(nCnt=0; nCnt < _toc.Count; nCnt++) { TOCItem curItem = ((TOCItem)(_toc[nCnt])); curItem.Dump( ref writer ); } } // Index dumping bool bWriteIdx = false; if( (_systemFile.BinaryIndex) && (dmpInfo.DumpBinaryIndex) ) { Debug.WriteLine("writing binary index"); bWriteIdx = true; } if( (!_systemFile.BinaryIndex) && (dmpInfo.DumpTextIndex) ) { Debug.WriteLine("writing text-based index"); bWriteIdx = true; } writer.Write(bWriteIdx); if(bWriteIdx) { // write index writer.Write( _indexALinks.Count ); for(nCnt=0; nCnt < _indexALinks.Count; nCnt++) { IndexItem curItem = ((IndexItem)(_indexALinks[nCnt])); curItem.Dump( ref writer ); } writer.Write( _indexKLinks.Count ); for(nCnt=0; nCnt < _indexKLinks.Count; nCnt++) { IndexItem curItem = ((IndexItem)(_indexKLinks[nCnt])); curItem.Dump( ref writer ); } } // Information types dumping writer.Write( _informationTypes.Count ); Debug.WriteLine("writing " + _informationTypes.Count.ToString() + " information types"); for(nCnt=0; nCnt<_informationTypes.Count;nCnt++) { InformationType curType = _informationTypes[nCnt] as InformationType; curType.Dump(ref writer); } // Categories dumping writer.Write( _categories.Count ); Debug.WriteLine("writing " + _categories.Count.ToString() + " categories"); for(nCnt=0; nCnt<_categories.Count; nCnt++) { Category curCat = _categories[nCnt] as Category; curCat.Dump( ref writer); } bRet=true; } finally { dmpInfo.SaveData(); } return bRet; } /// <summary> /// Parses a HHC file which is located in the current CHM. /// </summary> /// <param name="hhcFile">hhc file to parse</param> /// <returns>an arraylist with toc items</returns> public ArrayList ParseHHC(string hhcFile) { ArrayList arrRet = new ArrayList(); CHMStream.CHMStream iw=null; iw=new CHMStream.CHMStream(); iw.OpenCHM(_chmFileName); MemoryStream fileObject=null; fileObject = iw.OpenStream(hhcFile); if( fileObject != null) { ASCIIEncoding ascii=new ASCIIEncoding(); string fileString =ascii.GetString(fileObject.ToArray(),0,(int)fileObject.Length); fileObject.Close(); arrRet = HHCParser.ParseHHC(fileString, this); if(HHCParser.HasMergeLinks) { foreach(TOCItem curItem in HHCParser.MergeItems) { _mergeLinks.Add(curItem); } } } return arrRet; } /// <summary> /// Loads the toc and index from a data dump /// </summary> /// <param name="dmpInfo">dumping info</param> /// <returns>true if succeed</returns> private bool LoadDump(DumpingInfo dmpInfo) { if(dmpInfo == null) return false; bool bRet = false; try { BinaryReader reader = dmpInfo.Reader; if(reader == null) { Debug.WriteLine("No reader returned !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; } int nCnt = 0; Debug.WriteLine("reading dump-file header"); FileInfo fi = new FileInfo(_chmFileName); string ftime = fi.LastWriteTime.ToString("dd.MM.yyyy HH:mm:ss.ffffff"); string header = reader.ReadString(); if( header != "HtmlHelpSystem dump file 1.0") { Debug.WriteLine("Unsupported dump-file format !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; } string ftimecheck = reader.ReadString(); reader.ReadInt32(); // codepage, we'll use the same as for the chm file which is already set. // if(ftimecheck != ftime) // { // Debug.WriteLine("Dump is out of date (CHM file changed during last dump creation) !"); // dmpInfo.SaveData(); // closes the dump // _mustWriteDump = true; // return false; // force reload // } bool bFlag=false; // true if data should be in dump bool bFlagSupp=false; // false if data is not supported by the chm bFlag = reader.ReadBoolean(); if(bFlag) { bFlagSupp = reader.ReadBoolean(); if(!dmpInfo.DumpStrings) { Debug.WriteLine("Dumped #STRINGS found but not expected !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(bFlagSupp) { Debug.WriteLine("reading #STRINGS"); _stringsFile = new CHMStrings(); _stringsFile.SetCHMFile(this); _stringsFile.ReadDump(ref reader); } } else { if(dmpInfo.DumpStrings) { Debug.WriteLine("Dumped #STRINGS expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } bFlag = reader.ReadBoolean(); if(bFlag) { bFlagSupp = reader.ReadBoolean(); if(!dmpInfo.DumpUrlStr) { Debug.WriteLine("Dumped #URLSTR found but not expected !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(bFlagSupp) { Debug.WriteLine("reading #URLSTR"); _urlstrFile = new CHMUrlstr(); _urlstrFile.SetCHMFile(this); _urlstrFile.ReadDump(ref reader); } } else { if(dmpInfo.DumpUrlStr) { Debug.WriteLine("Dumped #URLSTR expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } bFlag = reader.ReadBoolean(); if(bFlag) { bFlagSupp = reader.ReadBoolean(); if(!dmpInfo.DumpUrlTbl) { Debug.WriteLine("Dumped #URLTBL found but not expected !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(bFlagSupp) { Debug.WriteLine("reading #URLTBL"); _urltblFile = new CHMUrltable(); _urltblFile.SetCHMFile(this); _urltblFile.ReadDump(ref reader); } } else { if(dmpInfo.DumpUrlTbl) { Debug.WriteLine("Dumped #URLTBL expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } bFlag = reader.ReadBoolean(); if(bFlag) { bFlagSupp = reader.ReadBoolean(); if(!dmpInfo.DumpTopics) { Debug.WriteLine("Dumped #TOPICS found but not expected !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(bFlagSupp) { Debug.WriteLine("reading #TOPICS"); _topicsFile = new CHMTopics(); _topicsFile.SetCHMFile(this); _topicsFile.ReadDump(ref reader); } } else { if(dmpInfo.DumpTopics) { Debug.WriteLine("Dumped #TOPICS expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } bFlag = reader.ReadBoolean(); if(bFlag) { bFlagSupp = reader.ReadBoolean(); if(!dmpInfo.DumpFullText) { Debug.WriteLine("Dumped $FIftiMain found but not expected !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(bFlagSupp) { Debug.WriteLine("reading $FIftiMain"); _ftSearcher = new FullTextEngine(); _ftSearcher.SetCHMFile(this); _ftSearcher.ReadDump(ref reader); } } else { if(dmpInfo.DumpFullText) { Debug.WriteLine("Dumped $FIftiMain expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } // read table of contents _toc.Clear(); bFlag = reader.ReadBoolean(); if(bFlag) { if((_systemFile.BinaryTOC)&&(!dmpInfo.DumpBinaryTOC)) { Debug.WriteLine("Binary TOC expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if((!_systemFile.BinaryTOC)&&(!dmpInfo.DumpTextTOC)) { Debug.WriteLine("Text-based TOC expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(_systemFile.BinaryTOC) Debug.WriteLine("reading binary TOC"); else Debug.WriteLine("reading text-based TOC"); int nTocCnt = reader.ReadInt32(); for(nCnt=0; nCnt < nTocCnt; nCnt++) { TOCItem item = new TOCItem(); item.AssociatedFile = this; item.ChmFile = _chmFileName; item.ReadDump(ref reader); if(item.MergeLink.Length > 0) _mergeLinks.Add(item); _toc.Add(item); } } else { if((_systemFile.BinaryTOC)&&(dmpInfo.DumpBinaryTOC)) { Debug.WriteLine("Binary TOC expected but no TOC dump !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if((!_systemFile.BinaryTOC)&&(dmpInfo.DumpTextTOC)) { Debug.WriteLine("Text-based TOC expected but no TOC dump !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } // read index _indexALinks.Clear(); _indexKLinks.Clear(); bFlag = reader.ReadBoolean(); if(bFlag) { if((_systemFile.BinaryIndex)&&(!dmpInfo.DumpBinaryIndex)) { Debug.WriteLine("Binary index expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if((!_systemFile.BinaryIndex)&&(!dmpInfo.DumpTextIndex)) { Debug.WriteLine("Binary index expected but not found !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if(_systemFile.BinaryIndex) Debug.WriteLine("reading binary index"); else Debug.WriteLine("reading text-based index"); int nIndxaCnt = reader.ReadInt32(); for(nCnt=0; nCnt < nIndxaCnt; nCnt++) { IndexItem item = new IndexItem(); item.ChmFile = this; item.ReadDump(ref reader); _indexALinks.Add(item); } int nIndxkCnt = reader.ReadInt32(); for(nCnt=0; nCnt < nIndxkCnt; nCnt++) { IndexItem item = new IndexItem(); item.ChmFile = this; item.ReadDump(ref reader); _indexKLinks.Add(item); } } else { if((_systemFile.BinaryIndex)&&(dmpInfo.DumpBinaryIndex)) { Debug.WriteLine("Binary index expected but no index in dump !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } if((!_systemFile.BinaryIndex)&&(dmpInfo.DumpTextIndex)) { Debug.WriteLine("Text-based index expected but no index dump !"); dmpInfo.SaveData(); // closes the dump _mustWriteDump = true; return false; // force reload } } // read information types from dump int nITCnt = reader.ReadInt32(); Debug.WriteLine("Reading " + nITCnt.ToString() + " information types from dump !"); for(nCnt=0; nCnt<nITCnt; nCnt++) { InformationType newType = new InformationType(); newType.ReadDump(ref reader); if( SystemInstance.GetInformationType( newType.Name ) != null) { // information type of this name already exists in the helpsystem InformationType sysType = SystemInstance.GetInformationType( newType.Name ); _informationTypes.Add(sysType); } else { _informationTypes.Add( newType ); } } // read categories from dump int nCCnt = reader.ReadInt32(); Debug.WriteLine("Reading " + nITCnt.ToString() + " categories from dump !"); for(nCnt=0; nCnt<nCCnt; nCnt++) { Category newCat = new Category(); newCat.ReadDump(ref reader, this); if( SystemInstance.GetCategory( newCat.Name ) != null) { // category of this name already exists in the helpsystem Category sysCat = SystemInstance.GetCategory( newCat.Name ); sysCat.MergeInfoTypes( newCat ); _categories.Add( sysCat ); } else { _categories.Add( newCat ); } } _dumpRead = true; bRet = true; } catch(Exception ex) { Debug.WriteLine("###ERROR :" + ex.Message); _mustWriteDump = true; } finally { dmpInfo.SaveData(); // closes the dump } return bRet; } #endregion #region Internal properties /// <summary> /// Gets the current storage wrapper. /// </summary> /// <remarks>This property will return not null, if there are currently file read actions running !</remarks> internal CHMStream.CHMStream CurrentStorageWrapper { get { return _currentWrapper;} } /// <summary> /// Gets/sets the hosting HtmlHelpSystem instance /// </summary> internal HtmlHelpSystem SystemInstance { get { return _systemInstance; } set { _systemInstance = value; } } /// <summary> /// Gets an arraylist of TOC items which contains merg-links to other CHMs /// </summary> internal ArrayList MergLinks { get { return _mergeLinks; } } /// <summary> /// Gets the internal system file instance /// </summary> internal CHMSystem SystemFile { get { return _systemFile; } } /// <summary> /// Gets the internal idxhdr file instance /// </summary> internal CHMIdxhdr IdxHdrFile { get { return _idxhdrFile; } } /// <summary> /// Gets the internal strings file instance /// </summary> internal CHMStrings StringsFile { get { return _stringsFile; } } /// <summary> /// Gets the internal urlstr file instance /// </summary> internal CHMUrlstr UrlstrFile { get { return _urlstrFile; } } /// <summary> /// Gets the internal urltbl file instance /// </summary> internal CHMUrltable UrltblFile { get { return _urltblFile; } } /// <summary> /// Gets the internal topics file instance /// </summary> internal CHMTopics TopicsFile { get { return _topicsFile; } } /// <summary> /// Gets the internal tocidx file instance /// </summary> internal CHMTocidx TocidxFile { get { return _tocidxFile; } } /// <summary> /// Gets the internal btree file instance for alinks /// </summary> internal CHMBtree ALinksFile { get { return _aLinks; } } /// <summary> /// Gets the internal btree file instance for klinks /// </summary> internal CHMBtree KLinksFile { get { return _kLinks; } } /// <summary> /// Gets/Sets the text encoding /// </summary> internal Encoding TextEncoding { get { return _textEncoding; } set { _textEncoding = value; } } #endregion #region Properties from the #SYSTEM file /// <summary> /// Gets the file version of the chm file. /// 2 for Compatibility=1.0, 3 for Compatibility=1.1 /// </summary> internal int FileVersion { get { if(_systemFile==null) return 0; return _systemFile.FileVersion; } } /// <summary> /// Gets the contents file name /// </summary> internal string ContentsFile { get { if(_systemFile==null) return String.Empty; return _systemFile.ContentsFile; } } /// <summary> /// Gets the index file name /// </summary> internal string IndexFile { get { if(_systemFile==null) return String.Empty; return _systemFile.IndexFile; } } /// <summary> /// Gets the default help topic /// </summary> internal string DefaultTopic { get { if(_systemFile==null) return String.Empty; return _systemFile.DefaultTopic; } } /// <summary> /// Gets the title of the help window /// </summary> internal string HelpWindowTitle { get { if(_systemFile==null) return String.Empty; return _systemFile.Title; } } /// <summary> /// Gets the flag if DBCS is in use /// </summary> internal bool DBCS { get { if(_systemFile==null) return false; return _systemFile.DBCS; } } /// <summary> /// Gets the flag if full-text-search is available /// </summary> internal bool FullTextSearch { get { if(_systemFile==null) return false; return _systemFile.FullTextSearch; } } /// <summary> /// Gets the flag if the file has ALinks /// </summary> internal bool HasALinks { get { if(_systemFile==null) return false; return _systemFile.HasALinks; } } /// <summary> /// Gets the flag if the file has KLinks /// </summary> internal bool HasKLinks { get { if(_systemFile==null) return false; return _systemFile.HasKLinks; } } /// <summary> /// Gets the default window name /// </summary> internal string DefaultWindow { get { if(_systemFile==null) return String.Empty; return _systemFile.DefaultWindow; } } /// <summary> /// Gets the file name of the compile file /// </summary> internal string CompileFile { get { if(_systemFile==null) return String.Empty; return _systemFile.CompileFile; } } /// <summary> /// Gets the flag if the chm has a binary index file /// </summary> internal bool BinaryIndex { get { if(_systemFile==null) return false; return _systemFile.BinaryIndex; } } /// <summary> /// Gets the flag if the chm has a binary index file /// </summary> internal string CompilerVersion { get { if(_systemFile==null) return String.Empty; return _systemFile.CompilerVersion; } } /// <summary> /// Gets the flag if the chm has a binary toc file /// </summary> internal bool BinaryTOC { get { if(_systemFile==null) return false; return _systemFile.BinaryTOC; } } /// <summary> /// Gets the font face of the read font property. /// Empty string for default font. /// </summary> internal string FontFace { get { if(_systemFile==null) return ""; return _systemFile.FontFace; } } /// <summary> /// Gets the font size of the read font property. /// 0 for default font size /// </summary> internal double FontSize { get { if(_systemFile==null) return 0; return _systemFile.FontSize; } } /// <summary> /// Gets the character set of the read font property /// 1 for default /// </summary> internal int CharacterSet { get { if(_systemFile==null) return 1; return _systemFile.CharacterSet; } } /// <summary> /// Gets the codepage depending on the read font property /// </summary> internal int CodePage { get { if(_systemFile==null) return CultureInfo.CurrentCulture.TextInfo.ANSICodePage; return _systemFile.CodePage; } } /// <summary> /// Gets the assiciated culture info /// </summary> internal CultureInfo Culture { get { if(_systemFile==null) return CultureInfo.CurrentCulture; return _systemFile.Culture; } } #endregion #region Properties from the #IDXHDR file /// <summary> /// Gets the number of topic nodes including the contents and index files /// </summary> internal int NumberOfTopicNodes { get { if(_idxhdrFile==null) return 0; return _idxhdrFile.NumberOfTopicNodes; } } /// <summary> /// Gets the ImageList string specyfied in the #IDXHDR file. /// </summary> /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks> internal string ImageList { get { if( (_stringsFile == null) || (_idxhdrFile == null) ) return ""; return _stringsFile[ _idxhdrFile.ImageListOffset ]; } } /// <summary> /// True if the value of the ImageType param of the /// "text/site properties" object of the sitemap contents is "Folder". /// </summary> /// <remarks>If this is set to true, the help will display folders instead of books</remarks> internal bool ImageTypeFolder { get { if(_idxhdrFile==null) return false; return _idxhdrFile.ImageTypeFolder; } } /// <summary> /// Gets the background setting /// </summary> internal int Background { get { if(_idxhdrFile==null) return 0; return _idxhdrFile.Background; } } /// <summary> /// Gets the foreground setting /// </summary> internal int Foreground { get { if(_idxhdrFile==null) return 0; return _idxhdrFile.Foreground; } } /// <summary> /// Gets the Font string specyfied in the #IDXHDR file. /// </summary> /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks> internal string FontName { get { if( (_stringsFile == null) || (_idxhdrFile == null) ) return ""; return _stringsFile[ _idxhdrFile.FontOffset ]; } } /// <summary> /// Gets the FrameName string specyfied in the #IDXHDR file. /// </summary> /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks> internal string FrameName { get { if( (_stringsFile == null) || (_idxhdrFile == null) ) return ""; return _stringsFile[ _idxhdrFile.FrameNameOffset ]; } } /// <summary> /// Gets the WindowName string specyfied in the #IDXHDR file. /// </summary> /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks> internal string WindowName { get { if( (_stringsFile == null) || (_idxhdrFile == null) ) return ""; return _stringsFile[ _idxhdrFile.WindowNameOffset ]; } } /// <summary> /// Gets a string array containing the merged file names /// </summary> internal string[] MergedFiles { get { if( (_stringsFile == null) || (_idxhdrFile == null) ) return new string[0]; string[] saRet = new string[ _idxhdrFile.MergedFileOffsets.Count ]; for(int i=0; i < _idxhdrFile.MergedFileOffsets.Count; i++) { saRet[i] = _stringsFile[ (int)_idxhdrFile.MergedFileOffsets[i] ]; } return saRet; } } #endregion /// <summary> /// Gets the file info associated with this instance /// </summary> public ChmFileInfo FileInfo { get { return _chmFileInfo; } } /// <summary> /// Gets the internal toc read from the text-based hhc file /// </summary> public ArrayList TOC { get { return _toc; } } /// <summary> /// Gets the internal index read from the chm file. /// </summary> public ArrayList IndexKLinks { get { return _indexKLinks; } } /// <summary> /// Gets the internal index read from the chm file. /// </summary> public ArrayList IndexALinks { get { return _indexALinks; } } /// <summary> /// Gets the full-text search engine for this file /// </summary> internal FullTextEngine FullTextSearchEngine { get { return _ftSearcher; } } /// <summary> /// Gets the full pathname of the file /// </summary> public string ChmFilePath { get { return _chmFileName; } } /// <summary> /// Gets the full pathname of the chi-file /// The file name is zero-length if there is no chi-file /// </summary> public string ChiFilePath { get { return _chiFileName; } } /// <summary> /// Gets the full pathname of the chw-file /// The file name is zero-length if there is no chw-file /// </summary> public string ChwFilePath { get { return _chwFileName; } } /// <summary> /// Gets the full pathname of the chq-file /// The file name is zero-length if there is no chq-file /// </summary> public string ChqFilePath { get { return _chqFileName; } } /// <summary> /// Forms an URL for the web browser /// </summary> /// <param name="local">local resource</param> /// <returns>a url for the web-browser</returns> internal string FormURL(string local) { if( (local.ToLower().IndexOf("http://") >= 0) || (local.ToLower().IndexOf("https://") >= 0) || (local.ToLower().IndexOf("mailto:") >= 0) || (local.ToLower().IndexOf("ftp://") >= 0) || (local.ToLower().IndexOf("ms-its:") >= 0)) return local; return HtmlHelpSystem.UrlPrefix + _chmFileName + "::/" + local; } /// <summary> /// Implement IDisposable. /// </summary> 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); } /// <summary> /// 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. /// </summary> /// <param name="disposing">disposing flag</param> 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. _toc.Clear(); _indexKLinks.Clear(); _indexALinks.Clear(); if(_systemFile!=null) _systemFile.Dispose(); if(_idxhdrFile != null) _idxhdrFile.Dispose(); if(_stringsFile != null) _stringsFile.Dispose(); if(_urlstrFile != null) _urlstrFile.Dispose(); if(_urltblFile != null) _urltblFile.Dispose(); if(_topicsFile != null) _topicsFile.Dispose(); if(_tocidxFile != null) _tocidxFile.Dispose(); if(_kLinks != null) _kLinks.Dispose(); if(_aLinks != null) _aLinks.Dispose(); if(_ftSearcher != null) _ftSearcher.Dispose(); if(_chmFileInfo != null) _chmFileInfo = null; } } disposed = true; } } }