diff --git a/irc/TechBot/CHMLibrary/AssemblyInfo.cs b/irc/TechBot/CHMLibrary/AssemblyInfo.cs
new file mode 100644
index 00000000000..dbacda112e8
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/AssemblyInfo.cs
@@ -0,0 +1,32 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following
+// attributes.
+//
+// change them to the information which is associated with the assembly
+// you compile.
+
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has following format :
+//
+// Major.Minor.Build.Revision
+//
+// You can specify all values by your own or you can build default build and revision
+// numbers with the '*' character (the default):
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes specify the key for the sign of your assembly. See the
+// .NET Framework documentation for more information about signing.
+// This is not required, if you don't want signing let these attributes like they're.
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs b/irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs
new file mode 100644
index 00000000000..64c9668eb30
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs
@@ -0,0 +1,274 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+using System.Globalization;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class BinaryReaderHelp implements static helper methods for extracting binary data
+ /// from a binary reader object.
+ ///
+ internal class BinaryReaderHelp
+ {
+ ///
+ /// Internal helper method to extract null-terminated strings from a binary reader
+ ///
+ /// reference to the binary reader
+ /// offset in the stream
+ /// true if the offset value should be used
+ /// encoder used for text encoding
+ /// An extracted string value
+ internal static string ExtractString(ref BinaryReader binReader, int offset, bool noOffset, Encoding encoder)
+ {
+ string strReturn = "";
+
+ if(encoder == null)
+ encoder = Encoding.ASCII;
+
+ ArrayList nameBytes = new ArrayList();
+ byte curByte;
+
+ if(!noOffset)
+ binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
+
+ if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
+ return "";
+
+ curByte = binReader.ReadByte();
+ while( (curByte != (byte)0) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
+ {
+ nameBytes.Add( curByte );
+ curByte = binReader.ReadByte();
+ }
+
+ byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
+ strReturn = encoder.GetString(name,0,name.Length);
+
+ return strReturn;
+ }
+
+ ///
+ /// Internal helper method to extract a string with a specific length from the binary reader
+ ///
+ /// reference to the binary reader
+ /// length of the string (number of bytes)
+ /// offset in the stream
+ /// true if the offset value should be used
+ /// encoder used for text encoding
+ /// An extracted string value
+ internal static string ExtractString(ref BinaryReader binReader, int length, int offset, bool noOffset, Encoding encoder)
+ {
+ string strReturn = "";
+
+ if(length == 0)
+ return "";
+
+ if(encoder == null)
+ encoder = Encoding.ASCII;
+
+ ArrayList nameBytes = new ArrayList();
+ byte curByte;
+
+ if(!noOffset)
+ binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
+
+ if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
+ return "";
+
+ curByte = binReader.ReadByte();
+ while( (curByte != (byte)0) && (nameBytes.Count < length) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
+ {
+ nameBytes.Add( curByte );
+
+ if(nameBytes.Count < length)
+ curByte = binReader.ReadByte();
+ }
+
+ byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
+ strReturn = encoder.GetString(name,0,name.Length);
+
+ return strReturn;
+ }
+
+ ///
+ /// Internal helper method to extract a string with a specific length from the binary reader
+ ///
+ /// reference to the binary reader
+ /// reference to a bool vairable which will receive true if the
+ /// string terminator \0 was found. false indicates that the end of the stream was reached.
+ /// offset in the stream
+ /// true if the offset value should be used
+ /// encoder used for text encoding
+ /// An extracted string value
+ internal static string ExtractString(ref BinaryReader binReader, ref bool bFoundTerminator, int offset, bool noOffset, Encoding encoder)
+ {
+ string strReturn = "";
+
+ ArrayList nameBytes = new ArrayList();
+ byte curByte;
+
+ if(encoder == null)
+ encoder = Encoding.ASCII;
+
+ if(!noOffset)
+ binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
+
+ if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
+ return "";
+
+ curByte = binReader.ReadByte();
+ while( (curByte != (byte)0) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
+ {
+ nameBytes.Add( curByte );
+ curByte = binReader.ReadByte();
+
+ if( curByte == (byte)0 )
+ {
+ bFoundTerminator = true;
+ }
+ }
+
+ byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
+ strReturn = encoder.GetString(name,0,name.Length);
+
+ return strReturn;
+ }
+
+ ///
+ /// Internal helper method to extract a null-terminated UTF-16/UCS-2 strings from a binary reader
+ ///
+ /// reference to the binary reader
+ /// offset in the stream
+ /// true if the offset value should be used
+ /// encoder used for text encoding
+ /// An extracted string value
+ internal static string ExtractUTF16String(ref BinaryReader binReader, int offset, bool noOffset, Encoding encoder)
+ {
+ string strReturn = "";
+
+ ArrayList nameBytes = new ArrayList();
+ byte curByte;
+ int lastByte=-1;
+
+ if(!noOffset)
+ binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
+
+ if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
+ return "";
+
+ if(encoder == null)
+ encoder = Encoding.Unicode;
+
+ curByte = binReader.ReadByte();
+ int nCnt = 0;
+ while( ((curByte != (byte)0) || (lastByte != 0) ) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
+ {
+ nameBytes.Add( curByte );
+
+ if(nCnt%2 == 0)
+ lastByte = (int)curByte;
+
+ curByte = binReader.ReadByte();
+
+ nCnt++;
+ }
+
+ byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
+ strReturn = Encoding.Unicode.GetString(name,0,name.Length);
+
+ // apply text encoding
+ name = Encoding.Default.GetBytes(strReturn);
+ strReturn = encoder.GetString(name,0,name.Length);
+
+ return strReturn;
+ }
+
+ ///
+ /// Internal helper for reading ENCINT encoded integer values
+ ///
+ /// reference to the reader
+ /// a long value
+ internal static long ReadENCINT(ref BinaryReader binReader)
+ {
+ long nRet = 0;
+ byte buffer = 0;
+ int shift = 0;
+
+ if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
+ return nRet;
+
+ do
+ {
+ buffer = binReader.ReadByte();
+ nRet |= ((long)((buffer & (byte)0x7F))) << shift;
+ shift += 7;
+
+ }while ( (buffer & (byte)0x80) != 0);
+
+ return nRet;
+ }
+
+ ///
+ /// Reads an s/r encoded value from the byte array and decodes it into an integer
+ ///
+ /// a byte array containing all bits (contains only 0 or 1 elements)
+ /// scale param for encoding
+ /// root param for encoding
+ /// current index in the wclBits array
+ /// Returns an decoded integer value.
+ internal static int ReadSRItem(byte[] wclBits, int s, int r, ref int nBitIndex)
+ {
+ int nRet = 0;
+ int q = r;
+
+ int nPref1Cnt = 0;
+
+ while( wclBits[nBitIndex++] == 1)
+ {
+ nPref1Cnt++;
+ }
+
+ if(nPref1Cnt == 0)
+ {
+ int nMask = 0;
+
+ for(int nbits=0; nbits
+ /// The class CHMBtree implements methods/properties to decode the binary help index.
+ /// This class automatically creates an index arraylist for the current CHMFile instance.
+ /// It does not store the index internally !
+ ///
+ /// The binary index can be found in the storage file $WWKeywordLinks/BTree
+ internal sealed class CHMBtree : IDisposable
+ {
+ ///
+ /// Constant specifying the size of the string blocks
+ ///
+ private const int BLOCK_SIZE = 2048;
+ ///
+ /// 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 flags
+ ///
+ private int _flags = 0;
+ ///
+ /// Internal member storing the data format
+ ///
+ private byte[] _dataFormat = new byte[16];
+ ///
+ /// Internal member storing the index of the last listing block
+ ///
+ private int _indexOfLastListingBlock = 0;
+ ///
+ /// Internal member storing the index of the root block
+ ///
+ private int _indexOfRootBlock = 0;
+ ///
+ /// Internal member storing the number of blocks
+ ///
+ private int _numberOfBlocks = 0;
+ ///
+ /// Internal member storing the tree depth.
+ /// (1 if no index blocks, 2 one level of index blocks, ...)
+ ///
+ private int _treeDepth = 0;
+ ///
+ /// Internal member storing the number of keywords in the file
+ ///
+ private int _numberOfKeywords = 0;
+ ///
+ /// Internal member storing the codepage
+ ///
+ private int _codePage = 0;
+ ///
+ /// true if the index is from a CHI or CHM file, else CHW
+ ///
+ private bool _isCHI_CHM = true;
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+ ///
+ /// Internal flag specifying if we have to read listing or index blocks
+ ///
+ private bool _readListingBlocks = true;
+ ///
+ /// Internal member storing an indexlist of the current file.
+ ///
+ private ArrayList _indexList = new ArrayList();
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the $WWKeywordLinks/BTree file
+ /// associated chm file
+ public CHMBtree(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ if( associatedFile == null)
+ {
+ throw new ArgumentException("CHMBtree.ctor() - Associated CHMFile must not be null !", "associatedFile");
+ }
+
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+ DecodeData();
+
+ // clear internal binary data after extraction
+ _binaryFileData = null;
+ }
+
+ ///
+ /// 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;
+ int nTemp = 0;
+
+ // decode header
+ binReader.ReadChars(2); // 2chars signature (not important)
+
+ _flags = (int)binReader.ReadInt16(); // WORD flags
+
+ binReader.ReadInt16(); // size of blocks (always 2048)
+
+ _dataFormat = binReader.ReadBytes(16);
+
+ binReader.ReadInt32(); // unknown DWORD
+
+ _indexOfLastListingBlock = binReader.ReadInt32();
+ _indexOfRootBlock = binReader.ReadInt32();
+
+ binReader.ReadInt32(); // unknown DWORD
+
+ _numberOfBlocks = binReader.ReadInt32();
+ _treeDepth = binReader.ReadInt16();
+ _numberOfKeywords = binReader.ReadInt32();
+ _codePage = binReader.ReadInt32();
+
+ binReader.ReadInt32(); // lcid DWORD
+
+ nTemp = binReader.ReadInt32();
+ _isCHI_CHM = (nTemp==1);
+
+ binReader.ReadInt32(); // unknown DWORD
+ binReader.ReadInt32(); // unknown DWORD
+ binReader.ReadInt32(); // unknown DWORD
+ binReader.ReadInt32(); // unknown DWORD
+
+ // end of header decode
+
+ while( (memStream.Position < memStream.Length) && (bRet) )
+ {
+ nCurOffset = (int)memStream.Position;
+ byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);
+ bRet &= DecodeBlock(dataBlock, ref nCurOffset, _treeDepth-1);
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Decodes a block of url-string data
+ ///
+ /// block of data
+ /// current file offset
+ /// number of index blocks
+ /// true if succeeded
+ private bool DecodeBlock( byte[] dataBlock, ref int nOffset, int indexBlocks )
+ {
+ bool bRet = true;
+ int nblockOffset = nOffset;
+
+ MemoryStream memStream = new MemoryStream(dataBlock);
+ BinaryReader binReader = new BinaryReader(memStream);
+
+ int freeSpace = binReader.ReadInt16(); // length of freespace
+ int nrOfEntries = binReader.ReadInt16(); // number of entries
+
+ bool bListingEndReached = false;
+
+ //while( (memStream.Position < (memStream.Length-freeSpace)) && (bRet) )
+ //{
+ int nIndexOfPrevBlock = -1;
+ int nIndexOfNextBlock = -1;
+ int nIndexOfChildBlock = 0;
+
+ if(_readListingBlocks)
+ {
+ nIndexOfPrevBlock = binReader.ReadInt32(); // -1 if this is the header
+ nIndexOfNextBlock = binReader.ReadInt32(); // -1 if this is the last block
+ }
+ else
+ {
+ nIndexOfChildBlock = binReader.ReadInt32();
+ }
+
+ for(int nE = 0; nE < nrOfEntries; nE++)
+ {
+ if(_readListingBlocks)
+ {
+ bListingEndReached = (nIndexOfNextBlock==-1);
+
+ string keyWord = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
+
+ bool isSeeAlsoKeyword = (binReader.ReadInt16()!=0);
+
+ int indent = binReader.ReadInt16(); // indent of entry
+ int nCharIndex = binReader.ReadInt32();
+
+ binReader.ReadInt32();
+
+ int numberOfPairs = binReader.ReadInt32();
+
+ int[] nTopics = new int[numberOfPairs];
+ string[] seeAlso = new string[numberOfPairs];
+
+ for(int i=0; i < numberOfPairs; i++)
+ {
+ if(isSeeAlsoKeyword)
+ {
+ seeAlso[i] = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
+ }
+ else
+ {
+ nTopics[i] = binReader.ReadInt32();
+ }
+ }
+
+ binReader.ReadInt32(); // unknown
+
+ int nIndexOfThisEntry = binReader.ReadInt32();
+
+ IndexItem newItem = new IndexItem(_associatedFile, keyWord, isSeeAlsoKeyword, indent, nCharIndex, nIndexOfThisEntry, seeAlso, nTopics);
+ _indexList.Add(newItem);
+ }
+ else
+ {
+ string keyWord = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
+
+ bool isSeeAlsoKeyword = (binReader.ReadInt16()!=0);
+
+ int indent = binReader.ReadInt16(); // indent of entry
+ int nCharIndex = binReader.ReadInt32();
+
+ binReader.ReadInt32();
+
+ int numberOfPairs = binReader.ReadInt32();
+
+ int[] nTopics = new int[numberOfPairs];
+ string[] seeAlso = new string[numberOfPairs];
+
+ for(int i=0; i < numberOfPairs; i++)
+ {
+ if(isSeeAlsoKeyword)
+ {
+ seeAlso[i] = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
+ }
+ else
+ {
+ nTopics[i] = binReader.ReadInt32();
+ }
+ }
+
+ int nIndexChild = binReader.ReadInt32();
+ int nIndexOfThisEntry=-1;
+
+ IndexItem newItem = new IndexItem(_associatedFile, keyWord, isSeeAlsoKeyword, indent, nCharIndex, nIndexOfThisEntry, seeAlso, nTopics);
+ _indexList.Add(newItem);
+
+ }
+ }
+ //}
+
+ binReader.ReadBytes(freeSpace);
+
+
+ if( bListingEndReached )
+ _readListingBlocks = false;
+
+ return bRet;
+ }
+
+ ///
+ /// Gets the internal generated index list
+ ///
+ internal ArrayList IndexList
+ {
+ get { return _indexList; }
+ }
+
+ ///
+ /// 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;
+ }
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs
new file mode 100644
index 00000000000..a39ffc4d684
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs
@@ -0,0 +1,2061 @@
+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
+{
+ ///
+ /// Internal enumeration for specifying the type of the html-help file
+ ///
+ internal enum HtmlHelpFileType
+ {
+ ///
+ /// CHM - compiled contents file
+ ///
+ /// A file with this extension must always exist. If the file would be too long, some parts
+ /// can be splitted into the filestypes below.
+ CHM = 0,
+ ///
+ /// CHI - compiled system file
+ ///
+ CHI = 1,
+ ///
+ /// CHQ - compiled fulltext search file
+ ///
+ CHQ = 2,
+ ///
+ /// CHW - compiled index file
+ ///
+ CHW = 3
+
+ }
+
+ ///
+ /// The class CHMFile implemts methods and properties for handling a single chmfile.
+ ///
+ public sealed class CHMFile : IDisposable
+ {
+ ///
+ /// Internal member storing a reference to the hosting HtmlHelpSystem instance
+ ///
+ private HtmlHelpSystem _systemInstance = null;
+ ///
+ /// Internal flag specifying if only system data has been loaded
+ ///
+ private bool _onlySystem = false;
+ ///
+ /// Internal flag specifying if the object is going to be disposed
+ ///
+ private bool disposed = false;
+ ///
+ /// Internal arraylist containing the table of contents
+ ///
+ private ArrayList _toc = new ArrayList();
+ ///
+ /// Internal arraylist containing items of the toc which are merge-Links
+ ///
+ private ArrayList _mergeLinks = new ArrayList();
+ ///
+ /// Internal member storing the read information types
+ ///
+ private ArrayList _informationTypes = new ArrayList();
+ ///
+ /// Internal member storing the read categories
+ ///
+ private ArrayList _categories = new ArrayList();
+ ///
+ /// Internal arraylist containing the index (klinks)
+ ///
+ private ArrayList _indexKLinks = new ArrayList();
+ ///
+ /// Internal arraylist containing the index (alinks)
+ ///
+ private ArrayList _indexALinks = new ArrayList();
+ ///
+ /// Internal member storing the full filename
+ ///
+ private string _chmFileName = "";
+ ///
+ /// 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
+ ///
+ private string _chiFileName = "";
+ ///
+ /// 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
+ ///
+ private string _chwFileName = "";
+ ///
+ /// 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
+ ///
+ private string _chqFileName = "";
+ ///
+ /// Internal member storing the decoded information from the internal #SYSTEM file
+ ///
+ private CHMSystem _systemFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal #IDXHDR file
+ ///
+ private CHMIdxhdr _idxhdrFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal #STRINGS file
+ ///
+ private CHMStrings _stringsFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal #URLSTR file
+ ///
+ private CHMUrlstr _urlstrFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal #URLTBL file
+ ///
+ private CHMUrltable _urltblFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal #TOPICS file
+ ///
+ private CHMTopics _topicsFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal #TOCIDX file
+ ///
+ private CHMTocidx _tocidxFile = null;
+ ///
+ /// Internal member storing the decoded information from the internal binary index file (KLinks).
+ ///
+ private CHMBtree _kLinks = null;
+ ///
+ /// Internal member storing the decoded information from the internal binary index file (ALinks).
+ ///
+ private CHMBtree _aLinks = null;
+ ///
+ /// Internal member storing the fulltext searcher for this file
+ ///
+ private FullTextEngine _ftSearcher = null;
+ ///
+ /// Internal member storing the default encoder
+ ///
+ private Encoding _textEncoding = Encoding.GetEncoding(1252); // standard windows-1252 encoder
+ ///
+ /// Internal memebr storing the chm file info
+ ///
+ private ChmFileInfo _chmFileInfo = null;
+ ///
+ /// Internal flag specifying if the dump must be written (if enabled)
+ ///
+ private bool _mustWriteDump = false;
+ ///
+ /// Internal flag specifying if data was read using the dump
+ ///
+ private bool _dumpRead = false;
+ ///
+ /// 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)
+ ///
+ private int _dumpReadTrys = 0;
+ ///
+ /// Internal member storing the dumping info instance
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Creates a new instance of the class
+ ///
+ /// a reference to the hosting HtmlHelpSystem instance
+ /// chm file to read
+ public CHMFile(HtmlHelpSystem systemInstance, string chmFile) : this(systemInstance, chmFile, false, null)
+ {
+ }
+
+ ///
+ /// Creates a new instance of the class
+ ///
+ /// a reference to the hosting HtmlHelpSystem instance
+ /// chm file to read
+ /// A dumping info class
+ public CHMFile(HtmlHelpSystem systemInstance, string chmFile, DumpingInfo dmpInfo) : this(systemInstance, chmFile, false, dmpInfo)
+ {
+ }
+
+ ///
+ /// Creates a new instance of the class
+ ///
+ /// a reference to the hosting HtmlHelpSystem instance
+ /// chm file to read
+ /// true if only system data should be extracted (no index or toc)
+ internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData) : this(systemInstance, chmFile, onlySystemData, null)
+ {
+ }
+
+ ///
+ /// Creates a new instance of the class
+ ///
+ /// a reference to the hosting HtmlHelpSystem instance
+ /// chm file to read
+ /// true if only system data should be extracted (no index or toc)
+ /// A dumping info class
+ 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");
+ }
+ }
+
+ ///
+ /// Read a IStorage file
+ ///
+ /// filename
+ /// type of file
+ 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;
+ }
+
+ ///
+ /// Enumerates the files in the chm storage and gets all files matching a given extension.
+ ///
+ /// extension to return
+ /// Returns an arraylist of filenames or null if nothing found
+ /// On large CHMs, enumerations are very slow. Only use it if necessary !
+ 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;
+ }
+
+ ///
+ /// Searches an TOC entry using the local
+ ///
+ /// local to search
+ /// Returns the TOC item
+ internal TOCItem GetTOCItemByLocal(string local)
+ {
+ return GetTOCItemByLocal(this.TOC, local);
+ }
+
+ ///
+ /// Recursively searches an TOC entry using its local
+ ///
+ /// toc level list
+ /// local to search
+ /// Returns the TOC item
+ 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;
+ }
+
+ ///
+ /// Removes a TOCItem from the toc
+ ///
+ /// item to remove
+ /// Returns true if removed
+ internal bool RemoveTOCItem(TOCItem rem)
+ {
+ if(rem == null)
+ return false;
+
+ return RemoveTOCItem(this.TOC, rem);
+ }
+
+ ///
+ /// Recursively searches a TOCItem and removes it if found
+ ///
+ /// toc level list
+ /// item to remove
+ /// Returns true if removed
+ private bool RemoveTOCItem(ArrayList arrTOC, TOCItem rem)
+ {
+ for(int i=0; i 0)
+ {
+ bool bRem = RemoveTOCItem(curItem.Children, rem);
+ if(bRem)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Returns true if the HtmlHelpSystem instance contains 1 or more information types
+ ///
+ public bool HasInformationTypes
+ {
+ get { return (_informationTypes.Count>0); }
+ }
+
+ ///
+ /// Returns true if the HtmlHelpSystem instance contains 1 or more categories
+ ///
+ public bool HasCategories
+ {
+ get { return (_categories.Count>0); }
+ }
+
+ ///
+ /// Gets an ArrayList of InformationType items
+ ///
+ public ArrayList InformationTypes
+ {
+ get { return _informationTypes; }
+ }
+
+ ///
+ /// Gets an ArrayList of Category items
+ ///
+ public ArrayList Categories
+ {
+ get { return _categories; }
+ }
+
+ ///
+ /// Gets the information type specified by its name
+ ///
+ /// name of the information type to receive
+ /// Returns the Instance for the name or null if not found
+ 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;
+ }
+
+ ///
+ /// Gets the category specifiyd by its name
+ ///
+ /// name of the category
+ /// Returns the Instance for the name or null if not found
+ 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
+
+ ///
+ /// Checks if a dump for this file exists and if it can be read
+ ///
+ /// dumping info class
+ /// true if it can be read
+ 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;
+ }
+
+ ///
+ /// Saves the the toc and index into a data dump
+ ///
+ /// dumping info
+ /// true if succeed
+ 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;
+ }
+
+ ///
+ /// Parses a HHC file which is located in the current CHM.
+ ///
+ /// hhc file to parse
+ /// an arraylist with toc items
+ 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;
+ }
+
+ ///
+ /// Loads the toc and index from a data dump
+ ///
+ /// dumping info
+ /// true if succeed
+ 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
+ /// Gets the current storage wrapper.
+ ///
+ /// This property will return not null, if there are currently file read actions running !
+ internal CHMStream.CHMStream CurrentStorageWrapper
+ {
+ get { return _currentWrapper;}
+ }
+ ///
+ /// Gets/sets the hosting HtmlHelpSystem instance
+ ///
+ internal HtmlHelpSystem SystemInstance
+ {
+ get { return _systemInstance; }
+ set { _systemInstance = value; }
+ }
+ ///
+ /// Gets an arraylist of TOC items which contains merg-links to other CHMs
+ ///
+ internal ArrayList MergLinks
+ {
+ get { return _mergeLinks; }
+ }
+
+ ///
+ /// Gets the internal system file instance
+ ///
+ internal CHMSystem SystemFile
+ {
+ get { return _systemFile; }
+ }
+
+ ///
+ /// Gets the internal idxhdr file instance
+ ///
+ internal CHMIdxhdr IdxHdrFile
+ {
+ get { return _idxhdrFile; }
+ }
+
+ ///
+ /// Gets the internal strings file instance
+ ///
+ internal CHMStrings StringsFile
+ {
+ get { return _stringsFile; }
+ }
+
+ ///
+ /// Gets the internal urlstr file instance
+ ///
+ internal CHMUrlstr UrlstrFile
+ {
+ get { return _urlstrFile; }
+ }
+
+ ///
+ /// Gets the internal urltbl file instance
+ ///
+ internal CHMUrltable UrltblFile
+ {
+ get { return _urltblFile; }
+ }
+
+ ///
+ /// Gets the internal topics file instance
+ ///
+ internal CHMTopics TopicsFile
+ {
+ get { return _topicsFile; }
+ }
+
+ ///
+ /// Gets the internal tocidx file instance
+ ///
+ internal CHMTocidx TocidxFile
+ {
+ get { return _tocidxFile; }
+ }
+
+ ///
+ /// Gets the internal btree file instance for alinks
+ ///
+ internal CHMBtree ALinksFile
+ {
+ get { return _aLinks; }
+ }
+
+ ///
+ /// Gets the internal btree file instance for klinks
+ ///
+ internal CHMBtree KLinksFile
+ {
+ get { return _kLinks; }
+ }
+
+ ///
+ /// Gets/Sets the text encoding
+ ///
+ internal Encoding TextEncoding
+ {
+ get { return _textEncoding; }
+ set { _textEncoding = value; }
+ }
+
+ #endregion
+
+ #region Properties from the #SYSTEM file
+ ///
+ /// Gets the file version of the chm file.
+ /// 2 for Compatibility=1.0, 3 for Compatibility=1.1
+ ///
+ internal int FileVersion
+ {
+ get
+ {
+ if(_systemFile==null)
+ return 0;
+
+ return _systemFile.FileVersion;
+ }
+ }
+
+ ///
+ /// Gets the contents file name
+ ///
+ internal string ContentsFile
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.ContentsFile;
+ }
+ }
+
+ ///
+ /// Gets the index file name
+ ///
+ internal string IndexFile
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.IndexFile;
+ }
+ }
+
+ ///
+ /// Gets the default help topic
+ ///
+ internal string DefaultTopic
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.DefaultTopic;
+ }
+ }
+
+ ///
+ /// Gets the title of the help window
+ ///
+ internal string HelpWindowTitle
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.Title;
+ }
+ }
+
+ ///
+ /// Gets the flag if DBCS is in use
+ ///
+ internal bool DBCS
+ {
+ get
+ {
+ if(_systemFile==null)
+ return false;
+
+ return _systemFile.DBCS;
+ }
+ }
+
+ ///
+ /// Gets the flag if full-text-search is available
+ ///
+ internal bool FullTextSearch
+ {
+ get
+ {
+ if(_systemFile==null)
+ return false;
+
+ return _systemFile.FullTextSearch;
+ }
+ }
+
+ ///
+ /// Gets the flag if the file has ALinks
+ ///
+ internal bool HasALinks
+ {
+ get
+ {
+ if(_systemFile==null)
+ return false;
+
+ return _systemFile.HasALinks;
+ }
+ }
+
+ ///
+ /// Gets the flag if the file has KLinks
+ ///
+ internal bool HasKLinks
+ {
+ get
+ {
+ if(_systemFile==null)
+ return false;
+
+ return _systemFile.HasKLinks;
+ }
+ }
+
+ ///
+ /// Gets the default window name
+ ///
+ internal string DefaultWindow
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.DefaultWindow;
+ }
+ }
+
+ ///
+ /// Gets the file name of the compile file
+ ///
+ internal string CompileFile
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.CompileFile;
+ }
+ }
+
+ ///
+ /// Gets the flag if the chm has a binary index file
+ ///
+ internal bool BinaryIndex
+ {
+ get
+ {
+ if(_systemFile==null)
+ return false;
+
+ return _systemFile.BinaryIndex;
+ }
+ }
+
+ ///
+ /// Gets the flag if the chm has a binary index file
+ ///
+ internal string CompilerVersion
+ {
+ get
+ {
+ if(_systemFile==null)
+ return String.Empty;
+
+ return _systemFile.CompilerVersion;
+ }
+ }
+
+ ///
+ /// Gets the flag if the chm has a binary toc file
+ ///
+ internal bool BinaryTOC
+ {
+ get
+ {
+ if(_systemFile==null)
+ return false;
+
+ return _systemFile.BinaryTOC;
+ }
+ }
+
+ ///
+ /// Gets the font face of the read font property.
+ /// Empty string for default font.
+ ///
+ internal string FontFace
+ {
+ get
+ {
+ if(_systemFile==null)
+ return "";
+
+ return _systemFile.FontFace;
+ }
+ }
+
+ ///
+ /// Gets the font size of the read font property.
+ /// 0 for default font size
+ ///
+ internal double FontSize
+ {
+ get
+ {
+ if(_systemFile==null)
+ return 0;
+
+ return _systemFile.FontSize;
+ }
+ }
+
+ ///
+ /// Gets the character set of the read font property
+ /// 1 for default
+ ///
+ internal int CharacterSet
+ {
+ get
+ {
+ if(_systemFile==null)
+ return 1;
+
+ return _systemFile.CharacterSet;
+ }
+ }
+
+ ///
+ /// Gets the codepage depending on the read font property
+ ///
+ internal int CodePage
+ {
+ get
+ {
+ if(_systemFile==null)
+ return CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
+
+ return _systemFile.CodePage;
+ }
+ }
+
+ ///
+ /// Gets the assiciated culture info
+ ///
+ internal CultureInfo Culture
+ {
+ get
+ {
+ if(_systemFile==null)
+ return CultureInfo.CurrentCulture;
+
+ return _systemFile.Culture;
+ }
+ }
+
+ #endregion
+
+ #region Properties from the #IDXHDR file
+ ///
+ /// Gets the number of topic nodes including the contents and index files
+ ///
+ internal int NumberOfTopicNodes
+ {
+ get
+ {
+ if(_idxhdrFile==null)
+ return 0;
+
+ return _idxhdrFile.NumberOfTopicNodes;
+ }
+ }
+
+ ///
+ /// Gets the ImageList string specyfied in the #IDXHDR file.
+ ///
+ /// This property uses the #STRINGS file to extract the string at a given offset.
+ internal string ImageList
+ {
+ get
+ {
+ if( (_stringsFile == null) || (_idxhdrFile == null) )
+ return "";
+
+ return _stringsFile[ _idxhdrFile.ImageListOffset ];
+ }
+ }
+
+ ///
+ /// True if the value of the ImageType param of the
+ /// "text/site properties" object of the sitemap contents is "Folder".
+ ///
+ /// If this is set to true, the help will display folders instead of books
+ internal bool ImageTypeFolder
+ {
+ get
+ {
+ if(_idxhdrFile==null)
+ return false;
+
+ return _idxhdrFile.ImageTypeFolder;
+ }
+ }
+
+ ///
+ /// Gets the background setting
+ ///
+ internal int Background
+ {
+ get
+ {
+ if(_idxhdrFile==null)
+ return 0;
+
+ return _idxhdrFile.Background;
+ }
+ }
+
+ ///
+ /// Gets the foreground setting
+ ///
+ internal int Foreground
+ {
+ get
+ {
+ if(_idxhdrFile==null)
+ return 0;
+
+ return _idxhdrFile.Foreground;
+ }
+ }
+
+ ///
+ /// Gets the Font string specyfied in the #IDXHDR file.
+ ///
+ /// This property uses the #STRINGS file to extract the string at a given offset.
+ internal string FontName
+ {
+ get
+ {
+ if( (_stringsFile == null) || (_idxhdrFile == null) )
+ return "";
+
+ return _stringsFile[ _idxhdrFile.FontOffset ];
+ }
+ }
+
+ ///
+ /// Gets the FrameName string specyfied in the #IDXHDR file.
+ ///
+ /// This property uses the #STRINGS file to extract the string at a given offset.
+ internal string FrameName
+ {
+ get
+ {
+ if( (_stringsFile == null) || (_idxhdrFile == null) )
+ return "";
+
+ return _stringsFile[ _idxhdrFile.FrameNameOffset ];
+ }
+ }
+
+ ///
+ /// Gets the WindowName string specyfied in the #IDXHDR file.
+ ///
+ /// This property uses the #STRINGS file to extract the string at a given offset.
+ internal string WindowName
+ {
+ get
+ {
+ if( (_stringsFile == null) || (_idxhdrFile == null) )
+ return "";
+
+ return _stringsFile[ _idxhdrFile.WindowNameOffset ];
+ }
+ }
+
+ ///
+ /// Gets a string array containing the merged file names
+ ///
+ 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
+
+ ///
+ /// Gets the file info associated with this instance
+ ///
+ public ChmFileInfo FileInfo
+ {
+ get { return _chmFileInfo; }
+ }
+
+ ///
+ /// Gets the internal toc read from the text-based hhc file
+ ///
+ public ArrayList TOC
+ {
+ get { return _toc; }
+ }
+
+ ///
+ /// Gets the internal index read from the chm file.
+ ///
+ public ArrayList IndexKLinks
+ {
+ get { return _indexKLinks; }
+ }
+
+ ///
+ /// Gets the internal index read from the chm file.
+ ///
+ public ArrayList IndexALinks
+ {
+ get { return _indexALinks; }
+ }
+
+ ///
+ /// Gets the full-text search engine for this file
+ ///
+ internal FullTextEngine FullTextSearchEngine
+ {
+ get { return _ftSearcher; }
+ }
+
+ ///
+ /// Gets the full pathname of the file
+ ///
+ public string ChmFilePath
+ {
+ get { return _chmFileName; }
+ }
+
+ ///
+ /// Gets the full pathname of the chi-file
+ /// The file name is zero-length if there is no chi-file
+ ///
+ public string ChiFilePath
+ {
+ get { return _chiFileName; }
+ }
+
+ ///
+ /// Gets the full pathname of the chw-file
+ /// The file name is zero-length if there is no chw-file
+ ///
+ public string ChwFilePath
+ {
+ get { return _chwFileName; }
+ }
+
+ ///
+ /// Gets the full pathname of the chq-file
+ /// The file name is zero-length if there is no chq-file
+ ///
+ public string ChqFilePath
+ {
+ get { return _chqFileName; }
+ }
+
+ ///
+ /// Forms an URL for the web browser
+ ///
+ /// local resource
+ /// a url for the web-browser
+ 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;
+ }
+
+ ///
+ /// 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.
+ _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;
+ }
+
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs
new file mode 100644
index 00000000000..7ac64ac3ea7
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs
@@ -0,0 +1,286 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class CHMIdxhdr implements t properties which have been read from the #IDXHDR file.
+ ///
+ internal sealed class CHMIdxhdr : 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 number of topic nodes including the contents and index files
+ ///
+ private int _numberOfTopicNodes = 0;
+ ///
+ /// Internal member storing the offset in the #STRINGS file of the ImageList param of the "text/site properties" object of the sitemap contents
+ ///
+ private int _imageListOffset = 0;
+ ///
+ /// True if the value of the ImageType param of the "text/site properties" object of the sitemap contents is "Folder".
+ ///
+ private bool _imageTypeFolder = false;
+ ///
+ /// Internal member storing the background value
+ ///
+ private int _background = 0;
+ ///
+ /// Internal member storing the foreground value
+ ///
+ private int _foreground = 0;
+ ///
+ /// Internal member storing the offset in the #STRINGS file of the Font param of the "text/site properties" object of the sitemap contents
+ ///
+ private int _fontOffset = 0;
+ ///
+ /// Internal member storing the offset in the #STRINGS file of the FrameName param of the "text/site properties" object of the sitemap contents
+ ///
+ private int _frameNameOffset = 0;
+ ///
+ /// Internal member storing the offset in the #STRINGS file of the WindowName param of the "text/site properties" object of the sitemap contents
+ ///
+ private int _windowNameOffset = 0;
+ ///
+ /// Internal member storing the number of merged files
+ ///
+ private int _numberOfMergedFiles = 0;
+ ///
+ /// Internal member storing the offset in the #STRINGS file of the merged file names
+ ///
+ private ArrayList _mergedFileOffsets = new ArrayList();
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the #IDXHDR file
+ /// associated CHMFile instance
+ public CHMIdxhdr(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+ DecodeData();
+ }
+
+ ///
+ /// 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 nTemp = 0;
+
+ // 4 character T#SM
+ binReader.ReadBytes(4);
+ // unknown timestamp DWORD
+ nTemp = binReader.ReadInt32();
+
+ // unknown 1
+ nTemp = binReader.ReadInt32();
+
+ // number of topic nodes including the contents & index files
+ _numberOfTopicNodes = binReader.ReadInt32();
+
+ // unknown DWORD
+ nTemp = binReader.ReadInt32();
+
+ // offset in the strings file
+ _imageListOffset = binReader.ReadInt32();
+ if( _imageListOffset == 0)
+ _imageListOffset = -1; // 0/-1 = none
+
+ // unknown DWORD
+ nTemp = binReader.ReadInt32();
+
+ // 1 if the value of the ImageType param of the "text/site properties" object of the sitemap contents is "Folder".
+ nTemp = binReader.ReadInt32();
+ _imageTypeFolder = (nTemp == 1);
+
+ // offset in the strings file
+ _background = binReader.ReadInt32();
+ // offset in the strings file
+ _foreground = binReader.ReadInt32();
+
+ // offset in the strings file
+ _fontOffset = binReader.ReadInt32();
+
+ // window styles DWORD
+ nTemp = binReader.ReadInt32();
+ // window styles DWORD
+ nTemp = binReader.ReadInt32();
+
+ // unknown DWORD
+ nTemp = binReader.ReadInt32();
+
+ // offset in the strings file
+ _frameNameOffset = binReader.ReadInt32();
+ if( _frameNameOffset == 0)
+ _frameNameOffset = -1; // 0/-1 = none
+ // offset in the strings file
+ _windowNameOffset = binReader.ReadInt32();
+ if( _windowNameOffset == 0)
+ _windowNameOffset = -1; // 0/-1 = none
+
+ // informations types DWORD
+ nTemp = binReader.ReadInt32();
+
+ // unknown DWORD
+ nTemp = binReader.ReadInt32();
+
+ // number of merged files in the merged file list DWORD
+ _numberOfMergedFiles = binReader.ReadInt32();
+
+ nTemp = binReader.ReadInt32();
+
+ for(int i = 0; i < _numberOfMergedFiles; i++)
+ {
+ // DWORD offset value of merged file
+ nTemp = binReader.ReadInt32();
+
+ if(nTemp > 0)
+ _mergedFileOffsets.Add(nTemp);
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Gets the number of topic nodes including the contents and index files
+ ///
+ public int NumberOfTopicNodes
+ {
+ get { return _numberOfTopicNodes; }
+ }
+
+ ///
+ /// Gets the offset in the #STRINGS file of the ImageList
+ /// param of the "text/site properties" object of the sitemap contents
+ ///
+ public int ImageListOffset
+ {
+ get { return _imageListOffset; }
+ }
+
+ ///
+ /// True if the value of the ImageType param of the
+ /// "text/site properties" object of the sitemap contents is "Folder".
+ ///
+ /// If this is set to true, the help will display folders instead of books
+ public bool ImageTypeFolder
+ {
+ get { return _imageTypeFolder; }
+ }
+
+ ///
+ /// Gets the background setting
+ ///
+ public int Background
+ {
+ get { return _background; }
+ }
+
+ ///
+ /// Gets the foreground setting
+ ///
+ public int Foreground
+ {
+ get { return _foreground; }
+ }
+
+ ///
+ /// Gets the offset in the #STRINGS file of the Font
+ /// param of the "text/site properties" object of the sitemap contents
+ ///
+ public int WindowNameOffset
+ {
+ get { return _fontOffset; }
+ }
+
+ ///
+ /// Gets the offset in the #STRINGS file of the FrameName
+ /// param of the "text/site properties" object of the sitemap contents
+ ///
+ public int FrameNameOffset
+ {
+ get { return _frameNameOffset; }
+ }
+
+ ///
+ /// Gets the offset in the #STRINGS file of the WindowName
+ /// param of the "text/site properties" object of the sitemap contents
+ ///
+ public int FontOffset
+ {
+ get { return _windowNameOffset; }
+ }
+
+ ///
+ /// Gets an array list of offset numbers in the #STRINGS file of the
+ /// merged file names.
+ ///
+ public ArrayList MergedFileOffsets
+ {
+ get { return _mergedFileOffsets; }
+ }
+
+ ///
+ /// 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;
+ _mergedFileOffsets = null;
+ }
+
+
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs
new file mode 100644
index 00000000000..5942d4e8cfe
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs
@@ -0,0 +1,256 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Specialized;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class CHMStrings implements a string collection read from the #STRINGS file
+ ///
+ internal sealed class CHMStrings : IDisposable
+ {
+ ///
+ /// Constant specifying the size of the string blocks
+ ///
+ private const int STRING_BLOCK_SIZE = 4096;
+ ///
+ /// 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 string dictionary
+ ///
+ private Hashtable _stringDictionary = new Hashtable();
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the #STRINGS file
+ /// associated chm file
+ public CHMStrings(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+ DecodeData();
+ }
+
+
+ ///
+ /// Standard constructor
+ ///
+ internal CHMStrings()
+ {
+ }
+
+ #region Data dumping
+ ///
+ /// Dump the class data to a binary writer
+ ///
+ /// writer to write the data
+ internal void Dump(ref BinaryWriter writer)
+ {
+ writer.Write( _stringDictionary.Count );
+
+ if (_stringDictionary.Count != 0)
+ {
+ IDictionaryEnumerator iDictionaryEnumerator = _stringDictionary.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 nCnt = reader.ReadInt32();
+
+ for(int 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);
+
+ //binReader.ReadByte(); // file starts with a NULL character for 0-based offset indexing
+
+ int nStringOffset = 0;
+ int nSubsetOffset = 0;
+
+ while( (memStream.Position < memStream.Length) && (bRet) )
+ {
+ nStringOffset = (int)memStream.Position;
+ byte [] stringBlock = binReader.ReadBytes(STRING_BLOCK_SIZE);
+ bRet &= DecodeBlock(stringBlock, ref nStringOffset, ref nSubsetOffset);
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Decodes a string block
+ ///
+ /// byte array which represents the string block
+ /// current string offset number
+ /// reference to a subset variable
+ /// true if succeeded
+ /// If a string crosses the end of a block then it will be cut off
+ /// without a NT and repeated in full, with a NT, at the start of the next block.
+ /// For eg "To customize the appearance of a contents file" might become
+ /// "To customize the (block ending)To customize the appearance of a contents file"
+ /// when there are 17 bytes left at the end of the block.
+ private bool DecodeBlock( byte[] stringBlock, ref int nStringOffset, ref int nSubsetOffset)
+ {
+ bool bRet = true;
+
+ MemoryStream memStream = new MemoryStream(stringBlock);
+ BinaryReader binReader = new BinaryReader(memStream);
+
+ while( (memStream.Position < memStream.Length) && (bRet) )
+ {
+ bool bFoundTerminator = false;
+
+ int nCurOffset = nStringOffset + (int)memStream.Position;
+
+ string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);
+
+ if(nSubsetOffset != 0)
+ {
+ _stringDictionary[nSubsetOffset.ToString()] = sTemp.ToString();
+ }
+ else
+ {
+ _stringDictionary[nCurOffset.ToString()] = sTemp.ToString();
+ }
+
+ if( bFoundTerminator )
+ {
+ nSubsetOffset = 0;
+ }
+ else
+ {
+ nSubsetOffset = nCurOffset;
+ }
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Indexer which returns the string at a given offset
+ ///
+ public string this[int offset]
+ {
+ get
+ {
+ if(offset == -1)
+ return String.Empty;
+
+ string sTemp = (string)_stringDictionary[ offset.ToString() ];
+
+ if(sTemp == null)
+ return String.Empty;
+
+ return sTemp;
+ }
+ }
+
+ ///
+ /// Indexer which returns the string at a given offset
+ ///
+ public string this[string offset]
+ {
+ get
+ {
+ if(offset == "-1")
+ return String.Empty;
+
+ string sTemp = (string)_stringDictionary[ offset ];
+
+ 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;
+ _stringDictionary = null;
+ }
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs
new file mode 100644
index 00000000000..042e243f5e1
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs
@@ -0,0 +1,821 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Globalization;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class CHMSystem reads the #SYSTEM file of the chm and stores its settings
+ ///
+ internal sealed class CHMSystem : 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 file version
+ ///
+ private int _fileVersion = 0;
+ ///
+ /// Internal member storing the contents file path
+ ///
+ private string _contentsFile = "";
+ ///
+ /// Internal member storing the index file path
+ ///
+ private string _indexFile = "";
+ ///
+ /// Internal member storing the default help topic
+ ///
+ private string _defaultTopic = "";
+ ///
+ /// Internal member storing the help-window title
+ ///
+ private string _title = "";
+ ///
+ /// Internal flag if dbcs is on
+ ///
+ private bool _dbcs = false;
+ ///
+ /// Internal flag if fulltext search is enabled
+ ///
+ private bool _fullTextSearch = false;
+ ///
+ /// Internal flag if KLinks are in the file
+ ///
+ private bool _hasKLinks = false;
+ ///
+ /// Internal flag if ALinks are in the file
+ ///
+ private bool _hasALinks = false;
+ ///
+ /// Internal member storing the name of the default window
+ ///
+ private string _defaultWindow = "";
+ ///
+ /// Internal member storing the filename of the compiled file
+ ///
+ private string _compileFile = "";
+ ///
+ /// Internal flag storing the offset value of the binary index
+ ///
+ private uint _binaryIndexURLTableID = 0;
+ ///
+ /// Inernal member storing the compiler version this file was compiled
+ ///
+ private string _compilerVersion = "";
+ ///
+ /// Internal flag storing the offset value of the binary TOC
+ ///
+ private uint _binaryTOCURLTableID = 0;
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+ ///
+ /// Internal member storing the default fontface, size, charset
+ ///
+ private string _defaultFont = "";
+ ///
+ /// Internal member storing the culture info of the file
+ ///
+ private CultureInfo _culture;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the #SYSTEM file
+ /// associated chm file
+ public CHMSystem(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+ DecodeData();
+
+ if(_culture == null)
+ {
+ // Set the text encoder of the chm file to the read charset/codepage
+ _associatedFile.TextEncoding = Encoding.GetEncoding( this.CodePage );
+ }
+ }
+
+ ///
+ /// 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);
+
+ // First entry = DWORD for version number
+ _fileVersion = (int) binReader.ReadInt32();
+
+ while( (memStream.Position < memStream.Length) && (bRet) )
+ {
+ bRet &= DecodeEntry(ref binReader);
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Decodes an #system file entry
+ ///
+ /// binary reader reference
+ /// true if succeeded
+ private bool DecodeEntry(ref BinaryReader binReader)
+ {
+ bool bRet = true;
+
+ int code = (int) binReader.ReadInt16(); // entry code, WORD
+ int length = (int) binReader.ReadInt16(); // length of entry
+
+ switch(code)
+ {
+ case 0:
+ {
+ _contentsFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 1:
+ {
+ _indexFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 2:
+ {
+ _defaultTopic = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 3:
+ {
+ _title = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 4:
+ {
+ int nTemp = 0;
+ nTemp = binReader.ReadInt32(); // read DWORD LCID
+ _culture = new CultureInfo(nTemp);
+
+ if(_culture != null)
+ _associatedFile.TextEncoding = Encoding.GetEncoding(_culture.TextInfo.ANSICodePage);
+
+ nTemp = binReader.ReadInt32(); // read DWORD DBCS
+ _dbcs = (nTemp == 1);
+
+ nTemp = binReader.ReadInt32(); // read DWORD Fulltext search
+ _fullTextSearch = (nTemp == 1);
+
+ nTemp = binReader.ReadInt32(); // read DWORD has klinks
+ _hasKLinks = (nTemp != 0);
+
+ nTemp = binReader.ReadInt32(); // read DWORD has alinks
+ _hasALinks = (nTemp != 0);
+
+ // read the rest of code 4 (not important for us)
+ byte[] temp = new byte[length-(5*4)];
+ temp = binReader.ReadBytes(length-(5*4));
+ };break;
+ case 5:
+ {
+ _defaultWindow = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 6:
+ {
+ _compileFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 7:
+ {
+ if(_fileVersion > 2)
+ {
+ _binaryIndexURLTableID = (uint) binReader.ReadInt32();
+ }
+ else
+ {
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ }
+ };break;
+ case 8:
+ {
+ // abbreviation (not interresting for us)
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ };break;
+ case 9:
+ {
+ _compilerVersion = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+ };break;
+ case 10:
+ {
+ // timestamp of the file (not interresting for us)
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ };break;
+ case 11:
+ {
+ if(_fileVersion > 2)
+ {
+ _binaryTOCURLTableID = (uint) binReader.ReadInt32();
+ }
+ else
+ {
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ }
+ };break;
+ case 12:
+ {
+ // number of information bytes
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ };break;
+ case 13:
+ {
+ // copy of file #idxhdr
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ };break;
+ case 14:
+ {
+ // custom tabs for HH viewer
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ };break;
+ case 15:
+ {
+ // a checksum
+ byte[] read = binReader.ReadBytes(length);
+ int i=read.Length;
+ };break;
+ case 16:
+ {
+ // Default Font=string,number,number
+ // The string is the name of the font, the first number is the
+ // point size & the last number is the character set used by the font.
+ // For acceptable values see *_CHARSET defines in wingdi.h from the
+ // Windows SDK or the same file in MinGW or Wine.
+ // Most of the time you will only want to use 0, which is the value for ANSI,
+ // which is the subset of ASCII used by Windows.
+ _defaultFont = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
+
+ };break;
+ default:
+ {
+ byte[] temp = new byte[length];
+ temp = binReader.ReadBytes(length);
+ //bRet = false;
+ int i=temp.Length;
+ };break;
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Reads all HHC files and checks which one has the global object tag.
+ /// This hhc file will be returned as master hhc
+ ///
+ /// list of topics containing the extension hhc
+ /// true if the arraylist contains topic items
+ /// the filename of the found master toc
+ private string GetMasterHHC(ArrayList hhcTopics, bool TopicItemArrayList)
+ {
+ string sRet = "";
+
+ if( (hhcTopics!=null) && (hhcTopics.Count > 0) )
+ {
+ if( TopicItemArrayList )
+ {
+ if(hhcTopics.Count == 1)
+ {
+ sRet = ((TopicEntry)hhcTopics[0]).Locale;
+ }
+ else
+ {
+ foreach(TopicEntry curEntry in hhcTopics)
+ {
+ CHMStream.CHMStream iw=null;
+ MemoryStream fileObject=null;
+
+ if( _associatedFile.CurrentStorageWrapper == null)
+ {
+ iw=new CHMStream.CHMStream();
+ iw.OpenCHM(_associatedFile.ChmFilePath);
+ }
+ else
+ {
+ iw = _associatedFile.CurrentStorageWrapper;
+ }
+
+ fileObject = iw.OpenStream(curEntry.Locale);
+ if( fileObject != null)
+ {
+ string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
+ fileObject.Close();
+
+ if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
+ {
+ sRet = curEntry.Locale;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if(hhcTopics.Count == 1)
+ {
+ sRet = ((string)hhcTopics[0]);
+ }
+ else
+ {
+ foreach(string curEntry in hhcTopics)
+ {
+ CHMStream.CHMStream iw=null;
+ MemoryStream fileObject=null;
+
+ if( _associatedFile.CurrentStorageWrapper == null)
+ {
+ iw=new CHMStream.CHMStream();
+ iw.OpenCHM(_associatedFile.ChmFilePath);
+ }
+ else
+ {
+ iw = _associatedFile.CurrentStorageWrapper;
+ }
+
+ fileObject = iw.OpenStream(curEntry);
+ if( fileObject != null)
+ {
+ string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
+ fileObject.Close();
+
+ if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
+ {
+ sRet = curEntry;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return sRet;
+ }
+
+ ///
+ /// Gets the file version of the chm file.
+ /// 2 for Compatibility=1.0, 3 for Compatibility=1.1
+ ///
+ public int FileVersion
+ {
+ get { return _fileVersion; }
+ }
+
+ ///
+ /// Gets the contents file name
+ ///
+ public string ContentsFile
+ {
+ get
+ {
+ if( BinaryTOC ) // if the file contains a binary TOC
+ {
+ // make sure the CHMFile instance exists and has loaded the file #URLTBL
+ if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
+ {
+ // Get an url-table entry by its unique id
+ UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryTOCURLTableID );
+ if(entry != null)
+ {
+ // entry found, return the url ( = filename )
+ return entry.URL;
+ }
+ }
+ }
+ else
+ {
+ if(_contentsFile.Length <= 0)
+ {
+ string sCheck = "Table of Contents.hhc"; // default HHP contents filename
+
+ if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
+ {
+ TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
+
+ if( te == null)
+ {
+ sCheck = "toc.hhc"; // default HHP contents filename
+
+ te = _associatedFile.TopicsFile.GetByLocale( sCheck );
+
+ if( te == null)
+ {
+ sCheck = CompileFile + ".hhc";
+
+ te = _associatedFile.TopicsFile.GetByLocale( sCheck );
+
+ if( te == null)
+ {
+ ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhc");
+
+ if( arrExt == null )
+ {
+ arrExt = _associatedFile.EnumFilesByExtension("hhc");
+
+ if( arrExt == null )
+ {
+ Debug.WriteLine("CHMSystem.ContentsFile - Failed, contents file not found !");
+ }
+ else
+ {
+ if(arrExt.Count > 1)
+ {
+ sCheck = GetMasterHHC(arrExt, false);
+ _contentsFile = sCheck;
+ }
+ else
+ {
+ _contentsFile = ((string)arrExt[0]);
+ sCheck = _contentsFile;
+ }
+ }
+ }
+ else
+ {
+ if(arrExt.Count > 1)
+ {
+ sCheck = GetMasterHHC(arrExt, true);
+ _contentsFile = sCheck;
+ }
+ else
+ {
+ _contentsFile = ((TopicEntry)arrExt[0]).Locale;
+ sCheck = _contentsFile;
+ }
+ }
+ }
+ else
+ {
+ _contentsFile = sCheck;
+ }
+ }
+ else
+ {
+ _contentsFile = sCheck;
+ }
+ }
+ else
+ {
+ _contentsFile = sCheck;
+ }
+ }
+
+ return sCheck;
+ }
+ }
+
+ return _contentsFile;
+ }
+ }
+
+ ///
+ /// Gets the index file name
+ ///
+ public string IndexFile
+ {
+ get
+ {
+ if( BinaryIndex ) // if the file contains a binary index
+ {
+ // make sure the CHMFile instance exists and has loaded the file #URLTBL
+ if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
+ {
+ // Get an url-table entry by its unique id
+ UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryIndexURLTableID );
+ if(entry != null)
+ {
+ // entry found, return the url ( = filename )
+ return entry.URL;
+ }
+ }
+ }
+ else
+ {
+ if(_indexFile.Length <= 0)
+ {
+ string sCheck = "Index.hhk"; // default HHP index filename
+
+ if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
+ {
+ TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
+
+ if( te == null)
+ {
+ sCheck = CompileFile + ".hhk";
+
+ te = _associatedFile.TopicsFile.GetByLocale( sCheck );
+
+ if( te == null)
+ {
+ ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhk");
+
+ if( arrExt == null )
+ {
+ Debug.WriteLine("CHMSystem.IndexFile - Failed, index file not found !");
+ }
+ else
+ {
+ _indexFile = ((TopicEntry)arrExt[0]).Locale;
+ sCheck = _indexFile;
+ }
+ }
+ else
+ {
+ _indexFile = sCheck;
+ }
+ }
+ else
+ {
+ _indexFile = sCheck;
+ }
+ }
+
+ return sCheck;
+ }
+ }
+ return _indexFile;
+ }
+ }
+
+ ///
+ /// Sets the default topic of this file
+ ///
+ /// new local value of the topic
+ internal void SetDefaultTopic(string local)
+ {
+ _defaultTopic = local;
+ }
+
+ ///
+ /// Gets the default help topic
+ ///
+ public string DefaultTopic
+ {
+ get { return _defaultTopic; }
+ }
+
+ ///
+ /// Gets the title of the help window
+ ///
+ public string Title
+ {
+ get { return _title; }
+ }
+
+ ///
+ /// Gets the flag if DBCS is in use
+ ///
+ public bool DBCS
+ {
+ get { return _dbcs; }
+ }
+
+ ///
+ /// Gets the flag if full-text-search is available
+ ///
+ public bool FullTextSearch
+ {
+ get { return _fullTextSearch; }
+ }
+
+ ///
+ /// Gets the flag if the file has ALinks
+ ///
+ public bool HasALinks
+ {
+ get { return _hasALinks; }
+ }
+
+ ///
+ /// Gets the flag if the file has KLinks
+ ///
+ public bool HasKLinks
+ {
+ get { return _hasKLinks; }
+ }
+
+ ///
+ /// Gets the default window name
+ ///
+ public string DefaultWindow
+ {
+ get { return _defaultWindow; }
+ }
+
+ ///
+ /// Gets the file name of the compile file
+ ///
+ public string CompileFile
+ {
+ get { return _compileFile; }
+ }
+
+ ///
+ /// Gets the id of the binary index in the url table
+ ///
+ public uint BinaryIndexURLTableID
+ {
+ get { return _binaryIndexURLTableID; }
+ }
+
+ ///
+ /// Gets the flag if the chm has a binary index file
+ ///
+ public bool BinaryIndex
+ {
+ get { return (_binaryIndexURLTableID>0); }
+ }
+
+ ///
+ /// Gets the flag if the chm has a binary index file
+ ///
+ public string CompilerVersion
+ {
+ get { return _compilerVersion; }
+ }
+
+ ///
+ /// Gets the id of the binary toc in the url table
+ ///
+ public uint BinaryTOCURLTableID
+ {
+ get { return _binaryTOCURLTableID; }
+ }
+
+ ///
+ /// Gets the flag if the chm has a binary toc file
+ ///
+ public bool BinaryTOC
+ {
+ get { return (_binaryTOCURLTableID>0); }
+ }
+
+ ///
+ /// Gets the font face of the read font property.
+ /// Empty string for default font.
+ ///
+ public string FontFace
+ {
+ get
+ {
+ if( _defaultFont.Length > 0)
+ {
+ string [] fontSplit = _defaultFont.Split( new char[]{','});
+ if(fontSplit.Length > 0)
+ return fontSplit[0].Trim();
+ }
+
+ return "";
+ }
+ }
+
+ ///
+ /// Gets the font size of the read font property.
+ /// 0 for default font size
+ ///
+ public double FontSize
+ {
+ get
+ {
+ if( _defaultFont.Length > 0)
+ {
+ string [] fontSplit = _defaultFont.Split( new char[]{','});
+ if(fontSplit.Length > 1)
+ return double.Parse(fontSplit[1].Trim());
+ }
+
+ return 0.0;
+ }
+ }
+
+ ///
+ /// Gets the character set of the read font property
+ /// 1 for default
+ ///
+ public int CharacterSet
+ {
+ get
+ {
+ if( _defaultFont.Length > 0)
+ {
+ string [] fontSplit = _defaultFont.Split( new char[]{','});
+ if(fontSplit.Length > 2)
+ return Int32.Parse(fontSplit[2].Trim());
+ }
+
+ return 0;
+ }
+ }
+
+ ///
+ /// Gets the codepage depending on the read font property
+ ///
+ public int CodePage
+ {
+ get
+ {
+ // if we've read a LCID from the system file
+ // ignore the font-property settings and return
+ // the codepage generated from the culture info
+ if(_culture != null)
+ {
+ return _culture.TextInfo.ANSICodePage;
+ }
+
+ int nRet = 1252; // default codepage windows-1252
+
+ int nCSet = CharacterSet;
+
+ switch(nCSet)
+ {
+ case 0x00: nRet = 1252;break; // ANSI_CHARSET
+ case 0xCC: nRet = 1251;break; // RUSSIAN_CHARSET
+ case 0xEE: nRet = 1250;break; // EE_CHARSET
+ case 0xA1: nRet = 1253;break; // GREEK_CHARSET
+ case 0xA2: nRet = 1254;break; // TURKISH_CHARSET
+ case 0xBA: nRet = 1257;break; // BALTIC_CHARSET
+ case 0xB1: nRet = 1255;break; // HEBREW_CHARSET
+ case 0xB2: nRet = 1256;break; // ARABIC_CHARSET
+ case 0x80: nRet = 932;break; // SHIFTJIS_CHARSET
+ case 0x81: nRet = 949;break; // HANGEUL_CHARSET
+ case 0x86: nRet = 936;break; // GB2313_CHARSET
+ case 0x88: nRet = 950;break; // CHINESEBIG5_CHARSET
+ }
+
+ return nRet;
+ }
+ }
+
+ ///
+ /// Gets the assiciated culture info
+ ///
+ public CultureInfo Culture
+ {
+ get { return _culture; }
+ }
+
+ ///
+ /// 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;
+ }
+
+
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs
new file mode 100644
index 00000000000..d6ca1aa02c1
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs
@@ -0,0 +1,288 @@
+using System;
+using System.IO;
+using System.Collections;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class CHMTocidx implements functions to decode the #TOCIDX internal file.
+ ///
+ internal sealed class CHMTocidx : IDisposable
+ {
+ ///
+ /// Constant specifying the size of the data 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 memebr storing the offset to the 20/28 byte structs
+ ///
+ private int _offset2028 = 0;
+ ///
+ /// Internal member storing the offset to the 16 byte structs
+ ///
+ private int _offset16structs = 0;
+ ///
+ /// Internal member storing the number of 16 byte structs
+ ///
+ private int _numberOf16structs = 0;
+ ///
+ /// Internal member storing the offset to the topic list
+ ///
+ private int _offsetOftopics = 0;
+ ///
+ /// Internal member storing the toc
+ ///
+ private ArrayList _toc = new ArrayList();
+ ///
+ /// Internal member for offset seeking
+ ///
+ private Hashtable _offsetTable = new Hashtable();
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the #TOCIDX file
+ /// associated chm file
+ public CHMTocidx(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+ DecodeData();
+
+ // clear internal binary data after extraction
+ _binaryFileData = null;
+ }
+
+ ///
+ /// Decodes the binary file data and fills the internal properties
+ ///
+ /// true if succeeded
+ private bool DecodeData()
+ {
+ _toc = new ArrayList();
+ _offsetTable = new Hashtable();
+
+ bool bRet = true;
+
+ MemoryStream memStream = new MemoryStream(_binaryFileData);
+ BinaryReader binReader = new BinaryReader(memStream);
+
+ int nCurOffset = 0;
+
+ _offset2028 = binReader.ReadInt32();
+ _offset16structs = binReader.ReadInt32();
+ _numberOf16structs = binReader.ReadInt32();
+ _offsetOftopics = binReader.ReadInt32();
+
+ binReader.BaseStream.Seek( _offset2028, SeekOrigin.Begin );
+
+ if( RecursivelyBuildTree(ref binReader, _offset2028, _toc, null) )
+ {
+ binReader.BaseStream.Seek( _offset16structs, SeekOrigin.Begin );
+ nCurOffset = (int)binReader.BaseStream.Position;
+
+ for(int i=0; i < _numberOf16structs; i++)
+ {
+ int tocOffset = binReader.ReadInt32();
+ int sqNr = binReader.ReadInt32();
+ int topOffset = binReader.ReadInt32();
+ int hhctopicIdx = binReader.ReadInt32();
+
+ nCurOffset = (int)binReader.BaseStream.Position;
+
+ int topicIdx = -1;
+ // if the topic offset is within the range of the stream
+ // and is >= the offset of the first topic dword
+ if((topOffset < (binReader.BaseStream.Length - 4)) && (topOffset >= _offsetOftopics))
+ {
+ // read the index of the topic for this item
+ binReader.BaseStream.Seek( topOffset, SeekOrigin.Begin);
+ topicIdx = binReader.ReadInt32();
+ binReader.BaseStream.Seek( nCurOffset, SeekOrigin.Begin);
+
+
+ TOCItem item = (TOCItem)_offsetTable[tocOffset.ToString()];
+ if( item != null)
+ {
+ if(( topicIdx < _associatedFile.TopicsFile.TopicTable.Count)&&(topicIdx>=0))
+ {
+ TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile.TopicTable[topicIdx]);
+ if( (te != null) && (item.TopicOffset < 0) )
+ {
+ item.TopicOffset = te.EntryOffset;
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Recursively reads the binary toc tree from the file
+ ///
+ /// reference to binary reader
+ /// offset of the first node in the current level
+ /// arraylist of TOCItems for the current level
+ /// parent item for the item
+ /// Returns true if succeeded
+ private bool RecursivelyBuildTree(ref BinaryReader binReader, int NodeOffset, ArrayList level, TOCItem parentItem)
+ {
+ bool bRet = true;
+ int nextOffset=0;
+
+ int nReadOffset = (int)binReader.BaseStream.Position;
+
+ binReader.BaseStream.Seek(NodeOffset, SeekOrigin.Begin);
+
+ do
+ {
+ int nCurOffset = (int)binReader.BaseStream.Position;
+
+ int unkn1 = binReader.ReadInt16(); // unknown
+ int unkn2 = binReader.ReadInt16(); // unknown
+
+ int flag = binReader.ReadInt32();
+
+ int nFolderAdd = 0;
+
+ if((_associatedFile != null) && (_associatedFile.ImageTypeFolder))
+ {
+ // get the value which should be added, to display folders instead of books
+ if(HtmlHelpSystem.UseHH2TreePics)
+ nFolderAdd = 8;
+ else
+ nFolderAdd = 4;
+ }
+
+ int nFolderImgIdx = (HtmlHelpSystem.UseHH2TreePics ? (TOCItem.STD_FOLDER_HH2+nFolderAdd) : (TOCItem.STD_FOLDER_HH1+nFolderAdd));
+ int nFileImgIdx = (HtmlHelpSystem.UseHH2TreePics ? TOCItem.STD_FILE_HH2 : TOCItem.STD_FILE_HH1);
+
+ int stdImage = ((flag & 0x4)!=0) ? nFolderImgIdx : nFileImgIdx;
+
+ int stringOffset = binReader.ReadInt32();
+
+ int ParentOffset = binReader.ReadInt32();
+ nextOffset = binReader.ReadInt32();
+
+ int firstChildOffset = 0;
+ int unkn3=0;
+
+ if( (flag&0x4)!=0 )
+ {
+ firstChildOffset = binReader.ReadInt32();
+ unkn3 = binReader.ReadInt32(); // unknown
+ }
+
+ TOCItem newItem = new TOCItem();
+ newItem.ImageIndex = stdImage;
+ newItem.Offset = nCurOffset;
+ newItem.OffsetNext = nextOffset;
+ newItem.AssociatedFile = _associatedFile;
+ newItem.TocMode = DataMode.Binary;
+ newItem.Parent = parentItem;
+
+ if( (flag&0x08) == 0)
+ {
+ // toc item doesn't have a local value (=> stringOffset = offset of strings file)
+ newItem.Name = _associatedFile.StringsFile[stringOffset];
+ }
+ else
+ {
+ // this item has a topic entry (=> stringOffset = index of topic entry)
+ if((stringOffset < _associatedFile.TopicsFile.TopicTable.Count) && (stringOffset >= 0))
+ {
+ TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile.TopicTable[stringOffset]);
+ if(te != null)
+ {
+ newItem.TopicOffset = te.EntryOffset;
+ }
+ }
+ }
+
+ _offsetTable[nCurOffset.ToString()] = newItem;
+
+ // if this item has children (firstChildOffset > 0)
+ if( firstChildOffset > 0)
+ {
+ bRet &= RecursivelyBuildTree(ref binReader, firstChildOffset, newItem.Children, newItem);
+ }
+
+ level.Add( newItem );
+
+ if(nCurOffset != nextOffset)
+ binReader.BaseStream.Seek(nextOffset, SeekOrigin.Begin);
+
+ }while(nextOffset != 0);
+
+ binReader.BaseStream.Seek(nReadOffset, SeekOrigin.Begin);
+
+ return bRet;
+ }
+
+ ///
+ /// Gets the internal read toc
+ ///
+ internal ArrayList TOC
+ {
+ get { return _toc; }
+ }
+
+ ///
+ /// 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;
+ _toc = null;
+ _offsetTable = null;
+ }
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs
new file mode 100644
index 00000000000..ce1451b53ef
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs
@@ -0,0 +1,235 @@
+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;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs
new file mode 100644
index 00000000000..b87eebab58e
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs
@@ -0,0 +1,308 @@
+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;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs
new file mode 100644
index 00000000000..79f718b84ce
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs
@@ -0,0 +1,245 @@
+using System;
+using System.IO;
+using System.Collections;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class CHMUrltable implements methods to decode the #URLTBL internal file.
+ ///
+ internal sealed class CHMUrltable : IDisposable
+ {
+ ///
+ /// Constant specifying the size of the data blocks
+ ///
+ private const int BLOCK_SIZE = 0x1000;
+ ///
+ /// Constant specifying the number of records per block
+ ///
+ private const int RECORDS_PER_BLOCK = 341;
+ ///
+ /// 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 table
+ ///
+ private ArrayList _urlTable = new ArrayList();
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the #URLTBL file
+ /// associated chm file
+ public CHMUrltable(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+ DecodeData();
+
+ // clear internal binary data after extraction
+ _binaryFileData = null;
+ }
+
+ ///
+ /// Standard constructor
+ ///
+ internal CHMUrltable()
+ {
+ }
+
+ #region Data dumping
+ ///
+ /// Dump the class data to a binary writer
+ ///
+ /// writer to write the data
+ internal void Dump(ref BinaryWriter writer)
+ {
+ writer.Write( _urlTable.Count );
+ foreach(UrlTableEntry curItem in _urlTable)
+ {
+ 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(UrlTableEntry curEntry in _urlTable)
+ {
+ 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) )
+ {
+ 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);
+
+ for(int i=0; i < RECORDS_PER_BLOCK; i++)
+ {
+ int recordOffset = blockOffset + (int)memStream.Position;
+
+ uint nuniqueID = (uint) binReader.ReadInt32(); // unknown dword
+ int ntopicsIdx = binReader.ReadInt32();
+ int urlstrOffset = binReader.ReadInt32();
+
+ UrlTableEntry newEntry = new UrlTableEntry(nuniqueID, recordOffset, ntopicsIdx, urlstrOffset, _associatedFile);
+ _urlTable.Add(newEntry);
+
+ if( memStream.Position >= memStream.Length)
+ break;
+ }
+
+ if(dataBlock.Length == BLOCK_SIZE)
+ binReader.ReadInt32();
+
+ return bRet;
+ }
+
+ ///
+ /// Gets the arraylist containing all urltable entries.
+ ///
+ public ArrayList UrlTable
+ {
+ get
+ {
+ return _urlTable;
+ }
+ }
+
+ ///
+ /// Gets the urltable entry of a given offset
+ ///
+ public UrlTableEntry this[int offset]
+ {
+ get
+ {
+ foreach(UrlTableEntry curEntry in _urlTable)
+ if(curEntry.EntryOffset == offset)
+ return curEntry;
+
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the urltable entry of a given uniqueID
+ ///
+ public UrlTableEntry GetByUniqueID(uint uniqueID)
+ {
+ foreach(UrlTableEntry curEntry in UrlTable)
+ {
+ if(curEntry.UniqueID == uniqueID)
+ return curEntry;
+ }
+
+ 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;
+ _urlTable = null;
+ }
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs b/irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs
new file mode 100644
index 00000000000..f42fb98ee11
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs
@@ -0,0 +1,395 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Diagnostics;
+using System.Collections.Specialized;
+
+using ICSharpCode.SharpZipLib;
+using ICSharpCode.SharpZipLib.Zip;
+using ICSharpCode.SharpZipLib.Zip.Compression;
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+
+using HtmlHelp;
+// using HtmlHelp.Storage;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// Enumeration for specifying the dumping compression
+ ///
+ public enum DumpCompression
+ {
+ ///
+ /// None - no data compression will be used.
+ /// Fastest but most memory intensive
+ ///
+ None = 0,
+ ///
+ /// Minimum - a minimum data compression will be used.
+ /// Fast but not much data reduction
+ ///
+ Minimum = 1,
+ ///
+ /// Medium - a medium data compression will be used.
+ /// Slower but medium data reduction
+ ///
+ Medium = 2,
+ ///
+ /// Maximum - a maximum data compression will be used.
+ /// Slowest but maximum data reduction
+ ///
+ Maximum = 3
+ }
+
+ ///
+ /// Flags which specify which data should be dumped
+ ///
+ [FlagsAttribute()]
+ public enum DumpingFlags
+ {
+ ///
+ /// DumpTextTOC - if this flag is set, text-based TOCs (sitemap format) will be dumped
+ ///
+ DumpTextTOC = 1,
+ ///
+ /// DumpBinaryTOC - if this flag is set, binary TOCs will be dumped
+ ///
+ DumpBinaryTOC = 2,
+ ///
+ /// DumpTextIndex - if this flag is set, the text-based index (sitemap format) will be dumped
+ ///
+ DumpTextIndex = 4,
+ ///
+ /// DumpBinaryIndex - if this flag is set, the binary index will be dumped
+ ///
+ DumpBinaryIndex = 8,
+ ///
+ /// DumpStrings - if this flag is set, the internal #STRINGS file will be dumped
+ ///
+ DumpStrings = 16,
+ ///
+ /// DumpUrlStr - if this flag is set, the internal #URLSTR file will be dumped
+ ///
+ DumpUrlStr = 32,
+ ///
+ /// DumpUrlTbl - if this flag is set, the internal #URLTBL file will be dumped
+ ///
+ DumpUrlTbl = 64,
+ ///
+ /// DumpTopics - if this flag is set, the internal #TOPICS file will be dumped
+ ///
+ DumpTopics = 128,
+ ///
+ /// DumpFullText - if this flag is set, the internal $FIftiMain file will be dumped
+ ///
+ DumpFullText = 256
+ }
+
+ ///
+ /// The class DumpingInfo implements information properties for the CHMFile class
+ /// if and how data dumping should be used.
+ ///
+ public sealed class DumpingInfo
+ {
+ public bool m_bAllowSaveDump=true;
+
+ private readonly static BitVector32.Section DumpFlags = BitVector32.CreateSection(512);
+
+ private const string _dumpHeader = "HtmlHelpSystem dump file 1.0";
+
+ private string _outputDir = ""; // emtpy string means, same directory as chm file
+ private DumpCompression _compressionLevel = DumpCompression.Maximum;
+ private CHMFile _chmFile = null;
+
+ private DeflaterOutputStream _outputStream = null;
+ private InflaterInputStream _inputStream = null;
+
+ private BinaryWriter _writer = null;
+ private BinaryReader _reader = null;
+
+ private BitVector32 _flags;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// Combine flag values to specify which data should be dumped.
+ /// output directory. emtpy string means,
+ /// same directory as chm file (only if destination = ExternalFile)
+ /// compression which should be used
+ public DumpingInfo(DumpingFlags flags, string outputDir, DumpCompression compressionLevel)
+ {
+ _flags = new BitVector32(0);
+ int i = _flags[DumpFlags];
+ _flags[DumpFlags] = i | (int)flags;
+
+ _outputDir = outputDir;
+ _compressionLevel = compressionLevel;
+ }
+
+ ///
+ /// Gets the flag if text-based TOCs will be written to the dumping file
+ ///
+ public bool DumpTextTOC
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTextTOC) != 0); }
+ }
+
+ ///
+ /// Gets the flag if binary TOCs will be written to the dumping file
+ ///
+ public bool DumpBinaryTOC
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpBinaryTOC) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the text-based index will be written to the dumping file
+ ///
+ public bool DumpTextIndex
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTextIndex) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the binary index will be written to the dumping file
+ ///
+ public bool DumpBinaryIndex
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpBinaryIndex) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the #STRINGS file will be written to the dumping file
+ ///
+ public bool DumpStrings
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpStrings) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the #URLSTR file will be written to the dumping file
+ ///
+ public bool DumpUrlStr
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpUrlStr) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the #URLTBL file will be written to the dumping file
+ ///
+ public bool DumpUrlTbl
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpUrlTbl) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the #TOPICS file will be written to the dumping file
+ ///
+ public bool DumpTopics
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTopics) != 0); }
+ }
+
+ ///
+ /// Gets the flag if the $FIftiMain file will be written to the dumping file
+ ///
+ public bool DumpFullText
+ {
+ get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpFullText) != 0); }
+ }
+
+ ///
+ /// Gets the dump output directory.
+ ///
+ /// emtpy string means, same directory as chm file
+ /// If Destination is set to DumpingOutput.InternalFile this property will be ignored
+ public string OutputDir
+ {
+ get { return _outputDir; }
+ }
+
+ ///
+ /// The compression level used.
+ ///
+ public DumpCompression CompressionLevel
+ {
+ get { return _compressionLevel; }
+ }
+
+ ///
+ /// Gets/Sets the CHMFile instance associated with this object
+ ///
+ internal CHMFile ChmFile
+ {
+ get { return _chmFile; }
+ set { _chmFile = value; }
+ }
+
+ ///
+ /// Translates the compression level to the deflater constants
+ ///
+ private int CompLvl
+ {
+ get
+ {
+ switch(CompressionLevel)
+ {
+ case DumpCompression.None: return Deflater.NO_COMPRESSION;
+ case DumpCompression.Minimum: return Deflater.BEST_SPEED;
+ case DumpCompression.Medium: return Deflater.DEFAULT_COMPRESSION;
+ case DumpCompression.Maximum: return Deflater.BEST_COMPRESSION;
+ }
+
+ return Deflater.BEST_COMPRESSION;
+ }
+ }
+
+ ///
+ /// Checks if a dump exists
+ ///
+ internal bool DumpExists
+ {
+ get
+ {
+ if(_flags[DumpFlags] == 0)
+ return false;
+
+ // we have a reader or writer to the dump so it must exist
+ if( (_reader != null) || (_writer != null) )
+ return true;
+
+ string sDmpFile = _chmFile.ChmFilePath;
+ sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");
+
+ return File.Exists(sDmpFile);
+ }
+ }
+
+ ///
+ /// Gets a binary writer instance which allows you to write to the dump
+ ///
+ internal BinaryWriter Writer
+ {
+ get
+ {
+ if (m_bAllowSaveDump==false)
+ return null;
+
+ if(_flags[DumpFlags] == 0)
+ throw new InvalidOperationException("Nothing to dump. No flags have been set !");
+
+ if(_reader != null)
+ throw new InvalidOperationException("Can't write and read at the same time !");
+
+ if(_chmFile == null)
+ throw new InvalidOperationException("Only usable with an associated CHMFile instance !");
+
+ if(_writer==null)
+ {
+ string sDmpFile = _chmFile.ChmFilePath;
+ sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");
+ StreamWriter stream = new StreamWriter(sDmpFile, false, _chmFile.TextEncoding);
+
+ // write header info uncompressed
+ BinaryWriter _hwriter = new BinaryWriter(stream.BaseStream);
+ _hwriter.Write(_dumpHeader);
+ _hwriter.Write((int)CompressionLevel);
+
+ if(_compressionLevel == DumpCompression.None)
+ {
+ _writer = new BinaryWriter(stream.BaseStream);
+ }
+ else
+ {
+ _outputStream = new DeflaterOutputStream(stream.BaseStream, new Deflater(CompLvl));
+
+ _writer = new BinaryWriter(_outputStream);
+ }
+ }
+
+ return _writer;
+
+ }
+ }
+
+ ///
+ /// Gets a binary reader which allows you to read from the dump
+ ///
+ internal BinaryReader Reader
+ {
+ get
+ {
+ if(_writer != null)
+ throw new InvalidOperationException("Can't write and read at the same time !");
+
+ if(_chmFile == null)
+ throw new InvalidOperationException("Only usable with an associated CHMFile instance !");
+
+ if(_reader==null)
+ {
+ string sDmpFile = _chmFile.ChmFilePath;
+ sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");
+ StreamReader stream = new StreamReader(sDmpFile, _chmFile.TextEncoding);
+
+ BinaryReader _hReader = new BinaryReader(stream.BaseStream);
+ string sH = _hReader.ReadString();
+
+ if(sH != _dumpHeader)
+ {
+ _hReader.Close();
+ Debug.WriteLine("Unexpected dump-file header !");
+ throw new FormatException("DumpingInfo.Reader - Unexpected dump-file header !");
+ }
+
+ _compressionLevel = (DumpCompression)_hReader.ReadInt32();
+// if(_compressionLevel != (DumpCompression)_hReader.ReadInt32())
+// {
+// _hReader.Close();
+// return null;
+// }
+
+ if(_compressionLevel == DumpCompression.None)
+ {
+ _reader = new BinaryReader(stream.BaseStream);
+ }
+ else
+ {
+ _inputStream = new InflaterInputStream(stream.BaseStream, new Inflater());
+
+ _reader = new BinaryReader(_inputStream);
+ }
+ }
+
+ return _reader;
+ }
+ }
+
+ ///
+ /// Saves data and closes the dump
+ ///
+ /// true if succeed
+ internal bool SaveData()
+ {
+ if (m_bAllowSaveDump==false)
+ return true;
+
+ if(_writer != null)
+ {
+ if(_writer!=null)
+ _writer.Close();
+ _outputStream = null;
+ _writer = null;
+ }
+
+ if(_reader != null)
+ {
+ if(_reader!=null)
+ _reader.Close();
+ _inputStream = null;
+ _reader = null;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs b/irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs
new file mode 100644
index 00000000000..fc1d634d924
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs
@@ -0,0 +1,1131 @@
+using System;
+using System.Data;
+using System.Diagnostics;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.IO;
+using System.Collections;
+using System.Globalization;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class FullTextSearcher implements a fulltext searcher for a single chm file !
+ ///
+ internal sealed class FullTextEngine : IDisposable
+ {
+ #region Internal helper classes
+ ///
+ /// Internal class for decoding the header
+ ///
+ private sealed class FTHeader
+ {
+ ///
+ /// Internal member storing the number of indexed files
+ ///
+ private int _numberOfIndexFiles = 0;
+ ///
+ /// Internal member storing the offset of the root node
+ ///
+ private int _rootOffset = 0;
+ ///
+ /// Internal member storing the index-page count
+ ///
+ private int _pageCount = 0;
+ ///
+ /// Internal member storing the depth of the tree
+ ///
+ private int _depth = 0;
+ ///
+ /// Internal member storing the scale param for document index en-/decoding
+ ///
+ private byte _scaleDocIdx = 0;
+ ///
+ /// Internal member storing the scale param for code-count en-/decoding
+ ///
+ private byte _scaleCodeCnt = 0;
+ ///
+ /// Internal member storing the scale param for location codes en-/decoding
+ ///
+ private byte _scaleLocCodes = 0;
+ ///
+ /// Internal member storing the root param for document index en-/decoding
+ ///
+ private byte _rootDocIdx = 0;
+ ///
+ /// Internal member storing the root param for code-count en-/decoding
+ ///
+ private byte _rootCodeCnt = 0;
+ ///
+ /// Internal member storing the root param for location codes en-/decoding
+ ///
+ private byte _rootLocCodes = 0;
+ ///
+ /// Internal member storing the size of the nodes in bytes
+ ///
+ private int _nodeSize = 0;
+ ///
+ /// Internal member storing the length of the longest word
+ ///
+ private int _lengthOfLongestWord = 0;
+ ///
+ /// Internal member storing the total number of words
+ ///
+ private int _totalNumberOfWords = 0;
+ ///
+ /// Internal member storing the total number of unique words
+ ///
+ private int _numberOfUniqueWords = 0;
+ ///
+ /// Internal member storing the codepage identifier
+ ///
+ private int _codePage = 1252;
+ ///
+ /// Internal member storing the language code id
+ ///
+ private int _lcid = 1033;
+ ///
+ /// Internal member storing the text encoder
+ ///
+ private Encoding _textEncoder = Encoding.Default;
+
+ ///
+ /// Constructor of the header
+ ///
+ /// binary data from which the header will be extracted
+ public FTHeader(byte[] binaryData)
+ {
+ DecodeHeader(binaryData);
+ }
+
+ ///
+ /// Internal constructor for reading from dump
+ ///
+ internal FTHeader()
+ {
+ }
+
+ ///
+ /// Decodes the binary header information and fills the members
+ ///
+ /// binary data from which the header will be extracted
+ private void DecodeHeader(byte[] binaryData)
+ {
+ MemoryStream memStream = new MemoryStream(binaryData);
+ BinaryReader binReader = new BinaryReader(memStream);
+
+ binReader.ReadBytes(4); // 4 unknown bytes
+
+ _numberOfIndexFiles = binReader.ReadInt32(); // number of indexed files
+
+ binReader.ReadInt32(); // unknown
+ binReader.ReadInt32(); // unknown
+
+ _pageCount = binReader.ReadInt32(); // page-count
+ _rootOffset = binReader.ReadInt32(); // file offset of the root node
+ _depth = binReader.ReadInt16(); // depth of the tree
+
+ binReader.ReadInt32(); // unknown
+
+ _scaleDocIdx = binReader.ReadByte();
+ _rootDocIdx = binReader.ReadByte();
+ _scaleCodeCnt = binReader.ReadByte();
+ _rootCodeCnt = binReader.ReadByte();
+ _scaleLocCodes = binReader.ReadByte();
+ _rootLocCodes = binReader.ReadByte();
+
+ if( (_scaleDocIdx != 2) || ( _scaleCodeCnt != 2 ) || ( _scaleLocCodes != 2 ) )
+ {
+ Debug.WriteLine("Unsupported scale for s/r encoding !");
+ throw new InvalidOperationException("Unsupported scale for s/r encoding !");
+ }
+
+ binReader.ReadBytes(10); // unknown
+
+ _nodeSize = binReader.ReadInt32();
+
+ binReader.ReadInt32(); // unknown
+ binReader.ReadInt32(); // not important
+ binReader.ReadInt32(); // not important
+
+ _lengthOfLongestWord = binReader.ReadInt32();
+ _totalNumberOfWords = binReader.ReadInt32();
+ _numberOfUniqueWords = binReader.ReadInt32();
+
+ binReader.ReadInt32(); // not important
+ binReader.ReadInt32(); // not important
+ binReader.ReadInt32(); // not important
+ binReader.ReadInt32(); // not important
+ binReader.ReadInt32(); // not important
+ binReader.ReadInt32(); // not important
+
+ binReader.ReadBytes(24); // not important
+
+ _codePage = binReader.ReadInt32();
+ _lcid = binReader.ReadInt32();
+
+ CultureInfo ci = new CultureInfo(_lcid);
+ _textEncoder = Encoding.GetEncoding( ci.TextInfo.ANSICodePage );
+
+ // rest of header is not important for us
+ }
+
+ ///
+ /// Dump the class data to a binary writer
+ ///
+ /// writer to write the data
+ internal void Dump(ref BinaryWriter writer)
+ {
+ writer.Write( _numberOfIndexFiles );
+ writer.Write( _rootOffset );
+ writer.Write( _pageCount );
+ writer.Write( _depth );
+ writer.Write( _scaleDocIdx );
+ writer.Write( _rootDocIdx );
+ writer.Write( _scaleCodeCnt );
+ writer.Write( _rootCodeCnt );
+ writer.Write( _scaleLocCodes );
+ writer.Write( _rootLocCodes );
+ writer.Write( _nodeSize );
+ writer.Write( _lengthOfLongestWord );
+ writer.Write( _totalNumberOfWords );
+ writer.Write( _numberOfUniqueWords );
+ }
+
+ ///
+ /// Reads the object data from a dump store
+ ///
+ /// reader to read the data
+ internal void ReadDump(ref BinaryReader reader)
+ {
+ _numberOfIndexFiles = reader.ReadInt32();
+ _rootOffset = reader.ReadInt32();
+ _pageCount = reader.ReadInt32();
+ _depth = reader.ReadInt32();
+
+ _scaleDocIdx = reader.ReadByte();
+ _rootDocIdx = reader.ReadByte();
+ _scaleCodeCnt = reader.ReadByte();
+ _rootCodeCnt = reader.ReadByte();
+ _scaleLocCodes = reader.ReadByte();
+ _rootLocCodes = reader.ReadByte();
+
+ _nodeSize = reader.ReadInt32();
+ _lengthOfLongestWord = reader.ReadInt32();
+ _totalNumberOfWords = reader.ReadInt32();
+ _numberOfUniqueWords = reader.ReadInt32();
+ }
+
+ ///
+ /// Gets the number of indexed files
+ ///
+ public int IndexedFileCount
+ {
+ get { return _numberOfIndexFiles; }
+ }
+
+ ///
+ /// Gets the file offset of the root node
+ ///
+ public int RootOffset
+ {
+ get { return _rootOffset; }
+ }
+
+ ///
+ /// Gets the page count
+ ///
+ public int PageCount
+ {
+ get { return _pageCount; }
+ }
+
+ ///
+ /// Gets the index depth
+ ///
+ public int Depth
+ {
+ get { return _depth; }
+ }
+
+ ///
+ /// Gets the scale param for document index en-/decoding
+ ///
+ /// The scale and root method of integer encoding needs two parameters,
+ /// which I'll call s (scale) and r (root size).
+ /// The integer is encoded as two parts, p (prefix) and q (actual bits).
+ /// p determines how many bits are stored, as well as implicitly determining
+ /// the high-order bit of the integer.
+ public byte ScaleDocumentIndex
+ {
+ get { return _scaleDocIdx; }
+ }
+
+ ///
+ /// Gets the root param for the document index en-/decoding
+ ///
+ /// The scale and root method of integer encoding needs two parameters,
+ /// which I'll call s (scale) and r (root size).
+ /// The integer is encoded as two parts, p (prefix) and q (actual bits).
+ /// p determines how many bits are stored, as well as implicitly determining
+ /// the high-order bit of the integer.
+ public byte RootDocumentIndex
+ {
+ get { return _rootDocIdx; }
+ }
+
+ ///
+ /// Gets the scale param for the code-count en-/decoding
+ ///
+ /// The scale and root method of integer encoding needs two parameters,
+ /// which I'll call s (scale) and r (root size).
+ /// The integer is encoded as two parts, p (prefix) and q (actual bits).
+ /// p determines how many bits are stored, as well as implicitly determining
+ /// the high-order bit of the integer.
+ public byte ScaleCodeCount
+ {
+ get { return _scaleCodeCnt; }
+ }
+
+ ///
+ /// Gets the root param for the code-count en-/decoding
+ ///
+ /// The scale and root method of integer encoding needs two parameters,
+ /// which I'll call s (scale) and r (root size).
+ /// The integer is encoded as two parts, p (prefix) and q (actual bits).
+ /// p determines how many bits are stored, as well as implicitly determining
+ /// the high-order bit of the integer.
+ public byte RootCodeCount
+ {
+ get { return _rootCodeCnt; }
+ }
+
+ ///
+ /// Gets the scale param for the location codes en-/decoding
+ ///
+ /// The scale and root method of integer encoding needs two parameters,
+ /// which I'll call s (scale) and r (root size).
+ /// The integer is encoded as two parts, p (prefix) and q (actual bits).
+ /// p determines how many bits are stored, as well as implicitly determining
+ /// the high-order bit of the integer.
+ public byte ScaleLocationCodes
+ {
+ get { return _scaleLocCodes; }
+ }
+
+ ///
+ /// Gets the root param for the location codes en-/decoding
+ ///
+ /// The scale and root method of integer encoding needs two parameters,
+ /// which I'll call s (scale) and r (root size).
+ /// The integer is encoded as two parts, p (prefix) and q (actual bits).
+ /// p determines how many bits are stored, as well as implicitly determining
+ /// the high-order bit of the integer.
+ public byte RootLocationCodes
+ {
+ get { return _rootLocCodes; }
+ }
+
+ ///
+ /// Gets the size in bytes of each index/leaf node
+ ///
+ public int NodeSize
+ {
+ get { return _nodeSize; }
+ }
+
+ ///
+ /// Gets the length of the longest word in the index
+ ///
+ private int LengthOfLongestWord
+ {
+ get { return _lengthOfLongestWord; }
+ }
+
+ ///
+ /// Gets the total number of words indexed (including duplicates)
+ ///
+ public int TotalWordCount
+ {
+ get { return _totalNumberOfWords; }
+ }
+
+ ///
+ /// Gets the total number of unique words indexed (excluding duplicates)
+ ///
+ public int UniqueWordCount
+ {
+ get { return _numberOfUniqueWords; }
+ }
+
+ ///
+ /// Gets the codepage identifier
+ ///
+ public int CodePage
+ {
+ get { return _codePage; }
+ }
+
+ ///
+ /// Gets the language code id
+ ///
+ public int LCID
+ {
+ get { return _lcid; }
+ }
+
+ public Encoding TextEncoder
+ {
+ get
+ {
+ return _textEncoder;
+ }
+ }
+ }
+
+
+ ///
+ /// Internal class for easier hit recording and rate-calculation
+ ///
+ private sealed class HitHelper : IComparable
+ {
+ ///
+ /// Internal member storing the associated document index
+ ///
+ private int _documentIndex = 0;
+ ///
+ /// Internal member storing the title
+ ///
+ private string _title = "";
+ ///
+ /// Internal member storing the locale
+ ///
+ private string _locale = "";
+ ///
+ /// Internal member storing the location
+ ///
+ private string _location = "";
+ ///
+ /// Internal member storing the url
+ ///
+ private string _url = "";
+ ///
+ /// Internal member storing the rating
+ ///
+ private double _rating = 0;
+ ///
+ /// Internal member used for rating calculation
+ ///
+ private Hashtable _partialRating = new Hashtable();
+
+ ///
+ /// Constructor of the class
+ ///
+ /// document index
+ /// title
+ /// locale parameter
+ /// location
+ /// url of document
+ /// rating
+ public HitHelper(int documentIndex, string title, string locale, string location, string url, double rating)
+ {
+ _documentIndex = documentIndex;
+ _title = title;
+ _locale = locale;
+ _location = location;
+ _url = url;
+ _rating = rating;
+ }
+
+ ///
+ /// Updates the rating for a found word
+ ///
+ /// word found
+ public void UpdateRating(string word)
+ {
+ if( _partialRating[word] == null)
+ {
+ _partialRating[word] = 100.0;
+ }
+ else
+ {
+ _partialRating[word] = ((double)_partialRating[word])*1.01;
+ }
+
+ _rating = 0.0;
+
+ foreach(double val in _partialRating.Values)
+ {
+ _rating += val;
+ }
+ }
+
+ ///
+ /// Implements the CompareTo method of the IComparable interface.
+ /// Allows an easy sort by the document rating
+ ///
+ /// object to compare
+ /// 0 ... equal, -1 ... this instance is less than obj, 1 ... this instance is greater than obj
+ public int CompareTo(object obj)
+ {
+ if( obj is HitHelper )
+ {
+ HitHelper hObj = (HitHelper)obj;
+
+ return this.Rating.CompareTo( hObj.Rating );
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Gets the internal hashtable used for counting word hits of the document
+ ///
+ internal Hashtable PartialRating
+ {
+ get { return _partialRating; }
+ }
+
+ ///
+ /// Gets the document index of the hit helper instance
+ ///
+ public int DocumentIndex
+ {
+ get { return _documentIndex; }
+ }
+
+ ///
+ /// Gets the title
+ ///
+ public string Title
+ {
+ get { return _title; }
+ }
+
+ ///
+ /// Gets the locale
+ ///
+ public string Locale
+ {
+ get { return _locale; }
+ }
+
+ ///
+ /// Gets the location
+ ///
+ public string Location
+ {
+ get { return _location; }
+ }
+
+ ///
+ /// Gets the url
+ ///
+ public string URL
+ {
+ get { return _url; }
+ }
+
+ ///
+ /// Gets the rating
+ ///
+ public double Rating
+ {
+ get { return _rating; }
+ }
+
+ }
+
+ #endregion
+
+ ///
+ /// Regular expression getting the text between to quotes
+ ///
+ private string RE_Quotes = @"\""(?.*?)\""";
+ ///
+ /// 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 datatable storing the search hits
+ ///
+ private DataTable _hits =null;
+ ///
+ /// Internal arraylist for hit management
+ ///
+ private ArrayList _hitsHelper = new ArrayList();
+ ///
+ /// Internal member storing the header of the file
+ ///
+ private FTHeader _header = null;
+ ///
+ /// Internal member storing the associated chmfile object
+ ///
+ private CHMFile _associatedFile = null;
+
+ ///
+ /// Constructor of the class
+ ///
+ /// binary file data of the $FIftiMain file
+ /// associated chm file
+ public FullTextEngine(byte[] binaryFileData, CHMFile associatedFile)
+ {
+ _binaryFileData = binaryFileData;
+ _associatedFile = associatedFile;
+
+ if(_associatedFile.SystemFile.FullTextSearch)
+ {
+ _header = new FTHeader(_binaryFileData); // reading header
+ }
+ }
+
+ ///
+ /// Standard constructor
+ ///
+ internal FullTextEngine()
+ {
+ }
+
+ #region Data dumping
+ ///
+ /// Dump the class data to a binary writer
+ ///
+ /// writer to write the data
+ internal void Dump(ref BinaryWriter writer)
+ {
+ _header.Dump(ref writer);
+ writer.Write( _binaryFileData.Length );
+ writer.Write(_binaryFileData);
+ }
+
+ ///
+ /// Reads the object data from a dump store
+ ///
+ /// reader to read the data
+ internal void ReadDump(ref BinaryReader reader)
+ {
+ _header = new FTHeader();
+ _header.ReadDump(ref reader);
+
+ int nCnt = reader.ReadInt32();
+ _binaryFileData = reader.ReadBytes(nCnt);
+ }
+
+ ///
+ /// Sets the associated CHMFile instance
+ ///
+ /// instance to set
+ internal void SetCHMFile(CHMFile associatedFile)
+ {
+ _associatedFile = associatedFile;
+ }
+ #endregion
+
+ ///
+ /// Gets a flag if full-text searching is available for this chm file.
+ ///
+ public bool CanSearch
+ {
+ get { return (_associatedFile.SystemFile.FullTextSearch && (_header != null) ); }
+ }
+
+ ///
+ /// Performs a fulltext search of a single file.
+ ///
+ /// word(s) or phrase to search
+ /// true if partial word should be matched also
+ /// ( if this is true a search of 'support' will match 'supports', otherwise not )
+ /// true if only search in titles
+ /// Hits are available through the Hists property.
+ public bool Search(string search, bool partialMatches, bool titleOnly)
+ {
+ return Search(search, -1, partialMatches, titleOnly);
+ }
+
+ ///
+ /// Performs a fulltext search of a single file.
+ ///
+ /// word(s) or phrase to search
+ /// max hits. If this number is reached, the search will be interrupted
+ /// true if partial word should be matched also
+ /// ( if this is true a search of 'support' will match 'supports', otherwise not )
+ /// true if only search in titles
+ /// Hits are available through the Hists property.
+ public bool Search(string search, int MaxHits, bool partialMatches, bool titleOnly)
+ {
+ if(CanSearch)
+ {
+ string searchString = search;
+
+ // Check if this is a quoted string
+ bool IsQuoted = (search.IndexOf("\"")>-1);
+
+ if(IsQuoted)
+ searchString = search.Replace("\"",""); // remove the quotes during search
+
+ bool bRet = true;
+
+ _hitsHelper = null;
+ _hitsHelper = new ArrayList();
+
+ _hits = null;
+ CreateHitsTable();
+
+ string[] words = searchString.Split(new char[] {' '});
+
+ for(int i=0; i= MaxHits)
+ break;
+ }
+
+ if(bRet && IsQuoted)
+ {
+ FinalizeQuoted(search);
+ }
+
+ if(bRet)
+ {
+ _hitsHelper.Sort();
+
+ int nhCount = MaxHits;
+
+ if( MaxHits < 0)
+ {
+ nhCount = _hitsHelper.Count;
+ }
+
+ if( nhCount > _hitsHelper.Count )
+ nhCount = _hitsHelper.Count;
+
+ // create hits datatable
+ for(int i=nhCount; i > 0; i--)
+ {
+ HitHelper curHlp = (HitHelper)(_hitsHelper[i-1]);
+
+ DataRow newRow = _hits.NewRow();
+
+ newRow["Rating"] = curHlp.Rating;
+ newRow["Title"] = curHlp.Title;
+ newRow["Locale"] = curHlp.Locale;
+ newRow["Location"] = curHlp.Location;
+ newRow["URL"] = curHlp.URL;
+
+ _hits.Rows.Add( newRow );
+ }
+ }
+ return bRet;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Gets rid of all search hits which doesn't match the quoted phrase
+ ///
+ /// full search string entered by the user
+ /// Phrase search is not possible using the internal full-text index. We're just filtering all
+ /// documents which don't contain all words of the phrase.
+ private void FinalizeQuoted(string search)
+ {
+ Regex quoteRE = new Regex(RE_Quotes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ int innerTextIdx = quoteRE.GroupNumberFromName("innerText");
+ int nIndex = 0;
+
+ // get all phrases
+ while( quoteRE.IsMatch(search, nIndex) )
+ {
+ Match m = quoteRE.Match(search, nIndex);
+
+ string phrase = m.Groups["innerText"].Value;
+
+ string[] wordsInPhrase = phrase.Split( new char[] {' '} );
+ int nCnt = _hitsHelper.Count;
+
+ for(int i=0; i < _hitsHelper.Count; i++)
+ {
+ if( ! CheckHit( ((HitHelper)(_hitsHelper[i])), wordsInPhrase) )
+ _hitsHelper.RemoveAt(i--);
+ }
+
+ nIndex = m.Index+m.Length;
+ }
+ }
+
+ ///
+ /// Eliminates all search hits where not all of the words have been found
+ ///
+ /// hithelper instance to check
+ /// word list
+ private bool CheckHit(HitHelper hit, string[] wordsInPhrase)
+ {
+
+ for(int i=0; i
+ /// Performs a search for a single word in the index
+ ///
+ /// word to search
+ /// maximal hits to return
+ /// true if partial word should be matched also
+ /// ( if this is true a search of 'support' will match 'supports', otherwise not )
+ /// true if only search in titles
+ /// Returns true if succeeded
+ private bool SearchSingleWord(string word,int MaxHits, bool partialMatches, bool titleOnly)
+ {
+ string wordLower = word.ToLower();
+
+ MemoryStream memStream = new MemoryStream(_binaryFileData);
+ BinaryReader binReader = new BinaryReader(memStream);
+
+ // seek to root node
+ binReader.BaseStream.Seek( _header.RootOffset, SeekOrigin.Begin );
+
+ if( _header.Depth > 2 )
+ {
+ // unsupported index depth
+ Debug.WriteLine("FullTextSearcher.SearchSingleWord() - Failed with message: Unsupported index depth !");
+ Debug.WriteLine("File: " + _associatedFile.ChmFilePath);
+ Debug.WriteLine(" ");
+ return false;
+ }
+
+ if( _header.Depth > 1 )
+ {
+ // seek to the right leaf node ( if depth == 1, we are at the leaf node)
+ int freeSpace = binReader.ReadInt16();
+
+ for(int i=0; i < _header.PageCount; ++i)
+ {
+ // exstract index entries
+ int nWLength = (int)binReader.ReadByte();
+ int nCPosition = (int)binReader.ReadByte();
+
+ string sName = BinaryReaderHelp.ExtractString(ref binReader, nWLength-1, 0, true, _header.TextEncoder);
+
+ int nLeafOffset = binReader.ReadInt32();
+ binReader.ReadInt16(); // unknown
+
+ if( sName.CompareTo(wordLower) >= 0)
+ {
+ // store current position
+ long curPos = binReader.BaseStream.Position;
+
+ // seek to leaf offset
+ binReader.BaseStream.Seek( nLeafOffset, SeekOrigin.Begin );
+
+ // read leafnode
+ ReadLeafNode(ref binReader, word, MaxHits, partialMatches, titleOnly);
+
+ // return to current position and continue reading index nodes
+ binReader.BaseStream.Seek( curPos, SeekOrigin.Begin );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Reads a leaf node and extracts documents which holds the searched word
+ ///
+ /// reference to the reader
+ /// word to search
+ /// maximal hits to return
+ /// true if partial word should be matched also
+ /// ( if this is true a search of 'support' will match 'supports', otherwise not )
+ /// true if only search in titles
+ private void ReadLeafNode(ref BinaryReader binReader, string word, int MaxHits, bool partialMatches, bool titleOnly)
+ {
+ int nNextPageOffset = binReader.ReadInt32();
+ binReader.ReadInt16(); // unknown
+ int lfreeSpace = binReader.ReadInt16();
+ string curFullWord = "";
+ bool bFound = false;
+ string wordLower = word.ToLower();
+
+ for(;;)
+ {
+ if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
+ break;
+
+ int nWLength = (int)binReader.ReadByte();
+
+ if(nWLength == 0)
+ break;
+
+ int nCPosition = (int)binReader.ReadByte();
+
+ string sName = BinaryReaderHelp.ExtractString(ref binReader, nWLength-1, 0, true, _header.TextEncoder);
+
+ int Context = (int)binReader.ReadByte(); // 0...body tag, 1...title tag, others unknown
+
+ long nrOfWCL = BinaryReaderHelp.ReadENCINT(ref binReader);
+ int wclOffset = binReader.ReadInt32();
+
+ binReader.ReadInt16(); // unknown
+
+ long bytesOfWCL = BinaryReaderHelp.ReadENCINT(ref binReader);
+
+ if( nCPosition > 0)
+ {
+ curFullWord = CombineStrings(curFullWord, sName, nCPosition);
+ }
+ else
+ {
+ curFullWord = sName;
+ }
+
+ bFound = false;
+ if(partialMatches)
+ bFound = ( curFullWord.IndexOf(wordLower) >= 0 );
+ else
+ bFound = (curFullWord == wordLower);
+
+ if( bFound )
+ {
+ if( (titleOnly && (Context==1)) || (!titleOnly) )
+ {
+ // store actual offset
+ long curPos = binReader.BaseStream.Position;
+
+ // found the word, begin with WCL encoding
+ binReader.BaseStream.Seek(wclOffset, SeekOrigin.Begin );
+
+ byte[] wclBytes = binReader.ReadBytes((int)bytesOfWCL);
+
+ DecodeWCL(wclBytes, MaxHits, word);
+
+ // back and continue reading leafnodes
+ binReader.BaseStream.Seek(curPos, SeekOrigin.Begin );
+ }
+ }
+ }
+ }
+
+ ///
+ /// Decodes the s/r encoded WordCodeList (=wcl) and creates hit entries
+ ///
+ /// wcl encoded byte array
+ /// maximal hits
+ /// the word to find
+ private void DecodeWCL(byte[] wclBytes,int MaxHits, string word)
+ {
+ byte[] wclBits = new byte[ wclBytes.Length*8 ];
+
+ int nBitIdx=0;
+
+ for(int i=0; i (byte)0 ? (byte)1 : (byte)0;
+ nBitIdx++;
+ }
+ }
+
+ nBitIdx = 0;
+
+ int nDocIdx = 0; // delta encoded
+
+ while(nBitIdx < wclBits.Length)
+ {
+ nDocIdx += BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleDocumentIndex, _header.RootDocumentIndex, ref nBitIdx);
+ int nCodeCnt = BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleCodeCount, _header.RootCodeCount, ref nBitIdx);
+
+ int nWordLocation = 0; // delta encoded
+
+ for(int locidx=0; locidx MaxHits)
+ return;
+
+ hitObj = new HitHelper(nDocIdx, ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).Title,
+ ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).Locale, _associatedFile.CompileFile,
+ ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).URL, 0.0);
+
+ for(int k=0;k
+ /// Combines a "master" word with a partial word.
+ ///
+ /// the master word
+ /// the partial word
+ /// position to place the parial word
+ /// returns a combined string
+ private string CombineStrings(string word, string partial, int partialPosition)
+ {
+ string sCombined = word;
+ int i=0;
+
+ for(i=0; i (sCombined.Length-1) )
+ {
+ sCombined += partial[i];
+ }
+ else
+ {
+ StringBuilder sb = new StringBuilder(sCombined);
+
+ sb.Replace( sCombined[partialPosition+i], partial[i], partialPosition+i, 1);
+ sCombined = sb.ToString();
+ }
+ }
+
+ if(! ((i+partialPosition) > (sCombined.Length-1)) )
+ {
+ sCombined = sCombined.Substring(0, partialPosition+partial.Length);
+ }
+
+ return sCombined;
+ }
+
+ ///
+ /// Gets the HitHelper instance for a specific document index
+ ///
+ /// document index
+ /// The reference of the hithelper instance for this document index, otherwise null
+ private HitHelper DocumentHit(int index)
+ {
+ foreach(HitHelper curObj in _hitsHelper)
+ {
+ if( curObj.DocumentIndex == index)
+ return curObj;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Creates a DataTable for storing the hits
+ ///
+ private void CreateHitsTable()
+ {
+ _hits = new DataTable("FT_Search_Hits");
+
+ DataColumn ftColumn;
+
+ ftColumn = new DataColumn();
+ ftColumn.DataType = System.Type.GetType("System.Double");
+ ftColumn.ColumnName = "Rating";
+ ftColumn.ReadOnly = false;
+ ftColumn.Unique = false;
+
+ _hits.Columns.Add(ftColumn);
+
+ ftColumn = new DataColumn();
+ ftColumn.DataType = System.Type.GetType("System.String");
+ ftColumn.ColumnName = "Title";
+ ftColumn.ReadOnly = false;
+ ftColumn.Unique = false;
+
+ _hits.Columns.Add(ftColumn);
+
+ ftColumn = new DataColumn();
+ ftColumn.DataType = System.Type.GetType("System.String");
+ ftColumn.ColumnName = "Locale";
+ ftColumn.ReadOnly = false;
+ ftColumn.Unique = false;
+
+ _hits.Columns.Add(ftColumn);
+
+ ftColumn = new DataColumn();
+ ftColumn.DataType = System.Type.GetType("System.String");
+ ftColumn.ColumnName = "Location";
+ ftColumn.ReadOnly = false;
+ ftColumn.Unique = false;
+
+ _hits.Columns.Add(ftColumn);
+
+ ftColumn = new DataColumn();
+ ftColumn.DataType = System.Type.GetType("System.String");
+ ftColumn.ColumnName = "URL";
+ ftColumn.ReadOnly = false;
+ ftColumn.Unique = false;
+
+ _hits.Columns.Add(ftColumn);
+ }
+
+ ///
+ /// Gets an datatable containing the hits of the last search
+ ///
+ public DataTable Hits
+ {
+ get { return _hits; }
+ }
+
+ ///
+ /// 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;
+ }
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs b/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs
new file mode 100644
index 00000000000..73482712291
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs
@@ -0,0 +1,593 @@
+using System;
+using System.Collections;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class HHCParser implements a parser for HHC contents files.
+ ///
+ internal sealed class HHCParser
+ {
+ ///
+ /// regular expressions for replacing the sitemap boundary tags
+ ///
+ private static string RE_ULOpening = @"\"; // will be replaced by a '(' for nested parsing
+ private static string RE_ULClosing = @"\
"; // will be replaced by a ')' for nested parsing
+
+ ///
+ /// Matching ul-tags
+ ///
+ private static string RE_ULBoundaries = @"\";
+ ///
+ /// Matching the nested tree structure.
+ ///
+ private static string RE_NestedBoundaries = @"\( (?> [^()]+ | \( (?) | \) (?<-DEPTH>) )* (?(DEPTH)(?!)) \)";
+ ///
+ /// Matching object-tags
+ ///
+ private static string RE_ObjectBoundaries = @"\";
+ ///
+ /// Matching param tags
+ ///
+ private static string RE_ParamBoundaries = @"\.*?)\>";
+ ///
+ /// Extracting tag attributes
+ ///
+ private const string RE_QuoteAttributes = @"( |\t)*(?[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?[\""\'])?(?.*?(?(attributeTD)\k|([\s>]|.$)))";
+
+ ///
+ /// private regular expressionobjects
+ ///
+ private static Regex ulRE;
+ private static Regex NestedRE;
+ private static Regex ObjectRE;
+ private static Regex ParamRE;
+ private static Regex AttributesRE;
+
+ ///
+ /// Internal member storing the list of TOCItems which are holding merge links
+ ///
+ private static ArrayList _mergeItems = null;
+
+ ///
+ /// Internal member storing the last read regular topic item.
+ /// This is used to handle "Merge" entries and add them as child to this instance.
+ ///
+ private static TOCItem _lastTopicItem = null;
+
+ ///
+ /// Parses a HHC file and returns an ArrayList with the table of contents (TOC) tree
+ ///
+ /// string content of the hhc file
+ /// CHMFile instance
+ /// Returns an ArrayList with the table of contents (TOC) tree
+ public static ArrayList ParseHHC(string hhcFile, CHMFile chmFile)
+ {
+ _lastTopicItem = null;
+ _mergeItems = null; // clear merged item list
+ ArrayList tocList = new ArrayList();
+
+ ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ NestedRE = new Regex(RE_NestedBoundaries, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+
+ int innerTextIdx = ulRE.GroupNumberFromName("innerText");
+
+ if( ulRE.IsMatch(hhcFile, 0) )
+ {
+ Match m = ulRE.Match(hhcFile, 0);
+
+ int nFirstUL = 0;
+
+ nFirstUL = hhcFile.ToLower().IndexOf("");
+
+ if(nFirstUL == -1)
+ nFirstUL = hhcFile.ToLower().IndexOf("");
+
+ if( ObjectRE.IsMatch(hhcFile, 0) ) // first object block contains information types and categories
+ {
+ Match mO = ObjectRE.Match(hhcFile, 0);
+ int iOTxt = ObjectRE.GroupNumberFromName("innerText");
+
+ string globalText = mO.Groups[iOTxt].Value;
+
+ if( mO.Groups[iOTxt].Index <= nFirstUL)
+ ParseGlobalSettings( globalText, chmFile );
+ }
+
+ // parse toc tree
+ string innerText = m.Groups["innerText"].Value;
+
+ innerText = innerText.Replace("(", "(");
+ innerText = innerText.Replace(")", ")");
+ innerText = Regex.Replace(innerText, RE_ULOpening, "(", RegexOptions.IgnoreCase);
+ innerText = Regex.Replace(innerText, RE_ULClosing, ")", RegexOptions.IgnoreCase);
+
+ ParseTree( innerText, null, tocList, chmFile );
+
+ }
+
+ return tocList;
+ }
+
+ ///
+ /// Checks if the hhc file contains a global object tag.
+ ///
+ /// string content of the hhc file
+ /// chm file
+ /// true if the hhc content contains a global object tag
+ public static bool HasGlobalObjectTag(string hhcFile, CHMFile chmFile)
+ {
+ bool bRet = false;
+
+ ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+
+ int innerTextIdx = ulRE.GroupNumberFromName("innerText");
+
+ if( ulRE.IsMatch(hhcFile, 0) )
+ {
+ Match m = ulRE.Match(hhcFile, 0);
+
+ int nFirstUL = 0;
+
+ nFirstUL = hhcFile.ToLower().IndexOf("");
+
+ if(nFirstUL == -1)
+ nFirstUL = hhcFile.ToLower().IndexOf("");
+
+ if( ObjectRE.IsMatch(hhcFile, 0) ) // first object block contains information types and categories
+ {
+ Match mO = ObjectRE.Match(hhcFile, 0);
+ int iOTxt = ObjectRE.GroupNumberFromName("innerText");
+
+ string globalText = mO.Groups[iOTxt].Value;
+
+ if( mO.Groups[iOTxt].Index <= nFirstUL)
+ bRet = true;
+ }
+ }
+
+ return bRet;
+ }
+
+ ///
+ /// Gets true if the previously done parsing found merge-links
+ ///
+ public static bool HasMergeLinks
+ {
+ get
+ {
+ if(_mergeItems==null)
+ return false;
+
+ return _mergeItems.Count > 0;
+ }
+ }
+
+ ///
+ /// Gets all TOCItem references which are holding merge-links
+ ///
+ public static ArrayList MergeItems
+ {
+ get { return _mergeItems; }
+ }
+
+ ///
+ /// Recursively parses a sitemap tree
+ ///
+ /// content text
+ /// Parent for all read items
+ /// arraylist which receives the extracted nodes
+ /// CHMFile instance
+ private static void ParseTree( string text, TOCItem parent, ArrayList arrNodes, CHMFile chmFile )
+ {
+ string strPreItems="", strPostItems="";
+ string innerText = "";
+
+ int nIndex = 0;
+
+ while( NestedRE.IsMatch(text, nIndex) )
+ {
+ Match m = NestedRE.Match(text, nIndex);
+
+ innerText = m.Value.Substring( 1, m.Length-2);
+
+ strPreItems = text.Substring(nIndex,m.Index-nIndex);
+
+ ParseItems(strPreItems, parent, arrNodes, chmFile);
+
+ if((arrNodes.Count>0) && (innerText.Length > 0) )
+ {
+ TOCItem p = ((TOCItem)(arrNodes[arrNodes.Count-1]));
+ ParseTree( innerText, p, p.Children, chmFile );
+ }
+
+ nIndex = m.Index+m.Length;
+ }
+
+ if( nIndex == 0)
+ {
+ strPostItems = text.Substring(nIndex, text.Length-nIndex);
+ ParseItems(strPostItems, parent, arrNodes, chmFile);
+ }
+ else if( nIndex < text.Length-1)
+ {
+ strPostItems = text.Substring(nIndex, text.Length-nIndex);
+ ParseTree(strPostItems, parent, arrNodes, chmFile);
+ }
+ }
+
+ ///
+ /// Parses tree nodes from the text
+ ///
+ /// text containing the items
+ /// Parent for all read items
+ /// arraylist where the nodes should be added
+ /// CHMFile instance
+ private static void ParseItems( string itemstext, TOCItem parent, ArrayList arrNodes, CHMFile chmFile)
+ {
+ int innerTextIdx = ObjectRE.GroupNumberFromName("innerText");
+ int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
+
+ // get group-name indexes
+ int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
+ int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
+ int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
+
+ int nObjStartIndex = 0;
+
+ while( ObjectRE.IsMatch(itemstext, nObjStartIndex) )
+ {
+ Match m = ObjectRE.Match(itemstext, nObjStartIndex);
+
+ string innerText = m.Groups[innerTextIdx].Value;
+
+ TOCItem tocItem = new TOCItem();
+ tocItem.TocMode = DataMode.TextBased;
+ tocItem.AssociatedFile = chmFile;
+ tocItem.Parent = parent;
+
+ // read parameters
+ int nParamIndex = 0;
+
+ while( ParamRE.IsMatch(innerText, nParamIndex) )
+ {
+ Match mP = ParamRE.Match(innerText, nParamIndex);
+
+ string innerP = mP.Groups[innerPTextIdx].Value;
+
+ string paramName = "";
+ string paramValue = "";
+
+ int nAttrIdx = 0;
+
+ while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
+ {
+ Match mA = AttributesRE.Match(innerP, nAttrIdx);
+
+ string attributeName = mA.Groups[nameIndex].Value;
+ string attributeValue = mA.Groups[valueIndex].Value;
+ string attributeTD = mA.Groups[tdIndex].Value;
+
+ if(attributeTD.Length > 0)
+ {
+ // delete the trailing textqualifier
+ if( attributeValue.Length > 0)
+ {
+ int ltqi = attributeValue.LastIndexOf( attributeTD );
+
+ if(ltqi >= 0)
+ {
+ attributeValue = attributeValue.Substring(0,ltqi);
+ }
+ }
+ }
+
+ if( attributeName.ToLower() == "name")
+ {
+ paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
+ }
+
+ if( attributeName.ToLower() == "value")
+ {
+ paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
+ // delete trailing /
+ while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))
+ paramValue = paramValue.Substring(0,paramValue.Length-1);
+
+ }
+
+ nAttrIdx = mA.Index+mA.Length;
+ }
+
+ tocItem.Params[paramName] = paramValue;
+ switch(paramName.ToLower())
+ {
+ case "name":
+ {
+ tocItem.Name = paramValue;
+ };break;
+ case "local":
+ {
+ tocItem.Local = paramValue.Replace("../", "").Replace("./", "");
+ };break;
+ case "imagenumber":
+ {
+ tocItem.ImageIndex = Int32.Parse(paramValue);
+ tocItem.ImageIndex-=1;
+
+ int nFolderAdd = 0;
+
+ if((chmFile != null) && (chmFile.ImageTypeFolder))
+ {
+ // get the value which should be added, to display folders instead of books
+ if(HtmlHelpSystem.UseHH2TreePics)
+ nFolderAdd = 8;
+ else
+ nFolderAdd = 4;
+ }
+
+ if(tocItem.ImageIndex%2 != 0)
+ {
+ if(tocItem.ImageIndex==1)
+ tocItem.ImageIndex=0;
+ }
+ if(HtmlHelpSystem.UseHH2TreePics)
+ if( tocItem.ImageIndex == 0)
+ tocItem.ImageIndex = TOCItem.STD_FOLDER_HH2+nFolderAdd;
+ };break;
+ case "merge": // this item contains topics or a full TOC from a merged CHM
+ {
+ tocItem.MergeLink = paramValue;
+
+ // "register" this item as merge-link
+ if(_mergeItems==null)
+ _mergeItems=new ArrayList();
+
+ _mergeItems.Add(tocItem);
+
+ };break;
+ case "type": // information type assignment for item
+ {
+ tocItem.InfoTypeStrings.Add( paramValue );
+ };break;
+ }
+
+ nParamIndex = mP.Index+mP.Length;
+ }
+
+ tocItem.ChmFile = chmFile.ChmFilePath;
+
+ if(tocItem.MergeLink.Length > 0)
+ {
+ if(_lastTopicItem != null)
+ {
+ tocItem.Parent = _lastTopicItem;
+ _lastTopicItem.Children.Add(tocItem);
+ }
+ else
+ arrNodes.Add( tocItem );
+ }
+ else
+ {
+ _lastTopicItem = tocItem;
+ arrNodes.Add( tocItem );
+ }
+
+ nObjStartIndex = m.Index+m.Length;
+ }
+ }
+
+ ///
+ /// Parses the very first <OBJECT> tag in the sitemap file and extracts
+ /// information types and categories.
+ ///
+ /// text of the object tag
+ /// CHMFile instance
+ private static void ParseGlobalSettings(string sText, CHMFile chmFile)
+ {
+ int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
+
+ // get group-name indexes
+ int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
+ int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
+ int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
+
+ // read parameters
+ int nParamIndex = 0;
+
+ // 0... unknown
+ // 1... inclusinve info type name
+ // 2... exclusive info type name
+ // 3... hidden info type name
+ // 4... category name
+ // 5... incl infotype name for category
+ // 6... excl infotype name for category
+ // 7... hidden infotype name for category
+ int prevItem = 0;
+
+ string sName = "";
+ string sDescription = "";
+ string curCategory = "";
+
+ while( ParamRE.IsMatch(sText, nParamIndex) )
+ {
+ Match mP = ParamRE.Match(sText, nParamIndex);
+
+ string innerP = mP.Groups[innerPTextIdx].Value;
+
+ string paramName = "";
+ string paramValue = "";
+
+ int nAttrIdx = 0;
+
+ while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
+ {
+ Match mA = AttributesRE.Match(innerP, nAttrIdx);
+
+ string attributeName = mA.Groups[nameIndex].Value;
+ string attributeValue = mA.Groups[valueIndex].Value;
+ string attributeTD = mA.Groups[tdIndex].Value;
+
+ if(attributeTD.Length > 0)
+ {
+ // delete the trailing textqualifier
+ if( attributeValue.Length > 0)
+ {
+ int ltqi = attributeValue.LastIndexOf( attributeTD );
+
+ if(ltqi >= 0)
+ {
+ attributeValue = attributeValue.Substring(0,ltqi);
+ }
+ }
+ }
+
+ if( attributeName.ToLower() == "name")
+ {
+ paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
+ }
+
+ if( attributeName.ToLower() == "value")
+ {
+ paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
+ // delete trailing /
+ while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))
+ paramValue = paramValue.Substring(0,paramValue.Length-1);
+
+ }
+
+ nAttrIdx = mA.Index+mA.Length;
+ }
+
+ switch(paramName.ToLower())
+ {
+ case "savetype": // inclusive information type name
+ {
+ prevItem = 1;
+ sName = paramValue;
+ };break;
+ case "savetypedesc": // description of information type
+ {
+ InformationTypeMode mode = InformationTypeMode.Inclusive;
+ sDescription = paramValue;
+
+ if( prevItem == 1)
+ mode = InformationTypeMode.Inclusive;
+ if( prevItem == 2)
+ mode = InformationTypeMode.Exclusive;
+ if( prevItem == 3)
+ mode = InformationTypeMode.Hidden;
+
+ if( chmFile.GetInformationType( sName ) == null)
+ {
+ // check if the HtmlHelpSystem already holds such an information type
+ if( chmFile.SystemInstance.GetInformationType( sName ) == null)
+ {
+ // info type not found yet
+
+ InformationType newType = new InformationType(sName, sDescription, mode);
+ chmFile.InformationTypes.Add(newType);
+ }
+ else
+ {
+ InformationType sysType = chmFile.SystemInstance.GetInformationType( sName );
+ chmFile.InformationTypes.Add( sysType );
+ }
+ }
+
+ prevItem = 0;
+ };break;
+ case "saveexclusive": // exclusive information type name
+ {
+ prevItem = 2;
+ sName = paramValue;
+ };break;
+ case "savehidden": // hidden information type name
+ {
+ prevItem = 3;
+ sName = paramValue;
+ };break;
+ case "category": // category name
+ {
+ prevItem = 4;
+ sName = paramValue;
+ curCategory = sName;
+ };break;
+ case "categorydesc": // category description
+ {
+ sDescription = paramValue;
+
+ if( chmFile.GetCategory( sName ) == null)
+ {
+ // check if the HtmlHelpSystem already holds such a category
+ if( chmFile.SystemInstance.GetCategory( sName ) == null)
+ {
+ // add category
+ Category newCat = new Category(sName, sDescription);
+ chmFile.Categories.Add(newCat);
+ }
+ else
+ {
+ Category sysCat = chmFile.SystemInstance.GetCategory( sName );
+ chmFile.Categories.Add( sysCat );
+ }
+ }
+
+ prevItem = 0;
+ };break;
+ case "type": // inclusive information type which is member of the previously read category
+ {
+ prevItem = 5;
+ sName = paramValue;
+ };break;
+ case "typedesc": // description of type for category
+ {
+ sDescription = paramValue;
+ Category cat = chmFile.GetCategory( curCategory );
+
+ if( cat != null)
+ {
+ // category found
+ InformationType infoType = chmFile.GetInformationType( sName );
+
+ if( infoType != null)
+ {
+ if( !cat.ContainsInformationType(infoType))
+ {
+ infoType.SetCategoryFlag(true);
+ cat.AddInformationType(infoType);
+ }
+ }
+ }
+
+ prevItem = 0;
+ };break;
+ case "typeexclusive": // exclusive information type which is member of the previously read category
+ {
+ prevItem = 6;
+ sName = paramValue;
+ };break;
+ case "typehidden": // hidden information type which is member of the previously read category
+ {
+ prevItem = 7;
+ sName = paramValue;
+ };break;
+ default:
+ {
+ prevItem = 0;
+ sName = "";
+ sDescription = "";
+ };break;
+ }
+
+ nParamIndex = mP.Index+mP.Length;
+ }
+ }
+ }
+}
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs b/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs
new file mode 100644
index 00000000000..bdb988a8665
--- /dev/null
+++ b/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs
@@ -0,0 +1,220 @@
+using System;
+using System.Collections;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace HtmlHelp.ChmDecoding
+{
+ ///
+ /// The class HHCParser implements a parser for HHC contents files.
+ ///
+ // internal sealed class HHCParser : IHHCParser
+ public class HHCParser2
+ {
+ static private string m_text1="";
+ static private string m_text2="";
+ static private int m_CurrentPos=0;
+
+ ///
+ /// Parses a HHC file and returns an ArrayList with the table of contents (TOC) tree
+ ///
+ /// string content of the hhc file
+ /// CHMFile instance
+ /// Returns an ArrayList with the table of contents (TOC) tree
+ public static ArrayList ParseHHC(string hhcFile, CHMFile chmFile)
+ {
+ DateTime StartTime=DateTime.Now;
+
+ ArrayList tocList = new ArrayList();
+
+ m_text2=hhcFile;
+ m_text1=hhcFile.ToLower();
+
+ int idx=m_text1.IndexOf("");
+ if (idx==-1)
+ return null;
+ m_CurrentPos=idx+4;
+
+ ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+
+ ParseTree(tocList,chmFile);
+
+ DateTime EndTime=DateTime.Now;
+ TimeSpan Diff=EndTime-StartTime;
+ string x=Diff.ToString();
+
+ return tocList;
+ }
+
+ ///
+ /// Recursively parses a sitemap tree
+ ///
+ /// content text
+ /// arraylist which receives the extracted nodes
+ /// CHMFile instance
+ static private void ParseTree( ArrayList arrNodes, CHMFile chmFile )
+ {
+ bool bProcessing=true;
+ do
+ {
+ bProcessing=false;
+
+ // Indent
+ int idxa=m_text1.IndexOf("",m_CurrentPos);
+ int idxb=m_text1.IndexOf("- ",m_CurrentPos);
+ int idxc=m_text1.IndexOf("
",m_CurrentPos);
+
+ if ((idxa-1))
+ {
+ bProcessing=true;
+ m_CurrentPos=idxa+4;
+ if (arrNodes.Count<1)
+ {
+ ParseTree(arrNodes,chmFile);
+ }
+ else
+ {
+ ParseTree(((TOCItem)(arrNodes[arrNodes.Count-1])).Children,chmFile);
+ }
+ continue;
+ }
+
+ // new item
+ if ((idxb-1))
+ {
+
+ bProcessing=true;
+ m_CurrentPos=idxb+4;
+
+ int idx2=m_text1.IndexOf("