Import TechBot
authorCasper Hornstrup <chorns@users.sourceforge.net>
Sat, 15 Jan 2005 19:27:25 +0000 (19:27 +0000)
committerCasper Hornstrup <chorns@users.sourceforge.net>
Sat, 15 Jan 2005 19:27:25 +0000 (19:27 +0000)
svn path=/trunk/; revision=13064

94 files changed:
irc/TechBot/CHMLibrary/AssemblyInfo.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMBtree.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/HHKParser.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/TopicEntry.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/UrlTableEntry.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMDecoding/enumerations.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMLibrary.cmbx [new file with mode: 0644]
irc/TechBot/CHMLibrary/CHMLibrary.prjx [new file with mode: 0644]
irc/TechBot/CHMLibrary/Category.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/ChmFileInfo.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/Default.build [new file with mode: 0644]
irc/TechBot/CHMLibrary/HtmlHelpSystem.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/HttpUtility.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/Index.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/IndexItem.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/IndexTopic.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/InformationType.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/Storage/CHMStream.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/TOCItem.cs [new file with mode: 0644]
irc/TechBot/CHMLibrary/TableOfContents.cs [new file with mode: 0644]
irc/TechBot/Compression/AssemblyInfo.cs [new file with mode: 0644]
irc/TechBot/Compression/Checksums/Adler32.cs [new file with mode: 0644]
irc/TechBot/Compression/Checksums/CRC32.cs [new file with mode: 0644]
irc/TechBot/Compression/Checksums/IChecksum.cs [new file with mode: 0644]
irc/TechBot/Compression/Checksums/StrangeCRC.cs [new file with mode: 0644]
irc/TechBot/Compression/Compression.cmbx [new file with mode: 0644]
irc/TechBot/Compression/Compression.prjx [new file with mode: 0644]
irc/TechBot/Compression/Default.build [new file with mode: 0644]
irc/TechBot/Compression/Deflater.cs [new file with mode: 0644]
irc/TechBot/Compression/DeflaterConstants.cs [new file with mode: 0644]
irc/TechBot/Compression/DeflaterEngine.cs [new file with mode: 0644]
irc/TechBot/Compression/DeflaterHuffman.cs [new file with mode: 0644]
irc/TechBot/Compression/DeflaterPending.cs [new file with mode: 0644]
irc/TechBot/Compression/Inflater.cs [new file with mode: 0644]
irc/TechBot/Compression/InflaterDynHeader.cs [new file with mode: 0644]
irc/TechBot/Compression/InflaterHuffmanTree.cs [new file with mode: 0644]
irc/TechBot/Compression/PendingBuffer.cs [new file with mode: 0644]
irc/TechBot/Compression/Streams/DeflaterOutputStream.cs [new file with mode: 0644]
irc/TechBot/Compression/Streams/InflaterInputStream.cs [new file with mode: 0644]
irc/TechBot/Compression/Streams/OutputWindow.cs [new file with mode: 0644]
irc/TechBot/Compression/Streams/StreamManipulator.cs [new file with mode: 0644]
irc/TechBot/Compression/ZipException.cs [new file with mode: 0644]
irc/TechBot/Default.build [new file with mode: 0644]
irc/TechBot/TechBot.Console/App.config [new file with mode: 0644]
irc/TechBot/TechBot.Console/AssemblyInfo.cs [new file with mode: 0644]
irc/TechBot/TechBot.Console/Default.build [new file with mode: 0644]
irc/TechBot/TechBot.Console/Main.cs [new file with mode: 0644]
irc/TechBot/TechBot.Console/TechBot.Console.cmbx [new file with mode: 0644]
irc/TechBot/TechBot.Console/TechBot.Console.prjx [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/AssemblyInfo.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/Default.build [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/IRC.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/IrcChannel.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/IrcClient.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/IrcException.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/IrcUser.cs [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.cmbx [new file with mode: 0644]
irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.prjx [new file with mode: 0644]
irc/TechBot/TechBot.Library/ApiCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/AssemblyInfo.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/Default.build [new file with mode: 0644]
irc/TechBot/TechBot.Library/HelpCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/HresultCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/ICommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/IrcService.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/NtStatusCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/NumberParser.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/ServiceOutput.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/SvnCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/TechBot.Library.cmbx [new file with mode: 0644]
irc/TechBot/TechBot.Library/TechBot.Library.prjx [new file with mode: 0644]
irc/TechBot/TechBot.Library/TechBotService.cs [new file with mode: 0644]
irc/TechBot/TechBot.Library/WinerrorCommand.cs [new file with mode: 0644]
irc/TechBot/TechBot.cmbx [new file with mode: 0644]
irc/TechBot/TechBot/App.config [new file with mode: 0644]
irc/TechBot/TechBot/AssemblyInfo.cs [new file with mode: 0644]
irc/TechBot/TechBot/Default.build [new file with mode: 0644]
irc/TechBot/TechBot/ServiceThread.cs [new file with mode: 0644]
irc/TechBot/TechBot/TechBot.prjx [new file with mode: 0644]
irc/TechBot/TechBot/TechBotService.cs [new file with mode: 0644]

diff --git a/irc/TechBot/CHMLibrary/AssemblyInfo.cs b/irc/TechBot/CHMLibrary/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..dbacda1
--- /dev/null
@@ -0,0 +1,32 @@
+using System.Reflection;\r
+using System.Runtime.CompilerServices;\r
+\r
+// Information about this assembly is defined by the following\r
+// attributes.\r
+//\r
+// change them to the information which is associated with the assembly\r
+// you compile.\r
+\r
+[assembly: AssemblyTitle("")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("")]\r
+[assembly: AssemblyProduct("")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+\r
+// The assembly version has following format :\r
+//\r
+// Major.Minor.Build.Revision\r
+//\r
+// You can specify all values by your own or you can build default build and revision\r
+// numbers with the '*' character (the default):\r
+\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+// The following attributes specify the key for the sign of your assembly. See the\r
+// .NET Framework documentation for more information about signing.\r
+// This is not required, if you don't want signing let these attributes like they're.\r
+[assembly: AssemblyDelaySign(false)]\r
+[assembly: AssemblyKeyFile("")]\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs b/irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs
new file mode 100644 (file)
index 0000000..64c9668
--- /dev/null
@@ -0,0 +1,274 @@
+using System;\r
+using System.Collections;\r
+using System.IO;\r
+using System.Text;\r
+using System.Globalization;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>BinaryReaderHelp</c> implements static helper methods for extracting binary data \r
+       /// from a binary reader object.\r
+       /// </summary>\r
+       internal class BinaryReaderHelp\r
+       {\r
+               /// <summary>\r
+               /// Internal helper method to extract null-terminated strings from a binary reader\r
+               /// </summary>\r
+               /// <param name="binReader">reference to the binary reader</param>\r
+               /// <param name="offset">offset in the stream</param>\r
+               /// <param name="noOffset">true if the offset value should be used</param>\r
+               /// <param name="encoder">encoder used for text encoding</param>\r
+               /// <returns>An extracted string value</returns>\r
+               internal static string ExtractString(ref BinaryReader binReader, int offset, bool noOffset, Encoding encoder)\r
+               {\r
+                       string strReturn = "";\r
+\r
+                       if(encoder == null)\r
+                               encoder = Encoding.ASCII;\r
+\r
+                       ArrayList nameBytes = new ArrayList();\r
+                       byte curByte;\r
+                       \r
+                       if(!noOffset)\r
+                               binReader.BaseStream.Seek(offset, SeekOrigin.Begin);\r
+\r
+                       if(binReader.BaseStream.Position >= binReader.BaseStream.Length)\r
+                               return "";\r
+\r
+                       curByte = binReader.ReadByte();\r
+                       while( (curByte != (byte)0) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )\r
+                       {       \r
+                               nameBytes.Add( curByte );\r
+                               curByte = binReader.ReadByte();\r
+                       }\r
+\r
+                       byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));\r
+                       strReturn = encoder.GetString(name,0,name.Length);\r
+\r
+                       return strReturn;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal helper method to extract a string with a specific length from the binary reader\r
+               /// </summary>\r
+               /// <param name="binReader">reference to the binary reader</param>\r
+               /// <param name="length">length of the string (number of bytes)</param>\r
+               /// <param name="offset">offset in the stream</param>\r
+               /// <param name="noOffset">true if the offset value should be used</param>\r
+               /// <param name="encoder">encoder used for text encoding</param>\r
+               /// <returns>An extracted string value</returns>\r
+               internal static string ExtractString(ref BinaryReader binReader, int length, int offset, bool noOffset, Encoding encoder)\r
+               {\r
+                       string strReturn = "";\r
+\r
+                       if(length == 0)\r
+                               return "";\r
+\r
+                       if(encoder == null)\r
+                               encoder = Encoding.ASCII;\r
+\r
+                       ArrayList nameBytes = new ArrayList();\r
+                       byte curByte;\r
+                       \r
+                       if(!noOffset)\r
+                               binReader.BaseStream.Seek(offset, SeekOrigin.Begin);\r
+\r
+                       if(binReader.BaseStream.Position >= binReader.BaseStream.Length)\r
+                               return "";\r
+\r
+                       curByte = binReader.ReadByte();\r
+                       while( (curByte != (byte)0) && (nameBytes.Count < length) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )\r
+                       {       \r
+                               nameBytes.Add( curByte );\r
+\r
+                               if(nameBytes.Count < length)\r
+                                       curByte = binReader.ReadByte();\r
+                       }\r
+\r
+                       byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));\r
+                       strReturn = encoder.GetString(name,0,name.Length);\r
+\r
+                       return strReturn;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal helper method to extract a string with a specific length from the binary reader\r
+               /// </summary>\r
+               /// <param name="binReader">reference to the binary reader</param>\r
+               /// <param name="bFoundTerminator">reference to a bool vairable which will receive true if the\r
+               /// string terminator \0 was found. false indicates that the end of the stream was reached.</param>\r
+               /// <param name="offset">offset in the stream</param>\r
+               /// <param name="noOffset">true if the offset value should be used</param>\r
+               /// <param name="encoder">encoder used for text encoding</param>\r
+               /// <returns>An extracted string value</returns>\r
+               internal static string ExtractString(ref BinaryReader binReader, ref bool bFoundTerminator, int offset, bool noOffset, Encoding encoder)\r
+               {\r
+                       string strReturn = "";\r
+\r
+                       ArrayList nameBytes = new ArrayList();\r
+                       byte curByte;\r
+                       \r
+                       if(encoder == null)\r
+                               encoder = Encoding.ASCII;\r
+\r
+                       if(!noOffset)\r
+                               binReader.BaseStream.Seek(offset, SeekOrigin.Begin);\r
+\r
+                       if(binReader.BaseStream.Position >= binReader.BaseStream.Length)\r
+                               return "";\r
+\r
+                       curByte = binReader.ReadByte();\r
+                       while( (curByte != (byte)0) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )\r
+                       {       \r
+                               nameBytes.Add( curByte );\r
+                               curByte = binReader.ReadByte();\r
+\r
+                               if( curByte == (byte)0 )\r
+                               {\r
+                                       bFoundTerminator = true;\r
+                               }\r
+                       }\r
+\r
+                       byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));\r
+                       strReturn = encoder.GetString(name,0,name.Length);\r
+\r
+                       return strReturn;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal helper method to extract a null-terminated UTF-16/UCS-2 strings from a binary reader\r
+               /// </summary>\r
+               /// <param name="binReader">reference to the binary reader</param>\r
+               /// <param name="offset">offset in the stream</param>\r
+               /// <param name="noOffset">true if the offset value should be used</param>\r
+               /// <param name="encoder">encoder used for text encoding</param>\r
+               /// <returns>An extracted string value</returns>\r
+               internal static string ExtractUTF16String(ref BinaryReader binReader, int offset, bool noOffset, Encoding encoder)\r
+               {\r
+                       string strReturn = "";\r
+\r
+                       ArrayList nameBytes = new ArrayList();\r
+                       byte curByte;\r
+                       int lastByte=-1;\r
+                       \r
+                       if(!noOffset)\r
+                               binReader.BaseStream.Seek(offset, SeekOrigin.Begin);\r
+\r
+                       if(binReader.BaseStream.Position >= binReader.BaseStream.Length)\r
+                               return "";\r
+\r
+                       if(encoder == null)\r
+                               encoder = Encoding.Unicode;\r
+\r
+                       curByte = binReader.ReadByte();\r
+                       int nCnt = 0;\r
+                       while( ((curByte != (byte)0) || (lastByte != 0) ) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )\r
+                       {       \r
+                               nameBytes.Add( curByte );\r
+\r
+                               if(nCnt%2 == 0)\r
+                                       lastByte = (int)curByte;\r
+\r
+                               curByte = binReader.ReadByte();\r
+\r
+                               nCnt++;\r
+                       }\r
+\r
+                       byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));\r
+                       strReturn = Encoding.Unicode.GetString(name,0,name.Length);\r
+\r
+                       // apply text encoding\r
+                       name = Encoding.Default.GetBytes(strReturn);\r
+                       strReturn = encoder.GetString(name,0,name.Length);\r
+\r
+                       return strReturn;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal helper for reading ENCINT encoded integer values\r
+               /// </summary>\r
+               /// <param name="binReader">reference to the reader</param>\r
+               /// <returns>a long value</returns>\r
+               internal static long ReadENCINT(ref BinaryReader binReader)\r
+               {\r
+                       long nRet = 0;\r
+                       byte buffer = 0;\r
+                       int shift = 0;\r
+\r
+                       if(binReader.BaseStream.Position >= binReader.BaseStream.Length)\r
+                               return nRet;\r
+                       \r
+                       do\r
+                       {\r
+                               buffer = binReader.ReadByte();\r
+                               nRet |= ((long)((buffer & (byte)0x7F))) << shift;\r
+                               shift += 7;\r
+\r
+                       }while ( (buffer & (byte)0x80) != 0);\r
+\r
+                       return nRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads an s/r encoded value from the byte array and decodes it into an integer\r
+               /// </summary>\r
+               /// <param name="wclBits">a byte array containing all bits (contains only 0 or 1 elements)</param>\r
+               /// <param name="s">scale param for encoding</param>\r
+               /// <param name="r">root param for encoding</param>\r
+               /// <param name="nBitIndex">current index in the wclBits array</param>\r
+               /// <returns>Returns an decoded integer value.</returns>\r
+               internal static int ReadSRItem(byte[] wclBits, int s, int r, ref int nBitIndex)\r
+               {\r
+                       int nRet = 0;\r
+                       int q = r;\r
+\r
+                       int nPref1Cnt = 0;\r
+\r
+                       while( wclBits[nBitIndex++] == 1)\r
+                       {\r
+                               nPref1Cnt++;\r
+                       }\r
+\r
+                       if(nPref1Cnt == 0)\r
+                       {\r
+                               int nMask = 0;\r
+\r
+                               for(int nbits=0; nbits<q;nbits++)\r
+                               {\r
+                                       nMask |= ( 0x01 & (int)wclBits[nBitIndex]) << (q-nbits-1);\r
+                                       nBitIndex++;\r
+                               }\r
+\r
+                               nRet = nMask;\r
+                       } \r
+                       else \r
+                       {\r
+                               q += (nPref1Cnt-1);\r
+\r
+                               int nMask = 0;\r
+                               int nRMaxValue = 0;\r
+\r
+                               for(int nbits=0; nbits<q;nbits++)\r
+                               {\r
+                                       nMask |= ( 0x01 & (int)wclBits[nBitIndex]) << (q-nbits-1);\r
+                                       nBitIndex++;\r
+                               }\r
+\r
+                               for(int nsv=0; nsv<r; nsv++)\r
+                               {\r
+                                       nRMaxValue = nRMaxValue << 1;\r
+                                       nRMaxValue |= 0x1;\r
+                               }\r
+       \r
+                               nRMaxValue++; // startvalue of s/r encoding with 1 prefixing '1'\r
+\r
+                               nRMaxValue *= (int) Math.Pow((double)2, (double)(nPref1Cnt-1));\r
+\r
+                               nRet = nRMaxValue + nMask;\r
+                       }\r
+\r
+                       return nRet;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMBtree.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMBtree.cs
new file mode 100644 (file)
index 0000000..f1d1a66
--- /dev/null
@@ -0,0 +1,325 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Collections.Specialized;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMBtree</c> implements methods/properties to decode the binary help index. \r
+       /// This class automatically creates an index arraylist for the current CHMFile instance. \r
+       /// It does not store the index internally !\r
+       /// </summary>\r
+       /// <remarks>The binary index can be found in the storage file $WWKeywordLinks/BTree</remarks>\r
+       internal sealed class CHMBtree : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Constant specifying the size of the string blocks\r
+               /// </summary>\r
+               private const int BLOCK_SIZE = 2048;\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing flags\r
+               /// </summary>\r
+               private int _flags = 0;\r
+               /// <summary>\r
+               /// Internal member storing the data format\r
+               /// </summary>\r
+               private byte[] _dataFormat = new byte[16];\r
+               /// <summary>\r
+               /// Internal member storing the index of the last listing block\r
+               /// </summary>\r
+               private int _indexOfLastListingBlock = 0;\r
+               /// <summary>\r
+               /// Internal member storing the index of the root block\r
+               /// </summary>\r
+               private int _indexOfRootBlock = 0;\r
+               /// <summary>\r
+               /// Internal member storing the number of blocks\r
+               /// </summary>\r
+               private int _numberOfBlocks = 0;\r
+               /// <summary>\r
+               /// Internal member storing the tree depth. \r
+               /// (1 if no index blocks, 2 one level of index blocks, ...)\r
+               /// </summary>\r
+               private int _treeDepth = 0;\r
+               /// <summary>\r
+               /// Internal member storing the number of keywords in the file\r
+               /// </summary>\r
+               private int _numberOfKeywords = 0;\r
+               /// <summary>\r
+               /// Internal member storing the codepage\r
+               /// </summary>\r
+               private int _codePage = 0;\r
+               /// <summary>\r
+               /// true if the index is from a CHI or CHM file, else CHW\r
+               /// </summary>\r
+               private bool _isCHI_CHM = true;\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+               /// <summary>\r
+               /// Internal flag specifying if we have to read listing or index blocks\r
+               /// </summary>\r
+               private bool _readListingBlocks = true;\r
+               /// <summary>\r
+               /// Internal member storing an indexlist of the current file.\r
+               /// </summary>\r
+               private ArrayList _indexList = new ArrayList();\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the $WWKeywordLinks/BTree file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMBtree(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       if( associatedFile == null)\r
+                       {\r
+                               throw new ArgumentException("CHMBtree.ctor() - Associated CHMFile must not be null !", "associatedFile");\r
+                       }\r
+\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+\r
+                       // clear internal binary data after extraction\r
+                       _binaryFileData = null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+                       \r
+                       int nCurOffset = 0;\r
+                       int nTemp = 0;\r
+\r
+                       // decode header\r
+                       binReader.ReadChars(2); // 2chars signature (not important)\r
+                       \r
+                       _flags = (int)binReader.ReadInt16(); // WORD flags\r
+\r
+                       binReader.ReadInt16(); // size of blocks (always 2048)\r
+\r
+                       _dataFormat = binReader.ReadBytes(16);\r
+                       \r
+                       binReader.ReadInt32();  // unknown DWORD\r
+\r
+                       _indexOfLastListingBlock = binReader.ReadInt32();\r
+                       _indexOfRootBlock = binReader.ReadInt32();\r
+\r
+                       binReader.ReadInt32(); // unknown DWORD\r
+\r
+                       _numberOfBlocks = binReader.ReadInt32();\r
+                       _treeDepth = binReader.ReadInt16();\r
+                       _numberOfKeywords = binReader.ReadInt32();\r
+                       _codePage = binReader.ReadInt32();\r
+\r
+                       binReader.ReadInt32(); // lcid DWORD\r
+                       \r
+                       nTemp = binReader.ReadInt32();\r
+                       _isCHI_CHM = (nTemp==1);\r
+\r
+                       binReader.ReadInt32(); // unknown DWORD\r
+                       binReader.ReadInt32(); // unknown DWORD\r
+                       binReader.ReadInt32(); // unknown DWORD\r
+                       binReader.ReadInt32(); // unknown DWORD\r
+\r
+                       // end of header decode\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               nCurOffset = (int)memStream.Position;\r
+                               byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);\r
+                               bRet &= DecodeBlock(dataBlock, ref nCurOffset, _treeDepth-1);\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes a block of url-string data\r
+               /// </summary>\r
+               /// <param name="dataBlock">block of data</param>\r
+               /// <param name="nOffset">current file offset</param>\r
+               /// <param name="indexBlocks">number of index blocks</param>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeBlock( byte[] dataBlock, ref int nOffset, int indexBlocks )\r
+               {\r
+                       bool bRet = true;\r
+                       int nblockOffset = nOffset;\r
+\r
+                       MemoryStream memStream = new MemoryStream(dataBlock);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       int freeSpace = binReader.ReadInt16(); // length of freespace\r
+                       int nrOfEntries = binReader.ReadInt16(); // number of entries\r
+\r
+                       bool bListingEndReached = false;\r
+\r
+                       //while( (memStream.Position < (memStream.Length-freeSpace)) && (bRet) )\r
+                       //{\r
+                               int nIndexOfPrevBlock = -1;\r
+                               int nIndexOfNextBlock = -1;\r
+                               int nIndexOfChildBlock = 0;\r
+                               \r
+                               if(_readListingBlocks)\r
+                               {\r
+                                       nIndexOfPrevBlock = binReader.ReadInt32(); // -1 if this is the header\r
+                                       nIndexOfNextBlock = binReader.ReadInt32(); // -1 if this is the last block\r
+                               } \r
+                               else \r
+                               {\r
+                                       nIndexOfChildBlock = binReader.ReadInt32(); \r
+                               }\r
+\r
+                               for(int nE = 0; nE < nrOfEntries; nE++)\r
+                               {\r
+                                       if(_readListingBlocks)\r
+                                       {\r
+                                               bListingEndReached = (nIndexOfNextBlock==-1);\r
+\r
+                                               string keyWord = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);\r
+\r
+                                               bool isSeeAlsoKeyword = (binReader.ReadInt16()!=0);\r
+                               \r
+                                               int indent = binReader.ReadInt16(); // indent of entry\r
+                                               int nCharIndex = binReader.ReadInt32();\r
+\r
+                                               binReader.ReadInt32();\r
+\r
+                                               int numberOfPairs =     binReader.ReadInt32();\r
+\r
+                                               int[] nTopics = new int[numberOfPairs];\r
+                                               string[] seeAlso = new string[numberOfPairs];\r
+\r
+                                               for(int i=0; i < numberOfPairs; i++)\r
+                                               {\r
+                                                       if(isSeeAlsoKeyword)\r
+                                                       {\r
+                                                               seeAlso[i] = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               nTopics[i] = binReader.ReadInt32();\r
+                                                       }\r
+                                               }\r
+\r
+                                               binReader.ReadInt32(); // unknown\r
+\r
+                                               int nIndexOfThisEntry = binReader.ReadInt32();\r
+\r
+                                               IndexItem newItem = new IndexItem(_associatedFile, keyWord, isSeeAlsoKeyword, indent, nCharIndex, nIndexOfThisEntry, seeAlso, nTopics);\r
+                                               _indexList.Add(newItem);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               string keyWord = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);\r
+\r
+                                               bool isSeeAlsoKeyword = (binReader.ReadInt16()!=0);\r
+                               \r
+                                               int indent = binReader.ReadInt16(); // indent of entry\r
+                                               int nCharIndex = binReader.ReadInt32();\r
+\r
+                                               binReader.ReadInt32();\r
+\r
+                                               int numberOfPairs =     binReader.ReadInt32();\r
+\r
+                                               int[] nTopics = new int[numberOfPairs];\r
+                                               string[] seeAlso = new string[numberOfPairs];\r
+\r
+                                               for(int i=0; i < numberOfPairs; i++)\r
+                                               {\r
+                                                       if(isSeeAlsoKeyword)\r
+                                                       {\r
+                                                               seeAlso[i] = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               nTopics[i] = binReader.ReadInt32();\r
+                                                       }\r
+                                               }\r
+\r
+                                               int nIndexChild = binReader.ReadInt32();\r
+                                               int nIndexOfThisEntry=-1;\r
+\r
+                                               IndexItem newItem = new IndexItem(_associatedFile, keyWord, isSeeAlsoKeyword, indent, nCharIndex, nIndexOfThisEntry, seeAlso, nTopics);\r
+                                               _indexList.Add(newItem);\r
+\r
+                                       }\r
+                               }\r
+                       //}\r
+\r
+                       binReader.ReadBytes(freeSpace);\r
+                       \r
+\r
+                       if( bListingEndReached )\r
+                               _readListingBlocks = false;\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal generated index list\r
+               /// </summary>\r
+               internal ArrayList IndexList\r
+               {\r
+                       get { return _indexList; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs
new file mode 100644 (file)
index 0000000..a39ffc4
--- /dev/null
@@ -0,0 +1,2061 @@
+using System;\r
+using System.Diagnostics;\r
+using System.Collections;\r
+using System.Collections.Specialized;\r
+using System.IO;\r
+using System.Text;\r
+using System.Runtime.InteropServices;\r
+using System.Globalization;\r
+// using HtmlHelp.Storage;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// Internal enumeration for specifying the type of the html-help file\r
+       /// </summary>\r
+       internal enum HtmlHelpFileType\r
+       {\r
+               /// <summary>\r
+               /// CHM - compiled contents file\r
+               /// </summary>\r
+               /// <remarks>A file with this extension must always exist. If the file would be too long, some parts \r
+               /// can be splitted into the filestypes below.</remarks>\r
+               CHM = 0,\r
+               /// <summary>\r
+               /// CHI - compiled system file\r
+               /// </summary>\r
+               CHI = 1,\r
+               /// <summary>\r
+               /// CHQ - compiled fulltext search file\r
+               /// </summary>\r
+               CHQ = 2,\r
+               /// <summary>\r
+               /// CHW - compiled index file\r
+               /// </summary>\r
+               CHW = 3\r
+\r
+       }\r
+\r
+       /// <summary>\r
+       /// The class <c>CHMFile</c> implemts methods and properties for handling a single chmfile.\r
+       /// </summary>\r
+       public sealed class CHMFile : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Internal member storing a reference to the hosting HtmlHelpSystem instance\r
+               /// </summary>\r
+               private HtmlHelpSystem _systemInstance = null;\r
+               /// <summary>\r
+               /// Internal flag specifying if only system data has been loaded\r
+               /// </summary>\r
+               private bool _onlySystem = false;\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal arraylist containing the table of contents\r
+               /// </summary>\r
+               private ArrayList _toc = new ArrayList();\r
+               /// <summary>\r
+               /// Internal arraylist containing items of the toc which are merge-Links\r
+               /// </summary>\r
+               private ArrayList _mergeLinks = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the read information types\r
+               /// </summary>\r
+               private ArrayList _informationTypes = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the read categories\r
+               /// </summary>\r
+               private ArrayList _categories = new ArrayList();\r
+               /// <summary>\r
+               /// Internal arraylist containing the index (klinks)\r
+               /// </summary>\r
+               private ArrayList _indexKLinks = new ArrayList();\r
+               /// <summary>\r
+               /// Internal arraylist containing the index (alinks)\r
+               /// </summary>\r
+               private ArrayList _indexALinks = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the full filename\r
+               /// </summary>\r
+               private string _chmFileName = "";\r
+               /// <summary>\r
+               /// Internal member storing the full filename of the chi-file (includes all system files)\r
+               /// The file name is zero-length if there is no chi-file\r
+               /// </summary>\r
+               private string _chiFileName = "";\r
+               /// <summary>\r
+               /// Internal member storing the full filename of the chw-file (includes the help index)\r
+               /// The file name is zero-length if there is no chw-file\r
+               /// </summary>\r
+               private string _chwFileName = "";\r
+               /// <summary>\r
+               /// Internal member storing the full filename of the chq-file (includes the fulltext contents)\r
+               /// The file name is zero-length if there is no chq-file\r
+               /// </summary>\r
+               private string _chqFileName = "";\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #SYSTEM file\r
+               /// </summary>\r
+               private CHMSystem _systemFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #IDXHDR file\r
+               /// </summary>\r
+               private CHMIdxhdr _idxhdrFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #STRINGS file\r
+               /// </summary>\r
+               private CHMStrings _stringsFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #URLSTR file\r
+               /// </summary>\r
+               private CHMUrlstr _urlstrFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #URLTBL file\r
+               /// </summary>\r
+               private CHMUrltable _urltblFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #TOPICS file\r
+               /// </summary>\r
+               private CHMTopics _topicsFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal #TOCIDX file\r
+               /// </summary>\r
+               private CHMTocidx _tocidxFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal binary index file (KLinks).\r
+               /// </summary>\r
+               private CHMBtree _kLinks = null;\r
+               /// <summary>\r
+               /// Internal member storing the decoded information from the internal binary index file (ALinks).\r
+               /// </summary>\r
+               private CHMBtree _aLinks = null;\r
+               /// <summary>\r
+               /// Internal member storing the fulltext searcher for this file\r
+               /// </summary>\r
+               private FullTextEngine _ftSearcher = null;\r
+               /// <summary>\r
+               /// Internal member storing the default encoder\r
+               /// </summary>\r
+               private Encoding _textEncoding = Encoding.GetEncoding(1252); // standard windows-1252 encoder\r
+               /// <summary>\r
+               /// Internal memebr storing the chm file info\r
+               /// </summary>\r
+               private ChmFileInfo _chmFileInfo = null;\r
+               /// <summary>\r
+               /// Internal flag specifying if the dump must be written (if enabled)\r
+               /// </summary>\r
+               private bool _mustWriteDump = false;\r
+               /// <summary>\r
+               /// Internal flag specifying if data was read using the dump\r
+               /// </summary>\r
+               private bool _dumpRead = false;\r
+               /// <summary>\r
+               /// Internal member for specifying the number of dump-reading trys.\r
+               /// If dump-reading fails, this is used that it will not be opened a second time\r
+               /// (in CHM-Systems with CHM, CHI, etc. files)\r
+               /// </summary>\r
+               private int _dumpReadTrys = 0;\r
+               /// <summary>\r
+               /// Internal member storing the dumping info instance\r
+               /// </summary>\r
+               private DumpingInfo _dmpInfo = null;\r
+\r
+               private CHMStream.CHMStream _currentWrapper;\r
+               private CHMStream.CHMStream _baseStream=null;\r
+               public CHMStream.CHMStream BaseStream\r
+               {\r
+                       get \r
+                       { \r
+                               if (_baseStream==null)\r
+                                       _baseStream=new CHMStream.CHMStream(this.ChmFilePath);\r
+                               return _baseStream; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Creates a new instance of the class\r
+               /// </summary>\r
+               /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>\r
+               /// <param name="chmFile">chm file to read</param>\r
+               public CHMFile(HtmlHelpSystem systemInstance, string chmFile) : this(systemInstance, chmFile, false, null)\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Creates a new instance of the class\r
+               /// </summary>\r
+               /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>\r
+               /// <param name="chmFile">chm file to read</param>\r
+               /// <param name="dmpInfo">A dumping info class</param>\r
+               public CHMFile(HtmlHelpSystem systemInstance, string chmFile, DumpingInfo dmpInfo) : this(systemInstance, chmFile, false, dmpInfo)\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Creates a new instance of the class\r
+               /// </summary>\r
+               /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>\r
+               /// <param name="chmFile">chm file to read</param>\r
+               /// <param name="onlySystemData">true if only system data should be extracted (no index or toc)</param>\r
+               internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData) : this(systemInstance, chmFile, onlySystemData, null)\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Creates a new instance of the class\r
+               /// </summary>\r
+               /// <param name="systemInstance">a reference to the hosting HtmlHelpSystem instance</param>\r
+               /// <param name="chmFile">chm file to read</param>\r
+               /// <param name="onlySystemData">true if only system data should be extracted (no index or toc)</param>\r
+               /// <param name="dmpInfo">A dumping info class</param>\r
+               internal CHMFile(HtmlHelpSystem systemInstance, string chmFile, bool onlySystemData, DumpingInfo dmpInfo)\r
+               {\r
+                       _systemInstance = systemInstance;\r
+                       _dumpReadTrys=0;\r
+\r
+                       _dmpInfo = dmpInfo;\r
+                       if(dmpInfo != null)\r
+                       {\r
+                               dmpInfo.ChmFile = this;\r
+                       }\r
+\r
+                       if( ! chmFile.ToLower().EndsWith(".chm") )\r
+                       {\r
+                               throw new ArgumentException("HtmlHelp file must have the extension .chm !", "chmFile");\r
+                       }\r
+\r
+                       _chmFileName = chmFile;\r
+                       _chiFileName = "";\r
+\r
+                       // Read the IStorage file system\r
+                       if( File.Exists(chmFile) )\r
+                       {\r
+                               _onlySystem = onlySystemData;\r
+                               \r
+                               DateTime dtStartHH = DateTime.Now;\r
+\r
+                               string sCHIName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chi";\r
+                               string sCHQName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chq";\r
+                               string sCHWName = _chmFileName.Substring(0, _chmFileName.Length-3) + "chw";\r
+\r
+                               // If there is a CHI file present (this file includes the internal system files for the current chm file)\r
+                               if( File.Exists(sCHIName) )\r
+                               {\r
+                                       _chiFileName = sCHIName;\r
+                               \r
+                                       ReadFile(_chiFileName, HtmlHelpFileType.CHI);\r
+                               }\r
+\r
+                               // If there is a CHW file present (this file includes the internal binary index of the help)\r
+                               if(( File.Exists(sCHWName) ) && (!_onlySystem) )\r
+                               {\r
+                                       _chwFileName = sCHWName;\r
+                               \r
+                                       ReadFile(_chwFileName, HtmlHelpFileType.CHW);\r
+                               }\r
+\r
+                               // If there is a CHQ file present (this file includes the fulltext-search data)\r
+                               if(( File.Exists(sCHQName) ) && (!_onlySystem) )\r
+                               {\r
+                                       _chqFileName = sCHQName;\r
+                               \r
+                                       ReadFile(_chqFileName, HtmlHelpFileType.CHQ);\r
+                               }\r
+\r
+                               ReadFile(chmFile, HtmlHelpFileType.CHM);\r
+\r
+                               if(_mustWriteDump)\r
+                               {                                       \r
+                                       _mustWriteDump = !SaveDump(dmpInfo);\r
+\r
+                               }\r
+\r
+                               // check the default-topic setting\r
+                               if(_systemFile.DefaultTopic.Length > 0)\r
+                               {\r
+                                       CHMStream.CHMStream iw=null;\r
+                                       iw = new CHMStream.CHMStream(chmFile);\r
+                                       _currentWrapper=iw;\r
+\r
+                                       // tryo to open the topic file\r
+                                       MemoryStream fileObject = iw.OpenStream( _systemFile.DefaultTopic);\r
+                                       if( fileObject != null)\r
+                                       {\r
+                                               // if succeed, the topic default topic is OK\r
+                                               fileObject.Close();\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               // set the first topic of the toc-tree as default topic\r
+                                               if(_toc.Count > 0)\r
+                                               {\r
+                                                       _systemFile.SetDefaultTopic( ((TOCItem) _toc[0]).Local );\r
+                                               }\r
+                                       }\r
+                                       _currentWrapper=null;\r
+                               } \r
+                               else \r
+                               {\r
+                                       // set the first topic of the toc-tree as default topic\r
+                                       if(_toc.Count > 0)\r
+                                       {\r
+                                               _systemFile.SetDefaultTopic( ((TOCItem) _toc[0]).Local );\r
+                                       }\r
+                               }\r
+\r
+                               _chmFileInfo = new ChmFileInfo(this);\r
+                       } \r
+                       else \r
+                       {\r
+                               throw new ArgumentException("File '" + chmFile + "' not found !", "chmFile");\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Read a IStorage file\r
+               /// </summary>\r
+               /// <param name="fname">filename</param>\r
+               /// <param name="type">type of file</param>\r
+               private void ReadFile(string fname, HtmlHelpFileType type)\r
+               {\r
+                       CHMStream.CHMStream iw=null;\r
+                       iw=new CHMStream.CHMStream();                   \r
+                       iw.OpenCHM(fname);\r
+                       _currentWrapper=iw;\r
+                       MemoryStream fileObject=null;                   \r
+\r
+                       // ITStorageWrapper iw = null;\r
+\r
+                       // Open the internal chm system files and parse their content\r
+                       // FileObject fileObject = null;\r
+                       // iw = new ITStorageWrapper(fname, false);                     \r
+\r
+                       if( (type != HtmlHelpFileType.CHQ) && (type != HtmlHelpFileType.CHW) )\r
+                       {                               \r
+                               fileObject = iw.OpenStream("#SYSTEM");\r
+                               if ((fileObject != null) && (fileObject.Length>0))\r
+                                       _systemFile = new CHMSystem(fileObject.ToArray(), this);                                                                        \r
+\r
+                               fileObject = iw.OpenStream("#IDXHDR");\r
+                               if ((fileObject != null) && (fileObject.Length>0))\r
+                                       _idxhdrFile = new CHMIdxhdr(fileObject.ToArray(), this);                                        \r
+                               \r
+                               // try to read Dump\r
+                               if((!_dumpRead)&&(CheckDump(_dmpInfo))&&(_dumpReadTrys==0))\r
+                               {\r
+                                       _dumpReadTrys++;\r
+                                       _dumpRead = LoadDump(_dmpInfo);                                 \r
+                               }\r
+\r
+                               if( (!_dumpRead)||(!_dmpInfo.DumpStrings) )\r
+                               {\r
+                                       fileObject = iw.OpenStream( "#STRINGS");\r
+                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                               _stringsFile = new CHMStrings(fileObject.ToArray(), this);\r
+                               }\r
+\r
+                               if( (!_dumpRead)||(!_dmpInfo.DumpUrlStr) )\r
+                               {\r
+                                       fileObject = iw.OpenStream( "#URLSTR");\r
+                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                               _urlstrFile = new CHMUrlstr(fileObject.ToArray(), this);\r
+                               }\r
+\r
+                               if( (!_dumpRead)||(!_dmpInfo.DumpUrlTbl) )\r
+                               {\r
+                                       fileObject = iw.OpenStream( "#URLTBL");\r
+                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                               _urltblFile = new CHMUrltable(fileObject.ToArray(), this);\r
+                               }\r
+\r
+                               if( (!_dumpRead)||(!_dmpInfo.DumpTopics) )\r
+                               {\r
+                                       fileObject = iw.OpenStream( "#TOPICS");\r
+                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                               _topicsFile = new CHMTopics(fileObject.ToArray(), this);\r
+                               }\r
+\r
+                               if(!_onlySystem)\r
+                               {\r
+                                       if( (!_dumpRead)||(!_dmpInfo.DumpBinaryTOC) )\r
+                                       {\r
+                                               fileObject = iw.OpenStream( "#TOCIDX");\r
+                                               if( (fileObject != null) && (fileObject.Length>0) && (_systemFile.BinaryTOC) )\r
+                                               {\r
+                                                       _tocidxFile = new CHMTocidx(fileObject.ToArray(), this);\r
+                                                       _toc = _tocidxFile.TOC;                                                 \r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               if( (_systemFile != null) && (!_onlySystem) )\r
+                               {\r
+                                       if(!_systemFile.BinaryTOC)\r
+                                       {\r
+                                               if( (!_dumpRead)||(!_dmpInfo.DumpTextTOC) )\r
+                                               {\r
+                                                       CHMStream.chmUnitInfo HHCInfo=iw.GetFileInfoByExtension(".hhc");\r
+                                                       if (HHCInfo!=null)\r
+                                                       {\r
+                                                               fileObject = iw.OpenStream(HHCInfo);                                                    \r
+                                                               if ((fileObject != null) && (fileObject.Length>0))\r
+                                                               {\r
+                                                                       ASCIIEncoding ascii=new ASCIIEncoding();                        \r
+                                                                       string fileString =ascii.GetString(fileObject.ToArray(),0,(int)fileObject.Length);\r
+\r
+                                                                       _toc = HHCParser.ParseHHC(fileString, this);\r
+                                                               }\r
+\r
+                                                               if(HHCParser.HasMergeLinks)\r
+                                                                       _mergeLinks = HHCParser.MergeItems;\r
+                                                       }                                                       \r
+                                               }\r
+                                       } \r
+                               }\r
+                       }\r
+\r
+                       if( type != HtmlHelpFileType.CHQ ) // no index information in CHQ files (only fulltext search)\r
+                       {\r
+                               if( (_systemFile != null) && (!_onlySystem) )\r
+                               {\r
+                                       if( ! _systemFile.BinaryIndex )\r
+                                       {\r
+                                               if( (!_dumpRead)||(!_dmpInfo.DumpTextIndex) )\r
+                                               {\r
+                                                       fileObject = iw.OpenStream( _systemFile.IndexFile);\r
+                                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                                       {\r
+\r
+                                                               string fileString = this.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);\r
+                                                               // string fileString = fileObject.ReadFromFile(this.TextEncoding);\r
+                                                               fileObject.Close();\r
+\r
+                                                               _indexKLinks = HHKParser.ParseHHK(fileString, this);\r
+                                                               _indexKLinks.Sort();\r
+                                                       }\r
+                                               }\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               if( (!_dumpRead)||(!_dmpInfo.DumpBinaryIndex) )\r
+                                               {\r
+                                                       fileObject=iw.OpenStream(@"$WWKeywordLinks\BTree");\r
+                                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                                       {\r
+                                                               _kLinks = new CHMBtree(fileObject.ToArray(), this);\r
+                                                               _indexKLinks = _kLinks.IndexList;\r
+                                                       }\r
+\r
+                                                       fileObject =iw.OpenStream(@"$WWAssociativeLinks\BTree");\r
+                                                       if ((fileObject != null) && (fileObject.Length>0))\r
+                                                       {                                                                       \r
+                                                               _aLinks = new CHMBtree(fileObject.ToArray(), this);\r
+                                                               _indexALinks = _aLinks.IndexList;\r
+                                                               _indexALinks.Sort();                                                                    \r
+                                                       }                                                       \r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if( (type != HtmlHelpFileType.CHI) && (type != HtmlHelpFileType.CHW) )\r
+                       {\r
+                               if( (_systemFile != null) && (!_onlySystem) )\r
+                               {\r
+                                       if( _systemFile.FullTextSearch)\r
+                                       {\r
+                                               if( (!_dumpRead)||(!_dmpInfo.DumpFullText) )\r
+                                               {\r
+                                                       fileObject = iw.OpenStream("$FIftiMain");\r
+                                                       if(( fileObject != null) && (fileObject .Length>0))\r
+                                                               _ftSearcher = new FullTextEngine(fileObject .ToArray(), this);                                                  \r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       _currentWrapper=null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Enumerates the files in the chm storage and gets all files matching a given extension.\r
+               /// </summary>\r
+               /// <param name="extension">extension to return</param>\r
+               /// <returns>Returns an arraylist of filenames or null if nothing found</returns>\r
+               /// <remarks>On large CHMs, enumerations are very slow. Only use it if necessary !</remarks>\r
+               internal ArrayList EnumFilesByExtension(string extension)\r
+               {\r
+                       ArrayList arrRet = new ArrayList();\r
+\r
+                       CHMStream.CHMStream iw = null;\r
+                       if(_currentWrapper == null)\r
+                               iw = new CHMStream.CHMStream(_chmFileName);\r
+                       else\r
+                               iw = _currentWrapper;\r
+\r
+                       arrRet=iw.GetFileListByExtenstion(extension);\r
+                       \r
+                       if(arrRet.Count > 0)\r
+                               return arrRet;\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Searches an TOC entry using the local\r
+               /// </summary>\r
+               /// <param name="local">local to search</param>\r
+               /// <returns>Returns the TOC item</returns>\r
+               internal TOCItem GetTOCItemByLocal(string local)\r
+               {\r
+                       return GetTOCItemByLocal(this.TOC, local);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Recursively searches an TOC entry using its local\r
+               /// </summary>\r
+               /// <param name="arrTOC">toc level list</param>\r
+               /// <param name="local">local to search</param>\r
+               /// <returns>Returns the TOC item</returns>\r
+               private TOCItem GetTOCItemByLocal(ArrayList arrTOC, string local)\r
+               {\r
+                       TOCItem ret = null;\r
+                       foreach(TOCItem curItem in arrTOC)\r
+                       {\r
+                               string scL = curItem.Local.ToLower();\r
+                               string sL = local.ToLower();\r
+\r
+                               while(scL[0]=='/') // delete prefixing '/'\r
+                                       scL = scL.Substring(1);\r
+\r
+                               while(sL[0]=='/') // delete prefixing '/'\r
+                                       sL = sL.Substring(1);\r
+\r
+                               if(scL == sL)\r
+                                       return curItem;\r
+\r
+                               if(curItem.Children.Count > 0)\r
+                               {\r
+                                       ret =  GetTOCItemByLocal(curItem.Children, local);\r
+                                       if(ret != null)\r
+                                               return ret;\r
+                               }\r
+                       }\r
+\r
+                       return ret;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Removes a TOCItem from the toc\r
+               /// </summary>\r
+               /// <param name="rem">item to remove</param>\r
+               /// <returns>Returns true if removed</returns>\r
+               internal bool RemoveTOCItem(TOCItem rem)\r
+               {\r
+                       if(rem == null)\r
+                               return false;\r
+\r
+                       return RemoveTOCItem(this.TOC, rem);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Recursively searches a TOCItem and removes it if found\r
+               /// </summary>\r
+               /// <param name="arrTOC">toc level list</param>\r
+               /// <param name="rem">item to remove</param>\r
+               /// <returns>Returns true if removed</returns>\r
+               private bool RemoveTOCItem(ArrayList arrTOC, TOCItem rem)\r
+               {\r
+                       for(int i=0; i<arrTOC.Count;i++)\r
+                       {\r
+                               TOCItem curItem = arrTOC[i] as TOCItem;\r
+\r
+                               if(curItem == rem)\r
+                               {\r
+                                       arrTOC.RemoveAt(i);\r
+                                       return true;\r
+                               }\r
+\r
+                               if(curItem.Children.Count > 0)\r
+                               {\r
+                                       bool bRem = RemoveTOCItem(curItem.Children, rem);\r
+                                       if(bRem)\r
+                                               return true;\r
+                               }\r
+                       }\r
+\r
+                       return false;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Returns true if the HtmlHelpSystem instance contains 1 or more information types\r
+               /// </summary>\r
+               public bool HasInformationTypes\r
+               {\r
+                       get { return (_informationTypes.Count>0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Returns true if the HtmlHelpSystem instance contains 1 or more categories\r
+               /// </summary>\r
+               public bool HasCategories\r
+               {\r
+                       get { return (_categories.Count>0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an ArrayList of <see cref="InformationType">InformationType</see> items\r
+               /// </summary>\r
+               public ArrayList InformationTypes\r
+               {\r
+                       get { return _informationTypes; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an ArrayList of <see cref="Category">Category</see> items\r
+               /// </summary>\r
+               public ArrayList Categories\r
+               {\r
+                       get { return _categories; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the information type specified by its name\r
+               /// </summary>\r
+               /// <param name="name">name of the information type to receive</param>\r
+               /// <returns>Returns the Instance for the name or null if not found</returns>\r
+               public InformationType GetInformationType(string name)\r
+               {\r
+                       if(HasInformationTypes)\r
+                       {\r
+                               for(int i=0; i<_informationTypes.Count;i++)\r
+                               {\r
+                                       InformationType iT = _informationTypes[i] as InformationType;\r
+\r
+                                       if(iT.Name == name)\r
+                                               return iT;\r
+                               }\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the category specifiyd by its name\r
+               /// </summary>\r
+               /// <param name="name">name of the category</param>\r
+               /// <returns>Returns the Instance for the name or null if not found</returns>\r
+               public Category GetCategory(string name)\r
+               {\r
+                       if(HasCategories)\r
+                       {\r
+                               for(int i=0; i<_categories.Count;i++)\r
+                               {\r
+                                       Category cat = _categories[i] as Category;\r
+\r
+                                       if(cat.Name == name)\r
+                                               return cat;\r
+                               }\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               #region Dumping methods\r
+               \r
+               /// <summary>\r
+               /// Checks if a dump for this file exists and if it can be read\r
+               /// </summary>\r
+               /// <param name="dmpInfo">dumping info class</param>\r
+               /// <returns>true if it can be read</returns>\r
+               private bool CheckDump(DumpingInfo dmpInfo)\r
+               {\r
+                       if(_dumpReadTrys<=0)\r
+                               _mustWriteDump = false;\r
+\r
+                       if(_onlySystem)\r
+                               return false;\r
+\r
+                       if( dmpInfo != null )\r
+                       {\r
+                               if(_dumpReadTrys > 0)\r
+                                       return _mustWriteDump;\r
+\r
+                               _mustWriteDump = !dmpInfo.DumpExists;\r
+                               return !_mustWriteDump;\r
+                       }\r
+\r
+                       return false;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Saves the the toc and index into a data dump\r
+               /// </summary>\r
+               /// <param name="dmpInfo">dumping info</param>\r
+               /// <returns>true if succeed</returns>\r
+               private bool SaveDump(DumpingInfo dmpInfo)\r
+               {\r
+                       if(dmpInfo == null)\r
+                               return false;\r
+\r
+                       bool bRet = false;\r
+\r
+\r
+                       BinaryWriter writer = dmpInfo.Writer;\r
+\r
+                       int nCnt = 0;\r
+                       try\r
+                       {\r
+                               Debug.WriteLine("writing dump-file header");\r
+                               FileInfo fi = new FileInfo(_chmFileName);\r
+                               string ftime = fi.LastWriteTime.ToString("dd.MM.yyyy HH:mm:ss.ffffff");\r
+\r
+                               writer.Write("HtmlHelpSystem dump file 1.0");\r
+                               writer.Write(ftime);\r
+                               writer.Write(_textEncoding.CodePage);\r
+\r
+                               // strings dumping\r
+                               if(dmpInfo.DumpStrings)\r
+                               {\r
+                                       writer.Write(true); // data should be in dump\r
+\r
+                                       if(_stringsFile==null)\r
+                                       {\r
+                                               writer.Write(false); // data not supported by the chm\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               Debug.WriteLine("writing #STRINGS");\r
+                                               writer.Write(true); // data supported and following\r
+                                               _stringsFile.Dump(ref writer);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       writer.Write(false); // data is not in dump\r
+                               }\r
+\r
+                               // urlstr dumping\r
+                               if(dmpInfo.DumpUrlStr)\r
+                               {\r
+                                       writer.Write(true);\r
+\r
+                                       if(_urlstrFile==null)\r
+                                       {\r
+                                               writer.Write(false);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               Debug.WriteLine("writing #URLSTR");\r
+                                               writer.Write(true);\r
+                                               _urlstrFile.Dump(ref writer);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       writer.Write(false);\r
+                               }\r
+\r
+                               // urltbl dumping\r
+                               if(dmpInfo.DumpUrlTbl)\r
+                               {\r
+                                       writer.Write(true);\r
+\r
+                                       if(_urltblFile==null)\r
+                                       {\r
+                                               writer.Write(false);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               Debug.WriteLine("writing #URLTBL");\r
+                                               writer.Write(true);\r
+                                               _urltblFile.Dump(ref writer);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       writer.Write(false);\r
+                               }\r
+\r
+                               // topics dumping\r
+                               if(dmpInfo.DumpTopics)\r
+                               {\r
+                                       writer.Write(true);\r
+\r
+                                       if(_topicsFile==null)\r
+                                       {\r
+                                               writer.Write(false);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               Debug.WriteLine("writing #TOPICS");\r
+                                               writer.Write(true);\r
+                                               _topicsFile.Dump(ref writer);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       writer.Write(false);\r
+                               }\r
+\r
+                               // ftsearch dumping\r
+                               if(dmpInfo.DumpFullText)\r
+                               {\r
+                                       writer.Write(true);\r
+\r
+                                       if(_ftSearcher==null)\r
+                                       {\r
+                                               writer.Write(false);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               Debug.WriteLine("writing $FIftiMain");\r
+                                               writer.Write(true);\r
+                                               _ftSearcher.Dump(ref writer);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       writer.Write(false);\r
+                               }\r
+\r
+                               // TOC dumping\r
+                               bool bWriteTOC = false;\r
+\r
+                               if( (_systemFile.BinaryTOC) && (dmpInfo.DumpBinaryTOC) )\r
+                               {\r
+                                       Debug.WriteLine("writing binary TOC");\r
+                                       bWriteTOC = true;\r
+                               }\r
+\r
+                               if( (!_systemFile.BinaryTOC) && (dmpInfo.DumpTextTOC) )\r
+                               {\r
+                                       Debug.WriteLine("writing text-based TOC");\r
+                                       bWriteTOC = true;\r
+                               }\r
+\r
+                               writer.Write(bWriteTOC);\r
+\r
+                               if(bWriteTOC)\r
+                               {\r
+                                       // write table of contents\r
+                                       writer.Write( _toc.Count );\r
+\r
+                                       for(nCnt=0; nCnt < _toc.Count; nCnt++)\r
+                                       {\r
+                                               TOCItem curItem = ((TOCItem)(_toc[nCnt]));\r
+                                               curItem.Dump( ref writer );\r
+                                       }\r
+                               }\r
+\r
+                               // Index dumping\r
+                               bool bWriteIdx = false;\r
+\r
+                               if( (_systemFile.BinaryIndex) && (dmpInfo.DumpBinaryIndex) )\r
+                               {\r
+                                       Debug.WriteLine("writing binary index");\r
+                                       bWriteIdx = true;\r
+                               }\r
+\r
+                               if( (!_systemFile.BinaryIndex) && (dmpInfo.DumpTextIndex) )\r
+                               {\r
+                                       Debug.WriteLine("writing text-based index");\r
+                                       bWriteIdx = true;\r
+                               }\r
+\r
+                               writer.Write(bWriteIdx);\r
+\r
+                               if(bWriteIdx)\r
+                               {\r
+                                       // write index\r
+                                       writer.Write( _indexALinks.Count );\r
+                                       for(nCnt=0; nCnt < _indexALinks.Count; nCnt++)\r
+                                       {\r
+                                               IndexItem curItem = ((IndexItem)(_indexALinks[nCnt]));\r
+                                               curItem.Dump( ref writer );\r
+                                       }\r
+\r
+                                       writer.Write( _indexKLinks.Count );\r
+                                       for(nCnt=0; nCnt < _indexKLinks.Count; nCnt++)\r
+                                       {\r
+                                               IndexItem curItem = ((IndexItem)(_indexKLinks[nCnt]));\r
+                                               curItem.Dump( ref writer );\r
+                                       }\r
+                               }\r
+\r
+                               // Information types dumping\r
+                               writer.Write( _informationTypes.Count );\r
+\r
+                               Debug.WriteLine("writing " + _informationTypes.Count.ToString() + " information types");\r
+\r
+                               for(nCnt=0; nCnt<_informationTypes.Count;nCnt++)\r
+                               {\r
+                                       InformationType curType = _informationTypes[nCnt] as InformationType;\r
+                                       \r
+                                       curType.Dump(ref writer);\r
+                               }\r
+\r
+                               // Categories dumping\r
+                               writer.Write( _categories.Count );\r
+\r
+                               Debug.WriteLine("writing " + _categories.Count.ToString() + " categories");\r
+\r
+                               for(nCnt=0; nCnt<_categories.Count; nCnt++)\r
+                               {\r
+                                       Category curCat = _categories[nCnt] as Category;\r
+\r
+                                       curCat.Dump( ref writer);\r
+                               }\r
+\r
+                               bRet=true;\r
+                       }\r
+                       finally\r
+                       {\r
+                               dmpInfo.SaveData();\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Parses a HHC file which is located in the current CHM.\r
+               /// </summary>\r
+               /// <param name="hhcFile">hhc file to parse</param>\r
+               /// <returns>an arraylist with toc items</returns>\r
+               public ArrayList ParseHHC(string hhcFile)\r
+               {\r
+                       ArrayList arrRet = new ArrayList();\r
+                       \r
+                       CHMStream.CHMStream iw=null;\r
+                       iw=new CHMStream.CHMStream();                   \r
+                       iw.OpenCHM(_chmFileName);                       \r
+                       MemoryStream fileObject=null;                   \r
+\r
+                       fileObject = iw.OpenStream(hhcFile);\r
+                       if( fileObject != null)\r
+                       {       \r
+                               ASCIIEncoding ascii=new ASCIIEncoding();                        \r
+                               string fileString =ascii.GetString(fileObject.ToArray(),0,(int)fileObject.Length);                              \r
+                               fileObject.Close();\r
+\r
+                               arrRet = HHCParser.ParseHHC(fileString, this);\r
+\r
+                               if(HHCParser.HasMergeLinks)\r
+                               {\r
+                                       foreach(TOCItem curItem in HHCParser.MergeItems)\r
+                                       {\r
+                                               _mergeLinks.Add(curItem);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       return arrRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Loads the toc and index from a data dump\r
+               /// </summary>\r
+               /// <param name="dmpInfo">dumping info</param>\r
+               /// <returns>true if succeed</returns>\r
+               private bool LoadDump(DumpingInfo dmpInfo)\r
+               {\r
+                       if(dmpInfo == null)\r
+                               return false;\r
+\r
+                       bool bRet = false;\r
+\r
+                       try\r
+                       {\r
+                               BinaryReader reader = dmpInfo.Reader;\r
+\r
+                               if(reader == null)\r
+                               {\r
+                                       Debug.WriteLine("No reader returned !");\r
+                                       dmpInfo.SaveData(); // closes the dump\r
+                                       _mustWriteDump = true;\r
+                                       return false;\r
+                               }\r
+\r
+                               int nCnt = 0;\r
+\r
+                               Debug.WriteLine("reading dump-file header");\r
+                               FileInfo fi = new FileInfo(_chmFileName);\r
+                               string ftime = fi.LastWriteTime.ToString("dd.MM.yyyy HH:mm:ss.ffffff");\r
+\r
+                               string header = reader.ReadString();\r
+\r
+                               if( header != "HtmlHelpSystem dump file 1.0")\r
+                               {\r
+                                       Debug.WriteLine("Unsupported dump-file format !");\r
+                                       dmpInfo.SaveData(); // closes the dump\r
+                                       _mustWriteDump = true;\r
+                                       return false;\r
+                               }\r
+\r
+                               string ftimecheck = reader.ReadString();\r
+\r
+                               reader.ReadInt32(); // codepage, we'll use the same as for the chm file which is already set.\r
+\r
+//                             if(ftimecheck != ftime)\r
+//                             {\r
+//                                     Debug.WriteLine("Dump is out of date (CHM file changed during last dump creation) !");\r
+//                                     dmpInfo.SaveData(); // closes the dump\r
+//                                     _mustWriteDump = true;\r
+//                                     return false; // force reload\r
+//                             }\r
+                               \r
+\r
+                               bool bFlag=false;  // true if data should be in dump\r
+                               bool bFlagSupp=false; // false if data is not supported by the chm\r
+\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       bFlagSupp = reader.ReadBoolean();\r
+\r
+                                       if(!dmpInfo.DumpStrings)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #STRINGS found but not expected !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(bFlagSupp)\r
+                                       {\r
+                                               Debug.WriteLine("reading #STRINGS");\r
+                                               _stringsFile = new CHMStrings();\r
+                                               _stringsFile.SetCHMFile(this);\r
+                                               _stringsFile.ReadDump(ref reader);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       if(dmpInfo.DumpStrings)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #STRINGS expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       bFlagSupp = reader.ReadBoolean();\r
+\r
+                                       if(!dmpInfo.DumpUrlStr)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #URLSTR found but not expected !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(bFlagSupp)\r
+                                       {\r
+                                               Debug.WriteLine("reading #URLSTR");\r
+                                               _urlstrFile = new CHMUrlstr();\r
+                                               _urlstrFile.SetCHMFile(this);\r
+                                               _urlstrFile.ReadDump(ref reader);\r
+                                       }\r
+                               }\r
+                               else \r
+                               {\r
+                                       if(dmpInfo.DumpUrlStr)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #URLSTR expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       bFlagSupp = reader.ReadBoolean();\r
+\r
+                                       if(!dmpInfo.DumpUrlTbl)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #URLTBL found but not expected !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(bFlagSupp)\r
+                                       {\r
+                                               Debug.WriteLine("reading #URLTBL");\r
+                                               _urltblFile = new CHMUrltable();\r
+                                               _urltblFile.SetCHMFile(this);\r
+                                               _urltblFile.ReadDump(ref reader);\r
+                                       }\r
+                               }\r
+                               else \r
+                               {\r
+                                       if(dmpInfo.DumpUrlTbl)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #URLTBL expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       bFlagSupp = reader.ReadBoolean();\r
+\r
+                                       if(!dmpInfo.DumpTopics)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #TOPICS found but not expected !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(bFlagSupp)\r
+                                       {\r
+                                               Debug.WriteLine("reading #TOPICS");\r
+                                               _topicsFile = new CHMTopics();\r
+                                               _topicsFile.SetCHMFile(this);\r
+                                               _topicsFile.ReadDump(ref reader);\r
+                                       }\r
+                               }\r
+                               else \r
+                               {\r
+                                       if(dmpInfo.DumpTopics)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped #TOPICS expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       bFlagSupp = reader.ReadBoolean();\r
+\r
+                                       if(!dmpInfo.DumpFullText)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped $FIftiMain found but not expected !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(bFlagSupp)\r
+                                       {\r
+                                               Debug.WriteLine("reading $FIftiMain");\r
+                                               _ftSearcher = new FullTextEngine();\r
+                                               _ftSearcher.SetCHMFile(this);\r
+                                               _ftSearcher.ReadDump(ref reader);\r
+                                       }\r
+                               }\r
+                               else \r
+                               {\r
+                                       if(dmpInfo.DumpFullText)\r
+                                       {\r
+                                               Debug.WriteLine("Dumped $FIftiMain expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               // read table of contents\r
+                               _toc.Clear();\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       if((_systemFile.BinaryTOC)&&(!dmpInfo.DumpBinaryTOC))\r
+                                       {\r
+                                               Debug.WriteLine("Binary TOC expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       } \r
+\r
+                                       if((!_systemFile.BinaryTOC)&&(!dmpInfo.DumpTextTOC))\r
+                                       {\r
+                                               Debug.WriteLine("Text-based TOC expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(_systemFile.BinaryTOC)\r
+                                               Debug.WriteLine("reading binary TOC");\r
+                                       else\r
+                                               Debug.WriteLine("reading text-based TOC");\r
+\r
+                                       int nTocCnt = reader.ReadInt32();\r
+\r
+                                       for(nCnt=0; nCnt < nTocCnt; nCnt++)\r
+                                       {\r
+                                               TOCItem item = new TOCItem();\r
+                                               item.AssociatedFile = this;\r
+                                               item.ChmFile = _chmFileName;\r
+                                               item.ReadDump(ref reader);\r
+                                               if(item.MergeLink.Length > 0)\r
+                                                       _mergeLinks.Add(item);\r
+\r
+                                               _toc.Add(item);\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       if((_systemFile.BinaryTOC)&&(dmpInfo.DumpBinaryTOC))\r
+                                       {\r
+                                               Debug.WriteLine("Binary TOC expected but no TOC dump !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       } \r
+\r
+                                       if((!_systemFile.BinaryTOC)&&(dmpInfo.DumpTextTOC))\r
+                                       {\r
+                                               Debug.WriteLine("Text-based TOC expected but no TOC dump !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               // read index\r
+                               _indexALinks.Clear();\r
+                               _indexKLinks.Clear();\r
+\r
+                               bFlag = reader.ReadBoolean();\r
+\r
+                               if(bFlag)\r
+                               {\r
+                                       if((_systemFile.BinaryIndex)&&(!dmpInfo.DumpBinaryIndex))\r
+                                       {\r
+                                               Debug.WriteLine("Binary index expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if((!_systemFile.BinaryIndex)&&(!dmpInfo.DumpTextIndex))\r
+                                       {\r
+                                               Debug.WriteLine("Binary index expected but not found !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+\r
+                                       if(_systemFile.BinaryIndex)\r
+                                               Debug.WriteLine("reading binary index");\r
+                                       else\r
+                                               Debug.WriteLine("reading text-based index");\r
+\r
+                                       int nIndxaCnt = reader.ReadInt32();\r
+\r
+                                       for(nCnt=0; nCnt < nIndxaCnt; nCnt++)\r
+                                       {\r
+                                               IndexItem item = new IndexItem();\r
+                                               item.ChmFile = this;\r
+                                               item.ReadDump(ref reader);\r
+                                               _indexALinks.Add(item);\r
+                                       }\r
+\r
+                               \r
+                                       int nIndxkCnt = reader.ReadInt32();\r
+\r
+                                       for(nCnt=0; nCnt < nIndxkCnt; nCnt++)\r
+                                       {\r
+                                               IndexItem item = new IndexItem();\r
+                                               item.ChmFile = this;\r
+                                               item.ReadDump(ref reader);\r
+                                               _indexKLinks.Add(item);\r
+                                       }\r
+                               }\r
+                               else \r
+                               {\r
+                                       if((_systemFile.BinaryIndex)&&(dmpInfo.DumpBinaryIndex))\r
+                                       {\r
+                                               Debug.WriteLine("Binary index expected but no index in dump !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       } \r
+\r
+                                       if((!_systemFile.BinaryIndex)&&(dmpInfo.DumpTextIndex))\r
+                                       {\r
+                                               Debug.WriteLine("Text-based index expected but no index dump !");\r
+                                               dmpInfo.SaveData(); // closes the dump\r
+                                               _mustWriteDump = true;\r
+                                               return false; // force reload\r
+                                       }\r
+                               }\r
+\r
+                               // read information types from dump\r
+                               int nITCnt = reader.ReadInt32();\r
+\r
+                               Debug.WriteLine("Reading " + nITCnt.ToString() + " information types from dump !");\r
+\r
+                               for(nCnt=0; nCnt<nITCnt; nCnt++)\r
+                               {\r
+                                       InformationType newType = new InformationType();\r
+                                       newType.ReadDump(ref reader);\r
+\r
+                                       if( SystemInstance.GetInformationType( newType.Name ) != null)\r
+                                       {\r
+                                               // information type of this name already exists in the helpsystem\r
+                                               InformationType sysType = SystemInstance.GetInformationType( newType.Name );\r
+                                               _informationTypes.Add(sysType);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               _informationTypes.Add( newType );\r
+                                       }\r
+                               }\r
+\r
+                               // read categories from dump\r
+                               int nCCnt = reader.ReadInt32();\r
+\r
+                               Debug.WriteLine("Reading " + nITCnt.ToString() + " categories from dump !");\r
+\r
+                               for(nCnt=0; nCnt<nCCnt; nCnt++)\r
+                               {\r
+                                       Category newCat = new Category();\r
+                                       newCat.ReadDump(ref reader, this);\r
+\r
+                                       if( SystemInstance.GetCategory( newCat.Name ) != null)\r
+                                       {\r
+                                               // category of this name already exists in the helpsystem\r
+                                               Category sysCat = SystemInstance.GetCategory( newCat.Name );\r
+\r
+                                               sysCat.MergeInfoTypes( newCat );\r
+                                               _categories.Add( sysCat );\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               _categories.Add( newCat );\r
+                                       }\r
+                               }\r
+\r
+                               _dumpRead = true;\r
+                               bRet = true;\r
+                       }\r
+                       catch(Exception ex)\r
+                       {\r
+                               Debug.WriteLine("###ERROR :" + ex.Message);\r
+                               _mustWriteDump = true;\r
+                       }\r
+                       finally\r
+                       {\r
+                               dmpInfo.SaveData(); // closes the dump\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               #endregion\r
+\r
+               #region Internal properties\r
+               /// <summary>\r
+               /// Gets the current storage wrapper.\r
+               /// </summary>\r
+               /// <remarks>This property will return not null, if there are currently file read actions running !</remarks>\r
+               internal CHMStream.CHMStream CurrentStorageWrapper\r
+               {\r
+                       get { return _currentWrapper;}\r
+               }\r
+               /// <summary>\r
+               /// Gets/sets the hosting HtmlHelpSystem instance\r
+               /// </summary>\r
+               internal HtmlHelpSystem SystemInstance\r
+               {\r
+                       get { return _systemInstance; }\r
+                       set { _systemInstance = value; }\r
+               }\r
+               /// <summary>\r
+               /// Gets an arraylist of TOC items which contains merg-links to other CHMs\r
+               /// </summary>\r
+               internal ArrayList MergLinks\r
+               {\r
+                       get { return _mergeLinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal system file instance\r
+               /// </summary>\r
+               internal CHMSystem SystemFile\r
+               {\r
+                       get { return _systemFile; }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the internal idxhdr file instance\r
+               /// </summary>\r
+               internal CHMIdxhdr IdxHdrFile\r
+               {\r
+                       get { return _idxhdrFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal strings file instance\r
+               /// </summary>\r
+               internal CHMStrings StringsFile\r
+               {\r
+                       get { return _stringsFile; }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the internal urlstr file instance\r
+               /// </summary>\r
+               internal CHMUrlstr UrlstrFile\r
+               {\r
+                       get { return _urlstrFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal urltbl file instance\r
+               /// </summary>\r
+               internal CHMUrltable UrltblFile\r
+               {\r
+                       get { return _urltblFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal topics file instance\r
+               /// </summary>\r
+               internal CHMTopics TopicsFile\r
+               {\r
+                       get { return _topicsFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal tocidx file instance\r
+               /// </summary>\r
+               internal CHMTocidx TocidxFile\r
+               {\r
+                       get { return _tocidxFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal btree file instance for alinks\r
+               /// </summary>\r
+               internal CHMBtree ALinksFile\r
+               {\r
+                       get { return _aLinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal btree file instance for klinks\r
+               /// </summary>\r
+               internal CHMBtree KLinksFile\r
+               {\r
+                       get { return _kLinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the text encoding\r
+               /// </summary>\r
+               internal Encoding TextEncoding\r
+               {\r
+                       get { return _textEncoding; }\r
+                       set { _textEncoding = value; }\r
+               }\r
+\r
+               #endregion\r
+\r
+               #region Properties from the #SYSTEM file\r
+               /// <summary>\r
+               /// Gets the file version of the chm file. \r
+               /// 2 for Compatibility=1.0,  3 for Compatibility=1.1\r
+               /// </summary>\r
+               internal int FileVersion\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return 0;\r
+\r
+                               return _systemFile.FileVersion; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the contents file name\r
+               /// </summary>\r
+               internal string ContentsFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.ContentsFile; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the index file name\r
+               /// </summary>\r
+               internal string IndexFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.IndexFile; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default help topic\r
+               /// </summary>\r
+               internal string DefaultTopic\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.DefaultTopic; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the title of the help window\r
+               /// </summary>\r
+               internal string HelpWindowTitle\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.Title; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if DBCS is in use\r
+               /// </summary>\r
+               internal bool DBCS\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return false;\r
+\r
+                               return _systemFile.DBCS; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if full-text-search is available\r
+               /// </summary>\r
+               internal bool FullTextSearch\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return false;\r
+\r
+                               return _systemFile.FullTextSearch; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the file has ALinks\r
+               /// </summary>\r
+               internal bool HasALinks\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return false;\r
+\r
+                               return _systemFile.HasALinks; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the file has KLinks\r
+               /// </summary>\r
+               internal bool HasKLinks\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return false;\r
+\r
+                               return _systemFile.HasKLinks; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default window name\r
+               /// </summary>\r
+               internal string DefaultWindow\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.DefaultWindow; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the file name of the compile file\r
+               /// </summary>\r
+               internal string CompileFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.CompileFile; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary index file\r
+               /// </summary>\r
+               internal bool BinaryIndex\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return false;\r
+\r
+                               return _systemFile.BinaryIndex; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary index file\r
+               /// </summary>\r
+               internal string CompilerVersion\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return String.Empty;\r
+\r
+                               return _systemFile.CompilerVersion; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary toc file\r
+               /// </summary>\r
+               internal bool BinaryTOC\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return false;\r
+\r
+                               return _systemFile.BinaryTOC; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the font face of the read font property.\r
+               /// Empty string for default font.\r
+               /// </summary>\r
+               internal string FontFace\r
+               {\r
+                       get\r
+                       {\r
+                               if(_systemFile==null)\r
+                                       return "";\r
+                                       \r
+                               return _systemFile.FontFace; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the font size of the read font property.\r
+               /// 0 for default font size\r
+               /// </summary>\r
+               internal double FontSize\r
+               {\r
+                       get\r
+                       {\r
+                               if(_systemFile==null)\r
+                                       return 0;\r
+                                       \r
+                               return _systemFile.FontSize; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the character set of the read font property\r
+               /// 1 for default\r
+               /// </summary>\r
+               internal int CharacterSet\r
+               {\r
+                       get\r
+                       {\r
+                               if(_systemFile==null)\r
+                                       return 1;\r
+                                       \r
+                               return _systemFile.CharacterSet; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the codepage depending on the read font property\r
+               /// </summary>\r
+               internal int CodePage\r
+               {\r
+                       get\r
+                       {\r
+                               if(_systemFile==null)\r
+                                       return CultureInfo.CurrentCulture.TextInfo.ANSICodePage;\r
+                                       \r
+                               return _systemFile.CodePage; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the assiciated culture info\r
+               /// </summary>\r
+               internal CultureInfo Culture\r
+               {\r
+                       get \r
+                       { \r
+                               if(_systemFile==null)\r
+                                       return CultureInfo.CurrentCulture;\r
+                                       \r
+                               return _systemFile.Culture; \r
+                       }\r
+               }\r
+\r
+               #endregion \r
+\r
+               #region Properties from the #IDXHDR file\r
+               /// <summary>\r
+               /// Gets the number of topic nodes including the contents and index files\r
+               /// </summary>\r
+               internal int NumberOfTopicNodes\r
+               {\r
+                       get \r
+                       {\r
+                               if(_idxhdrFile==null)\r
+                                       return 0;\r
+\r
+                               return _idxhdrFile.NumberOfTopicNodes; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the ImageList string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+               internal string ImageList\r
+               {\r
+                       get \r
+                       {\r
+                               if( (_stringsFile == null) || (_idxhdrFile == null) )\r
+                                       return "";\r
+\r
+                               return _stringsFile[ _idxhdrFile.ImageListOffset ];\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// True if the value of the ImageType param of the \r
+               /// "text/site properties" object of the sitemap contents is "Folder". \r
+               /// </summary>\r
+               /// <remarks>If this is set to true, the help will display folders instead of books</remarks>\r
+               internal bool ImageTypeFolder\r
+               {\r
+                       get \r
+                       { \r
+                               if(_idxhdrFile==null)\r
+                                       return false;\r
+\r
+                               return _idxhdrFile.ImageTypeFolder; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the background setting \r
+               /// </summary>\r
+               internal int Background\r
+               {\r
+                       get \r
+                       { \r
+                               if(_idxhdrFile==null)\r
+                                       return 0;\r
+\r
+                               return _idxhdrFile.Background; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the foreground setting \r
+               /// </summary>\r
+               internal int Foreground\r
+               {\r
+                       get \r
+                       { \r
+                               if(_idxhdrFile==null)\r
+                                       return 0;\r
+\r
+                               return _idxhdrFile.Foreground; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the Font string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+               internal string FontName\r
+               {\r
+                       get \r
+                       {\r
+                               if( (_stringsFile == null) || (_idxhdrFile == null) )\r
+                                       return "";\r
+\r
+                               return _stringsFile[ _idxhdrFile.FontOffset ];\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the FrameName string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+               internal string FrameName\r
+               {\r
+                       get \r
+                       {\r
+                               if( (_stringsFile == null) || (_idxhdrFile == null) )\r
+                                       return "";\r
+\r
+                               return _stringsFile[ _idxhdrFile.FrameNameOffset ];\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the WindowName string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+               internal string WindowName\r
+               {\r
+                       get \r
+                       {\r
+                               if( (_stringsFile == null) || (_idxhdrFile == null) )\r
+                                       return "";\r
+\r
+                               return _stringsFile[ _idxhdrFile.WindowNameOffset ];\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a string array containing the merged file names\r
+               /// </summary>\r
+               internal string[] MergedFiles\r
+               {\r
+                       get\r
+                       {\r
+                               if( (_stringsFile == null) || (_idxhdrFile == null) )\r
+                                       return new string[0];\r
+\r
+                               string[] saRet = new string[ _idxhdrFile.MergedFileOffsets.Count ];\r
+\r
+                               for(int i=0; i < _idxhdrFile.MergedFileOffsets.Count; i++)\r
+                               {\r
+                                       saRet[i] = _stringsFile[ (int)_idxhdrFile.MergedFileOffsets[i] ];\r
+                               }\r
+\r
+                               return saRet;\r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Gets the file info associated with this instance\r
+               /// </summary>\r
+               public ChmFileInfo FileInfo\r
+               {\r
+                       get { return _chmFileInfo; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal toc read from the text-based hhc file\r
+               /// </summary>\r
+               public ArrayList TOC\r
+               {\r
+                       get { return _toc; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal index read from the chm file.\r
+               /// </summary>\r
+               public ArrayList IndexKLinks\r
+               {\r
+                       get { return _indexKLinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal index read from the chm file.\r
+               /// </summary>\r
+               public ArrayList IndexALinks\r
+               {\r
+                       get { return _indexALinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the full-text search engine for this file\r
+               /// </summary>\r
+               internal FullTextEngine FullTextSearchEngine\r
+               {\r
+                       get { return _ftSearcher; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the full pathname of the file\r
+               /// </summary>\r
+               public string ChmFilePath\r
+               {\r
+                       get { return _chmFileName; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the full pathname of the chi-file\r
+               /// The file name is zero-length if there is no chi-file\r
+               /// </summary>\r
+               public string ChiFilePath\r
+               {\r
+                       get { return _chiFileName; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the full pathname of the chw-file\r
+               /// The file name is zero-length if there is no chw-file\r
+               /// </summary>\r
+               public string ChwFilePath\r
+               {\r
+                       get { return _chwFileName; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the full pathname of the chq-file\r
+               /// The file name is zero-length if there is no chq-file\r
+               /// </summary>\r
+               public string ChqFilePath\r
+               {\r
+                       get { return _chqFileName; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Forms an URL for the web browser\r
+               /// </summary>\r
+               /// <param name="local">local resource</param>\r
+               /// <returns>a url for the web-browser</returns>\r
+               internal string FormURL(string local)\r
+               {\r
+                       if( (local.ToLower().IndexOf("http://") >= 0) ||\r
+                               (local.ToLower().IndexOf("https://") >= 0) ||\r
+                               (local.ToLower().IndexOf("mailto:") >= 0) ||\r
+                               (local.ToLower().IndexOf("ftp://") >= 0)  ||\r
+                               (local.ToLower().IndexOf("ms-its:") >= 0))                              \r
+                               return local;\r
+\r
+                       return HtmlHelpSystem.UrlPrefix + _chmFileName + "::/" + local;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _toc.Clear();\r
+                                       _indexKLinks.Clear();\r
+                                       _indexALinks.Clear();\r
+\r
+                                       if(_systemFile!=null)\r
+                                               _systemFile.Dispose();\r
+\r
+                                       if(_idxhdrFile != null)\r
+                                               _idxhdrFile.Dispose();\r
+\r
+                                       if(_stringsFile != null)\r
+                                               _stringsFile.Dispose();\r
+\r
+                                       if(_urlstrFile != null)\r
+                                               _urlstrFile.Dispose();\r
+\r
+                                       if(_urltblFile != null)\r
+                                               _urltblFile.Dispose();\r
+\r
+                                       if(_topicsFile != null)\r
+                                               _topicsFile.Dispose();\r
+\r
+                                       if(_tocidxFile != null)\r
+                                               _tocidxFile.Dispose();\r
+\r
+                                       if(_kLinks != null)\r
+                                               _kLinks.Dispose();\r
+\r
+                                       if(_aLinks != null)\r
+                                               _aLinks.Dispose();\r
+\r
+                                       if(_ftSearcher != null)\r
+                                               _ftSearcher.Dispose();\r
+\r
+                                       if(_chmFileInfo != null)\r
+                                               _chmFileInfo = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs
new file mode 100644 (file)
index 0000000..7ac64ac
--- /dev/null
@@ -0,0 +1,286 @@
+using System;\r
+using System.Collections;\r
+using System.IO;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMIdxhdr</c> implements t properties which have been read from the #IDXHDR file.\r
+       /// </summary>\r
+       internal sealed class CHMIdxhdr : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing the number of topic nodes including the contents and index files\r
+               /// </summary>\r
+               private int _numberOfTopicNodes = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset in the #STRINGS file of the ImageList param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               private int _imageListOffset = 0;\r
+               /// <summary>\r
+               /// True if the value of the ImageType param of the "text/site properties" object of the sitemap contents is "Folder". \r
+               /// </summary>\r
+               private bool _imageTypeFolder = false;\r
+               /// <summary>\r
+               /// Internal member storing the background value\r
+               /// </summary>\r
+               private int _background = 0;\r
+               /// <summary>\r
+               /// Internal member storing the foreground value\r
+               /// </summary>\r
+               private int _foreground = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset in the #STRINGS file of the Font param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               private int _fontOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset in the #STRINGS file of the FrameName param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               private int _frameNameOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset in the #STRINGS file of the WindowName param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               private int _windowNameOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the number of merged files\r
+               /// </summary>\r
+               private int _numberOfMergedFiles = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset in the #STRINGS file of the merged file names\r
+               /// </summary>\r
+               private ArrayList _mergedFileOffsets = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #IDXHDR file</param>\r
+               /// <param name="associatedFile">associated CHMFile instance</param>\r
+               public CHMIdxhdr(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       int nTemp = 0;\r
+\r
+                       // 4 character T#SM\r
+                       binReader.ReadBytes(4);\r
+                       // unknown timestamp DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // unknown 1\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // number of topic nodes including the contents & index files\r
+                       _numberOfTopicNodes = binReader.ReadInt32();\r
+                       \r
+                       // unknown DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // offset in the strings file\r
+                       _imageListOffset = binReader.ReadInt32();\r
+                       if( _imageListOffset == 0)\r
+                               _imageListOffset = -1; // 0/-1 = none\r
+\r
+                       // unknown DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // 1 if the value of the ImageType param of the "text/site properties" object of the sitemap contents is "Folder". \r
+                       nTemp = binReader.ReadInt32();\r
+                       _imageTypeFolder = (nTemp == 1);\r
+\r
+                       // offset in the strings file\r
+                       _background = binReader.ReadInt32();\r
+                       // offset in the strings file\r
+                       _foreground = binReader.ReadInt32();\r
+\r
+                       // offset in the strings file\r
+                       _fontOffset = binReader.ReadInt32();\r
+\r
+                       // window styles DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+                       // window styles DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // unknown DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // offset in the strings file\r
+                       _frameNameOffset = binReader.ReadInt32();\r
+                       if( _frameNameOffset == 0)\r
+                               _frameNameOffset = -1; // 0/-1 = none\r
+                       // offset in the strings file\r
+                       _windowNameOffset = binReader.ReadInt32();\r
+                       if( _windowNameOffset == 0)\r
+                               _windowNameOffset = -1; // 0/-1 = none\r
+\r
+                       // informations types DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // unknown DWORD\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       // number of merged files in the merged file list DWORD\r
+                       _numberOfMergedFiles = binReader.ReadInt32();\r
+\r
+                       nTemp = binReader.ReadInt32();\r
+\r
+                       for(int i = 0; i < _numberOfMergedFiles; i++)\r
+                       {\r
+                               // DWORD offset value of merged file\r
+                               nTemp = binReader.ReadInt32();\r
+                               \r
+                               if(nTemp > 0)\r
+                                       _mergedFileOffsets.Add(nTemp);\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the number of topic nodes including the contents and index files\r
+               /// </summary>\r
+               public int NumberOfTopicNodes\r
+               {\r
+                       get { return _numberOfTopicNodes; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the offset in the #STRINGS file of the ImageList \r
+               /// param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               public int ImageListOffset\r
+               {\r
+                       get { return _imageListOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// True if the value of the ImageType param of the \r
+               /// "text/site properties" object of the sitemap contents is "Folder". \r
+               /// </summary>\r
+               /// <remarks>If this is set to true, the help will display folders instead of books</remarks>\r
+               public bool ImageTypeFolder\r
+               {\r
+                       get { return _imageTypeFolder; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the background setting \r
+               /// </summary>\r
+               public int Background\r
+               {\r
+                       get { return _background; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the foreground setting \r
+               /// </summary>\r
+               public int Foreground\r
+               {\r
+                       get { return _foreground; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the offset in the #STRINGS file of the Font \r
+               /// param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               public int WindowNameOffset\r
+               {\r
+                       get { return _fontOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the offset in the #STRINGS file of the FrameName \r
+               /// param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               public int FrameNameOffset\r
+               {\r
+                       get { return _frameNameOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the offset in the #STRINGS file of the WindowName \r
+               /// param of the "text/site properties" object of the sitemap contents\r
+               /// </summary>\r
+               public int FontOffset\r
+               {\r
+                       get { return _windowNameOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an array list of offset numbers in the #STRINGS file of the \r
+               /// merged file names.\r
+               /// </summary>\r
+               public ArrayList MergedFileOffsets\r
+               {\r
+                       get { return _mergedFileOffsets; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                                       _mergedFileOffsets = null;\r
+                               }\r
+             \r
+                                         \r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs
new file mode 100644 (file)
index 0000000..5942d4e
--- /dev/null
@@ -0,0 +1,256 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Collections.Specialized;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMStrings</c> implements a string collection read from the #STRINGS file\r
+       /// </summary>\r
+       internal sealed class CHMStrings : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Constant specifying the size of the string blocks\r
+               /// </summary>\r
+               private const int STRING_BLOCK_SIZE = 4096;\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing the string dictionary\r
+               /// </summary>\r
+               private Hashtable _stringDictionary = new Hashtable();\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #STRINGS file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMStrings(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+               }\r
+\r
+               \r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal CHMStrings()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _stringDictionary.Count );\r
+\r
+                       if (_stringDictionary.Count != 0)\r
+                       {\r
+                               IDictionaryEnumerator iDictionaryEnumerator = _stringDictionary.GetEnumerator();\r
+                               while (iDictionaryEnumerator.MoveNext())\r
+                               {\r
+                                       DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;\r
+                                       writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );\r
+                                       writer.Write( dictionaryEntry.Value.ToString() );\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(int i=0; i<nCnt;i++)\r
+                       {\r
+                               int nKey = reader.ReadInt32();\r
+                               string sValue = reader.ReadString();\r
+                               \r
+                               _stringDictionary[nKey.ToString()] = sValue;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+       \r
+                       //binReader.ReadByte(); // file starts with a NULL character for 0-based offset indexing\r
+                       \r
+                       int nStringOffset = 0;\r
+                       int nSubsetOffset = 0;\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               nStringOffset = (int)memStream.Position;\r
+                               byte [] stringBlock = binReader.ReadBytes(STRING_BLOCK_SIZE);\r
+                               bRet &= DecodeBlock(stringBlock, ref nStringOffset, ref nSubsetOffset);\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes a string block\r
+               /// </summary>\r
+               /// <param name="stringBlock">byte array which represents the string block</param>\r
+               /// <param name="nStringOffset">current string offset number</param>\r
+               /// <param name="nSubsetOffset">reference to a subset variable</param>\r
+               /// <returns>true if succeeded</returns>\r
+               /// <remarks>If a string crosses the end of a block then it will be cut off \r
+               /// without a NT and repeated in full, with a NT, at the start of the next block. \r
+               /// For eg "To customize the appearance of a contents file" might become \r
+               /// "To customize the (block ending)To customize the appearance of a contents file" \r
+               /// when there are 17 bytes left at the end of the block. </remarks>\r
+               private bool DecodeBlock( byte[] stringBlock, ref int nStringOffset, ref int nSubsetOffset)\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(stringBlock);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               bool bFoundTerminator = false;\r
+\r
+                               int nCurOffset = nStringOffset + (int)memStream.Position;\r
+\r
+                               string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);\r
+\r
+                               if(nSubsetOffset != 0)\r
+                               {\r
+                                       _stringDictionary[nSubsetOffset.ToString()] = sTemp.ToString();\r
+                               } \r
+                               else \r
+                               {\r
+                                       _stringDictionary[nCurOffset.ToString()] = sTemp.ToString();\r
+                               }\r
+\r
+                               if( bFoundTerminator )\r
+                               {\r
+                                       nSubsetOffset = 0;\r
+                               } \r
+                               else \r
+                               {\r
+                                       nSubsetOffset = nCurOffset;\r
+                               }\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Indexer which returns the string at a given offset\r
+               /// </summary>\r
+               public string this[int offset]\r
+               {\r
+                       get\r
+                       {\r
+                               if(offset == -1)\r
+                                       return String.Empty;\r
+\r
+                               string sTemp = (string)_stringDictionary[ offset.ToString() ];\r
+\r
+                               if(sTemp == null)\r
+                                       return String.Empty;\r
+\r
+                               return sTemp;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Indexer which returns the string at a given offset\r
+               /// </summary>\r
+               public string this[string offset]\r
+               {\r
+                       get\r
+                       {\r
+                               if(offset == "-1")\r
+                                       return String.Empty;\r
+\r
+                               string sTemp = (string)_stringDictionary[ offset ];\r
+\r
+                               if(sTemp == null)\r
+                                       return String.Empty;\r
+\r
+                               return sTemp;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                                       _stringDictionary = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs
new file mode 100644 (file)
index 0000000..042e243
--- /dev/null
@@ -0,0 +1,821 @@
+using System;\r
+using System.Collections;\r
+using System.Diagnostics;\r
+using System.IO;\r
+using System.Text;\r
+using System.Globalization;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMSystem</c> reads the #SYSTEM file of the chm and stores its settings\r
+       /// </summary>\r
+       internal sealed class CHMSystem : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing the file version \r
+               /// </summary>\r
+               private int _fileVersion = 0;\r
+               /// <summary>\r
+               /// Internal member storing the contents file path\r
+               /// </summary>\r
+               private string _contentsFile = "";\r
+               /// <summary>\r
+               /// Internal member storing the index file path\r
+               /// </summary>\r
+               private string _indexFile = "";\r
+               /// <summary>\r
+               /// Internal member storing the default help topic\r
+               /// </summary>\r
+               private string _defaultTopic = "";\r
+               /// <summary>\r
+               /// Internal member storing the help-window title\r
+               /// </summary>\r
+               private string _title = "";\r
+               /// <summary>\r
+               /// Internal flag if dbcs is on\r
+               /// </summary>\r
+               private bool _dbcs = false;\r
+               /// <summary>\r
+               /// Internal flag if fulltext search is enabled\r
+               /// </summary>\r
+               private bool _fullTextSearch = false;\r
+               /// <summary>\r
+               /// Internal flag if KLinks are in the file\r
+               /// </summary>\r
+               private bool _hasKLinks = false;\r
+               /// <summary>\r
+               /// Internal flag if ALinks are in the file\r
+               /// </summary>\r
+               private bool _hasALinks = false;\r
+               /// <summary>\r
+               /// Internal member storing the name of the default window\r
+               /// </summary>\r
+               private string _defaultWindow = "";\r
+               /// <summary>\r
+               /// Internal member storing the filename of the compiled file\r
+               /// </summary>\r
+               private string _compileFile = "";\r
+               /// <summary>\r
+               /// Internal flag storing the offset value of the binary index\r
+               /// </summary>\r
+               private uint _binaryIndexURLTableID = 0;\r
+               /// <summary>\r
+               /// Inernal member storing the compiler version this file was compiled\r
+               /// </summary>\r
+               private string _compilerVersion = "";\r
+               /// <summary>\r
+               /// Internal flag storing the offset value of the binary TOC\r
+               /// </summary>\r
+               private uint _binaryTOCURLTableID = 0;\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the default fontface, size, charset\r
+               /// </summary>\r
+               private string _defaultFont = "";\r
+               /// <summary>\r
+               /// Internal member storing the culture info of the file\r
+               /// </summary>\r
+               private CultureInfo _culture;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #SYSTEM file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMSystem(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+\r
+                       if(_culture == null)\r
+                       {\r
+                               // Set the text encoder of the chm file to the read charset/codepage\r
+                               _associatedFile.TextEncoding = Encoding.GetEncoding( this.CodePage );\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       // First entry = DWORD for version number\r
+                       _fileVersion = (int) binReader.ReadInt32();\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               bRet &= DecodeEntry(ref binReader);\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes an #system file entry\r
+               /// </summary>\r
+               /// <param name="binReader">binary reader reference</param>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeEntry(ref BinaryReader binReader)\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       int code = (int) binReader.ReadInt16(); // entry code, WORD\r
+                       int length = (int) binReader.ReadInt16(); // length of entry\r
+\r
+                       switch(code)\r
+                       {\r
+                               case 0:\r
+                               {\r
+                                       _contentsFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 1:\r
+                               {\r
+                                       _indexFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 2:\r
+                               {\r
+                                       _defaultTopic = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 3:\r
+                               {\r
+                                       _title = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 4:\r
+                               {\r
+                                       int nTemp = 0;\r
+                                       nTemp = binReader.ReadInt32(); // read DWORD LCID\r
+                                       _culture = new CultureInfo(nTemp);\r
+\r
+                                       if(_culture != null)\r
+                                               _associatedFile.TextEncoding = Encoding.GetEncoding(_culture.TextInfo.ANSICodePage);\r
+\r
+                                       nTemp = binReader.ReadInt32(); // read DWORD DBCS\r
+                                       _dbcs = (nTemp == 1);\r
+\r
+                                       nTemp = binReader.ReadInt32(); // read DWORD Fulltext search\r
+                                       _fullTextSearch = (nTemp == 1);\r
+\r
+                                       nTemp = binReader.ReadInt32(); // read DWORD has klinks\r
+                                       _hasKLinks = (nTemp != 0);\r
+\r
+                                       nTemp = binReader.ReadInt32(); // read DWORD has alinks\r
+                                       _hasALinks = (nTemp != 0);\r
+                                       \r
+                                       // read the rest of code 4 (not important for us)\r
+                                       byte[] temp = new byte[length-(5*4)];\r
+                                       temp = binReader.ReadBytes(length-(5*4));\r
+                               };break;\r
+                               case 5:\r
+                               {\r
+                                       _defaultWindow = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 6:\r
+                               {\r
+                                       _compileFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 7:\r
+                               {\r
+                                       if(_fileVersion > 2)\r
+                                       {\r
+                                               _binaryIndexURLTableID = (uint) binReader.ReadInt32();\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               byte[] read = binReader.ReadBytes(length);\r
+                                               int i=read.Length;\r
+                                       }\r
+                               };break;\r
+                               case 8:\r
+                               {\r
+                                       // abbreviation (not interresting for us)\r
+                                       byte[] read = binReader.ReadBytes(length);\r
+                                       int i=read.Length;\r
+                               };break;\r
+                               case 9:\r
+                               {\r
+                                       _compilerVersion = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+                               };break;\r
+                               case 10:\r
+                               {\r
+                                       // timestamp of the file (not interresting for us)\r
+                                       byte[] read = binReader.ReadBytes(length);\r
+                                       int i=read.Length;\r
+                               };break;\r
+                               case 11:\r
+                               {\r
+                                       if(_fileVersion > 2)\r
+                                       {\r
+                                               _binaryTOCURLTableID = (uint) binReader.ReadInt32();\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               byte[] read = binReader.ReadBytes(length);\r
+                                               int i=read.Length;\r
+                                       }\r
+                               };break;\r
+                               case 12:\r
+                               {\r
+                                       // number of information bytes\r
+                                       byte[] read = binReader.ReadBytes(length);\r
+                                       int i=read.Length;\r
+                               };break;\r
+                               case 13:\r
+                               {\r
+                                       // copy of file #idxhdr\r
+                                       byte[] read = binReader.ReadBytes(length);\r
+                                       int i=read.Length;\r
+                               };break;\r
+                               case 14:\r
+                               {\r
+                                       // custom tabs for HH viewer\r
+                                       byte[] read = binReader.ReadBytes(length);\r
+                                       int i=read.Length;\r
+                               };break;\r
+                               case 15:\r
+                               {\r
+                                       // a checksum\r
+                                       byte[] read = binReader.ReadBytes(length);\r
+                                       int i=read.Length;\r
+                               };break;\r
+                               case 16:\r
+                               {\r
+                                       // Default Font=string,number,number  \r
+                                       // The string is the name of the font, the first number is the \r
+                                       // point size & the last number is the character set used by the font. \r
+                                       // For acceptable values see *_CHARSET defines in wingdi.h from the \r
+                                       // Windows SDK or the same file in MinGW or Wine. \r
+                                       // Most of the time you will only want to use 0, which is the value for ANSI, \r
+                                       // which is the subset of ASCII used by Windows. \r
+                                       _defaultFont = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);\r
+\r
+                               };break;\r
+                               default:\r
+                               {\r
+                                       byte[] temp = new byte[length];\r
+                                       temp = binReader.ReadBytes(length);\r
+                                       //bRet = false;\r
+                                       int i=temp.Length;\r
+                               };break;\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads all HHC files and checks which one has the global object tag.\r
+               /// This hhc file will be returned as master hhc\r
+               /// </summary>\r
+               /// <param name="hhcTopics">list of topics containing the extension hhc</param>\r
+               /// <param name="TopicItemArrayList">true if the arraylist contains topic items</param>\r
+               /// <returns>the filename of the found master toc</returns>\r
+               private string GetMasterHHC(ArrayList hhcTopics, bool TopicItemArrayList)\r
+               {\r
+                       string sRet = "";\r
+\r
+                       if( (hhcTopics!=null) && (hhcTopics.Count > 0) )\r
+                       {\r
+                               if( TopicItemArrayList )\r
+                               {\r
+                                       if(hhcTopics.Count == 1)\r
+                                       {\r
+                                               sRet = ((TopicEntry)hhcTopics[0]).Locale;\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               foreach(TopicEntry curEntry in hhcTopics)\r
+                                               {\r
+                                                       CHMStream.CHMStream iw=null;\r
+                                                       MemoryStream fileObject=null;                   \r
+\r
+                                                       if( _associatedFile.CurrentStorageWrapper == null)\r
+                                                       {\r
+                                                               iw=new CHMStream.CHMStream();                   \r
+                                                               iw.OpenCHM(_associatedFile.ChmFilePath);                        \r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               iw = _associatedFile.CurrentStorageWrapper;\r
+                                                       }\r
+\r
+                                                       fileObject = iw.OpenStream(curEntry.Locale);\r
+                                                       if( fileObject != null)\r
+                                                       {\r
+                                                               string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);\r
+                                                               fileObject.Close();\r
+\r
+                                                               if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )\r
+                                                               {\r
+                                                                       sRet = curEntry.Locale;\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       if(hhcTopics.Count == 1)\r
+                                       {\r
+                                               sRet = ((string)hhcTopics[0]);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               foreach(string curEntry in hhcTopics)\r
+                                               {\r
+                                                       CHMStream.CHMStream iw=null;\r
+                                                       MemoryStream fileObject=null;                   \r
+\r
+                                                       if( _associatedFile.CurrentStorageWrapper == null)\r
+                                                       {                                                               \r
+                                                               iw=new CHMStream.CHMStream();                   \r
+                                                               iw.OpenCHM(_associatedFile.ChmFilePath);                        \r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               iw = _associatedFile.CurrentStorageWrapper;\r
+                                                       }\r
+\r
+                                                       fileObject = iw.OpenStream(curEntry);\r
+                                                       if( fileObject != null)\r
+                                                       {\r
+                                                               string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);                                                               \r
+                                                               fileObject.Close();\r
+\r
+                                                               if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )\r
+                                                               {\r
+                                                                       sRet = curEntry;\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       return sRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the file version of the chm file. \r
+               /// 2 for Compatibility=1.0,  3 for Compatibility=1.1\r
+               /// </summary>\r
+               public int FileVersion\r
+               {\r
+                       get { return _fileVersion; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the contents file name\r
+               /// </summary>\r
+               public string ContentsFile\r
+               {\r
+                       get \r
+                       { \r
+                               if( BinaryTOC ) // if the file contains a binary TOC\r
+                               {\r
+                                       // make sure the CHMFile instance exists and has loaded the file #URLTBL\r
+                                       if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )\r
+                                       {\r
+                                               // Get an url-table entry by its unique id\r
+                                               UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryTOCURLTableID );\r
+                                               if(entry != null)\r
+                                               {\r
+                                                       // entry found, return the url ( = filename )\r
+                                                       return entry.URL;\r
+                                               }\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       if(_contentsFile.Length <= 0)\r
+                                       {\r
+                                               string sCheck = "Table of Contents.hhc"; // default HHP contents filename\r
+\r
+                                               if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )\r
+                                               {\r
+                                                       TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );\r
+\r
+                                                       if( te == null)\r
+                                                       {\r
+                                                               sCheck = "toc.hhc"; // default HHP contents filename\r
+\r
+                                                               te = _associatedFile.TopicsFile.GetByLocale( sCheck );\r
+\r
+                                                               if( te == null)\r
+                                                               {\r
+                                                                       sCheck = CompileFile + ".hhc";\r
+\r
+                                                                       te = _associatedFile.TopicsFile.GetByLocale( sCheck );\r
+\r
+                                                                       if( te == null)\r
+                                                                       {\r
+                                                                               ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhc");\r
+\r
+                                                                               if( arrExt == null )\r
+                                                                               {\r
+                                                                                       arrExt = _associatedFile.EnumFilesByExtension("hhc");\r
+\r
+                                                                                       if( arrExt == null )\r
+                                                                                       {\r
+                                                                                               Debug.WriteLine("CHMSystem.ContentsFile - Failed, contents file not found !");\r
+                                                                                       } \r
+                                                                                       else \r
+                                                                                       {\r
+                                                                                               if(arrExt.Count > 1)\r
+                                                                                               {\r
+                                                                                                       sCheck = GetMasterHHC(arrExt, false);\r
+                                                                                                       _contentsFile = sCheck;\r
+                                                                                               } \r
+                                                                                               else \r
+                                                                                               {\r
+                                                                                                       _contentsFile = ((string)arrExt[0]);\r
+                                                                                                       sCheck = _contentsFile;\r
+                                                                                               }\r
+                                                                                       }\r
+                                                                               } \r
+                                                                               else \r
+                                                                               {\r
+                                                                                       if(arrExt.Count > 1)\r
+                                                                                       {\r
+                                                                                               sCheck = GetMasterHHC(arrExt, true);\r
+                                                                                               _contentsFile = sCheck;\r
+                                                                                       } \r
+                                                                                       else \r
+                                                                                       {\r
+                                                                                               _contentsFile = ((TopicEntry)arrExt[0]).Locale;\r
+                                                                                               sCheck = _contentsFile;\r
+                                                                                       }\r
+                                                                               }\r
+                                                                       } \r
+                                                                       else \r
+                                                                       {\r
+                                                                               _contentsFile = sCheck;\r
+                                                                       }\r
+                                                               } \r
+                                                               else \r
+                                                               {\r
+                                                                       _contentsFile = sCheck;\r
+                                                               }\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               _contentsFile = sCheck;\r
+                                                       }\r
+                                               }\r
+\r
+                                               return sCheck;\r
+                                       }\r
+                               }\r
+\r
+                               return _contentsFile; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the index file name\r
+               /// </summary>\r
+               public string IndexFile\r
+               {\r
+                       get \r
+                       { \r
+                               if( BinaryIndex ) // if the file contains a binary index\r
+                               {\r
+                                       // make sure the CHMFile instance exists and has loaded the file #URLTBL\r
+                                       if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )\r
+                                       {\r
+                                               // Get an url-table entry by its unique id\r
+                                               UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryIndexURLTableID );\r
+                                               if(entry != null)\r
+                                               {\r
+                                                       // entry found, return the url ( = filename )\r
+                                                       return entry.URL;\r
+                                               }\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       if(_indexFile.Length <= 0)\r
+                                       {\r
+                                               string sCheck = "Index.hhk"; // default HHP index filename\r
+\r
+                                               if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )\r
+                                               {\r
+                                                       TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );\r
+\r
+                                                       if( te == null)\r
+                                                       {\r
+                                                               sCheck = CompileFile + ".hhk";\r
+\r
+                                                               te = _associatedFile.TopicsFile.GetByLocale( sCheck );\r
+\r
+                                                               if( te == null)\r
+                                                               {\r
+                                                                       ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhk");\r
+\r
+                                                                       if( arrExt == null )\r
+                                                                       {\r
+                                                                               Debug.WriteLine("CHMSystem.IndexFile - Failed, index file not found !");\r
+                                                                       } \r
+                                                                       else \r
+                                                                       {\r
+                                                                               _indexFile = ((TopicEntry)arrExt[0]).Locale;\r
+                                                                               sCheck = _indexFile;\r
+                                                                       }\r
+                                                               } \r
+                                                               else \r
+                                                               {\r
+                                                                       _indexFile = sCheck;\r
+                                                               }\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               _indexFile = sCheck;\r
+                                                       }\r
+                                               }\r
+\r
+                                               return sCheck;\r
+                                       }\r
+                               }\r
+                               return _indexFile; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the default topic of this file\r
+               /// </summary>\r
+               /// <param name="local">new local value of the topic</param>\r
+               internal void SetDefaultTopic(string local)\r
+               {\r
+                       _defaultTopic = local;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default help topic\r
+               /// </summary>\r
+               public string DefaultTopic\r
+               {\r
+                       get { return _defaultTopic; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the title of the help window\r
+               /// </summary>\r
+               public string Title\r
+               {\r
+                       get { return _title; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if DBCS is in use\r
+               /// </summary>\r
+               public bool DBCS\r
+               {\r
+                       get { return _dbcs; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if full-text-search is available\r
+               /// </summary>\r
+               public bool FullTextSearch\r
+               {\r
+                       get { return _fullTextSearch; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the file has ALinks\r
+               /// </summary>\r
+               public bool HasALinks\r
+               {\r
+                       get { return _hasALinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the file has KLinks\r
+               /// </summary>\r
+               public bool HasKLinks\r
+               {\r
+                       get { return _hasKLinks; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default window name\r
+               /// </summary>\r
+               public string DefaultWindow\r
+               {\r
+                       get { return _defaultWindow; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the file name of the compile file\r
+               /// </summary>\r
+               public string CompileFile\r
+               {\r
+                       get { return _compileFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the id of the binary index in the url table\r
+               /// </summary>\r
+               public uint BinaryIndexURLTableID\r
+               {\r
+                       get { return _binaryIndexURLTableID; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary index file\r
+               /// </summary>\r
+               public bool BinaryIndex\r
+               {\r
+                       get { return (_binaryIndexURLTableID>0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary index file\r
+               /// </summary>\r
+               public string CompilerVersion\r
+               {\r
+                       get { return _compilerVersion; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the id of the binary toc in the url table\r
+               /// </summary>\r
+               public uint BinaryTOCURLTableID\r
+               {\r
+                       get { return _binaryTOCURLTableID; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary toc file\r
+               /// </summary>\r
+               public bool BinaryTOC\r
+               {\r
+                       get { return (_binaryTOCURLTableID>0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the font face of the read font property.\r
+               /// Empty string for default font.\r
+               /// </summary>\r
+               public string FontFace\r
+               {\r
+                       get\r
+                       {\r
+                               if( _defaultFont.Length > 0)\r
+                               {\r
+                                       string [] fontSplit = _defaultFont.Split( new char[]{','});\r
+                                       if(fontSplit.Length > 0)\r
+                                               return fontSplit[0].Trim();\r
+                               }\r
+\r
+                               return "";\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the font size of the read font property.\r
+               /// 0 for default font size\r
+               /// </summary>\r
+               public double FontSize\r
+               {\r
+                       get\r
+                       {\r
+                               if( _defaultFont.Length > 0)\r
+                               {\r
+                                       string [] fontSplit = _defaultFont.Split( new char[]{','});\r
+                                       if(fontSplit.Length > 1)\r
+                                               return double.Parse(fontSplit[1].Trim());\r
+                               }\r
+\r
+                               return 0.0;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the character set of the read font property\r
+               /// 1 for default\r
+               /// </summary>\r
+               public int CharacterSet\r
+               {\r
+                       get\r
+                       {\r
+                               if( _defaultFont.Length > 0)\r
+                               {\r
+                                       string [] fontSplit = _defaultFont.Split( new char[]{','});\r
+                                       if(fontSplit.Length > 2)\r
+                                               return Int32.Parse(fontSplit[2].Trim());\r
+                               }\r
+\r
+                               return 0;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the codepage depending on the read font property\r
+               /// </summary>\r
+               public int CodePage\r
+               {\r
+                       get\r
+                       {\r
+                               // if we've read a LCID from the system file\r
+                               // ignore the font-property settings and return\r
+                               // the codepage generated from the culture info\r
+                               if(_culture != null)\r
+                               {\r
+                                       return _culture.TextInfo.ANSICodePage;\r
+                               }\r
+\r
+                               int nRet = 1252; // default codepage windows-1252\r
+\r
+                               int nCSet = CharacterSet;\r
+\r
+                               switch(nCSet)\r
+                               {\r
+                                       case 0x00: nRet = 1252;break;   // ANSI_CHARSET\r
+                                       case 0xCC: nRet = 1251;break;   // RUSSIAN_CHARSET\r
+                                       case 0xEE: nRet = 1250;break;   // EE_CHARSET\r
+                                       case 0xA1: nRet = 1253;break;   // GREEK_CHARSET\r
+                                       case 0xA2: nRet = 1254;break;   // TURKISH_CHARSET\r
+                                       case 0xBA: nRet = 1257;break;   // BALTIC_CHARSET\r
+                                       case 0xB1: nRet = 1255;break;   // HEBREW_CHARSET\r
+                                       case 0xB2: nRet = 1256;break;   // ARABIC_CHARSET\r
+                                       case 0x80: nRet =  932;break;   // SHIFTJIS_CHARSET\r
+                                       case 0x81: nRet =  949;break;   // HANGEUL_CHARSET\r
+                                       case 0x86: nRet =  936;break;   // GB2313_CHARSET\r
+                                       case 0x88: nRet =  950;break;   // CHINESEBIG5_CHARSET\r
+                               }\r
+\r
+                               return nRet;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the assiciated culture info\r
+               /// </summary>\r
+               public CultureInfo Culture\r
+               {\r
+                       get { return _culture; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                               }\r
+             \r
+                                         \r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs
new file mode 100644 (file)
index 0000000..d6ca1aa
--- /dev/null
@@ -0,0 +1,288 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMTocidx</c> implements functions to decode the #TOCIDX internal file.\r
+       /// </summary>\r
+       internal sealed class CHMTocidx : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Constant specifying the size of the data blocks\r
+               /// </summary>\r
+               private const int BLOCK_SIZE = 0x1000;\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal memebr storing the offset to the 20/28 byte structs\r
+               /// </summary>\r
+               private int _offset2028 = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset to the 16 byte structs\r
+               /// </summary>\r
+               private int _offset16structs = 0;\r
+               /// <summary>\r
+               /// Internal member storing the number of 16 byte structs\r
+               /// </summary>\r
+               private int _numberOf16structs = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset to the topic list\r
+               /// </summary>\r
+               private int _offsetOftopics = 0;\r
+               /// <summary>\r
+               /// Internal member storing the toc\r
+               /// </summary>\r
+               private ArrayList _toc = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member for offset seeking\r
+               /// </summary>\r
+               private Hashtable _offsetTable = new Hashtable();\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #TOCIDX file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMTocidx(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+\r
+                       // clear internal binary data after extraction\r
+                       _binaryFileData = null;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       _toc = new ArrayList();\r
+                       _offsetTable = new Hashtable();\r
+\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+                       \r
+                       int nCurOffset = 0;\r
+\r
+                       _offset2028 = binReader.ReadInt32();\r
+                       _offset16structs = binReader.ReadInt32();\r
+                       _numberOf16structs = binReader.ReadInt32();\r
+                       _offsetOftopics = binReader.ReadInt32();\r
+\r
+                       binReader.BaseStream.Seek( _offset2028, SeekOrigin.Begin );\r
+\r
+                       if( RecursivelyBuildTree(ref binReader, _offset2028, _toc, null) )\r
+                       {\r
+                               binReader.BaseStream.Seek( _offset16structs, SeekOrigin.Begin );\r
+                               nCurOffset = (int)binReader.BaseStream.Position;\r
+\r
+                               for(int i=0; i < _numberOf16structs; i++)\r
+                               {\r
+                                       int tocOffset = binReader.ReadInt32();\r
+                                       int sqNr = binReader.ReadInt32();\r
+                                       int topOffset = binReader.ReadInt32();\r
+                                       int hhctopicIdx = binReader.ReadInt32();\r
+\r
+                                       nCurOffset = (int)binReader.BaseStream.Position;\r
+\r
+                                       int topicIdx = -1;\r
+                                       // if the topic offset is within the range of the stream\r
+                                       // and is >= the offset of the first topic dword\r
+                                       if((topOffset < (binReader.BaseStream.Length - 4)) && (topOffset >= _offsetOftopics))\r
+                                       {\r
+                                               // read the index of the topic for this item\r
+                                               binReader.BaseStream.Seek( topOffset, SeekOrigin.Begin);\r
+                                               topicIdx = binReader.ReadInt32();\r
+                                               binReader.BaseStream.Seek( nCurOffset, SeekOrigin.Begin);\r
+                                       \r
+\r
+                                               TOCItem item = (TOCItem)_offsetTable[tocOffset.ToString()];\r
+                                               if( item != null)\r
+                                               {\r
+                                                       if(( topicIdx < _associatedFile.TopicsFile.TopicTable.Count)&&(topicIdx>=0))\r
+                                                       {\r
+                                                               TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile.TopicTable[topicIdx]);\r
+                                                               if( (te != null) && (item.TopicOffset < 0) )\r
+                                                               {\r
+                                                                       item.TopicOffset = te.EntryOffset;\r
+                                                               }\r
+                                       \r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Recursively reads the binary toc tree from the file\r
+               /// </summary>\r
+               /// <param name="binReader">reference to binary reader</param>\r
+               /// <param name="NodeOffset">offset of the first node in the current level</param>\r
+               /// <param name="level">arraylist of TOCItems for the current level</param>\r
+               /// <param name="parentItem">parent item for the item</param>\r
+               /// <returns>Returns true if succeeded</returns>\r
+               private bool RecursivelyBuildTree(ref BinaryReader binReader, int NodeOffset, ArrayList level, TOCItem parentItem)\r
+               {\r
+                       bool bRet = true;\r
+                       int nextOffset=0;\r
+\r
+                       int nReadOffset = (int)binReader.BaseStream.Position;\r
+\r
+                       binReader.BaseStream.Seek(NodeOffset, SeekOrigin.Begin);\r
+\r
+                       do\r
+                       {\r
+                               int nCurOffset = (int)binReader.BaseStream.Position;\r
+\r
+                               int unkn1 = binReader.ReadInt16(); // unknown\r
+                               int unkn2 = binReader.ReadInt16(); // unknown\r
+\r
+                               int flag = binReader.ReadInt32();\r
+\r
+                               int nFolderAdd = 0;\r
+\r
+                               if((_associatedFile != null) && (_associatedFile.ImageTypeFolder))\r
+                               {\r
+                                       // get the value which should be added, to display folders instead of books\r
+                                       if(HtmlHelpSystem.UseHH2TreePics) \r
+                                               nFolderAdd = 8;\r
+                                       else\r
+                                               nFolderAdd = 4;\r
+                               }\r
+\r
+                               int nFolderImgIdx = (HtmlHelpSystem.UseHH2TreePics ? (TOCItem.STD_FOLDER_HH2+nFolderAdd) : (TOCItem.STD_FOLDER_HH1+nFolderAdd));\r
+                               int nFileImgIdx = (HtmlHelpSystem.UseHH2TreePics ? TOCItem.STD_FILE_HH2 : TOCItem.STD_FILE_HH1);\r
+\r
+                               int stdImage =  ((flag & 0x4)!=0) ? nFolderImgIdx : nFileImgIdx;\r
+\r
+                               int stringOffset = binReader.ReadInt32();\r
+\r
+                               int ParentOffset = binReader.ReadInt32();\r
+                               nextOffset = binReader.ReadInt32();\r
+\r
+                               int firstChildOffset = 0;\r
+                               int unkn3=0;\r
+\r
+                               if( (flag&0x4)!=0 )\r
+                               {\r
+                                       firstChildOffset = binReader.ReadInt32();\r
+                                       unkn3 = binReader.ReadInt32(); // unknown\r
+                               }\r
+\r
+                               TOCItem newItem = new TOCItem();\r
+                               newItem.ImageIndex = stdImage;\r
+                               newItem.Offset = nCurOffset;\r
+                               newItem.OffsetNext = nextOffset;\r
+                               newItem.AssociatedFile = _associatedFile;\r
+                               newItem.TocMode = DataMode.Binary;\r
+                               newItem.Parent = parentItem;\r
+\r
+                               if( (flag&0x08) == 0)\r
+                               {\r
+                                       // toc item doesn't have a local value (=> stringOffset = offset of strings file)\r
+                                       newItem.Name = _associatedFile.StringsFile[stringOffset];\r
+                               } \r
+                               else \r
+                               {\r
+                                       // this item has a topic entry (=> stringOffset = index of topic entry)\r
+                                       if((stringOffset < _associatedFile.TopicsFile.TopicTable.Count) && (stringOffset >= 0))\r
+                                       {\r
+                                               TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile.TopicTable[stringOffset]);\r
+                                               if(te != null)\r
+                                               {\r
+                                                       newItem.TopicOffset = te.EntryOffset;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               _offsetTable[nCurOffset.ToString()] = newItem;\r
+\r
+                               // if this item has children (firstChildOffset > 0)\r
+                               if( firstChildOffset > 0)\r
+                               {\r
+                                       bRet &= RecursivelyBuildTree(ref binReader, firstChildOffset, newItem.Children, newItem);\r
+                               }\r
+\r
+                               level.Add( newItem );\r
+\r
+                               if(nCurOffset != nextOffset)\r
+                                       binReader.BaseStream.Seek(nextOffset, SeekOrigin.Begin);\r
+\r
+                       }while(nextOffset != 0);\r
+\r
+                       binReader.BaseStream.Seek(nReadOffset, SeekOrigin.Begin);\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal read toc\r
+               /// </summary>\r
+               internal ArrayList TOC\r
+               {\r
+                       get { return _toc; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                                       _toc = null;\r
+                                       _offsetTable = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs
new file mode 100644 (file)
index 0000000..ce1451b
--- /dev/null
@@ -0,0 +1,235 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Collections.Specialized;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMTopics</c> implements functionality to decode the #TOPICS internal file\r
+       /// </summary>\r
+       internal sealed class CHMTopics : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+               /// <summary>\r
+               /// Internal member storing the topic list\r
+               /// </summary>\r
+               private ArrayList _topicTable = new ArrayList();\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #TOPICS file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMTopics(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+\r
+                       // clear internal binary data after extraction\r
+                       _binaryFileData = null;\r
+               }\r
+\r
+               \r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal CHMTopics()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _topicTable.Count );\r
+                       foreach(TopicEntry curItem in _topicTable)\r
+                       {\r
+                               curItem.Dump(ref writer);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       int i=0;\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt;i++)\r
+                       {\r
+                               TopicEntry newItem = new TopicEntry();\r
+                               newItem.SetCHMFile(_associatedFile);\r
+                               newItem.ReadDump(ref reader);\r
+                               _topicTable.Add(newItem);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+\r
+                       foreach(TopicEntry curEntry in _topicTable)\r
+                       {\r
+                               curEntry.SetCHMFile(associatedFile);\r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+                       \r
+                       int nCurOffset = 0;\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               int entryOffset = nCurOffset;\r
+                               int tocIdx = binReader.ReadInt32();\r
+                               int titleOffset = binReader.ReadInt32();\r
+                               int urltablOffset = binReader.ReadInt32();\r
+                               int visibilityMode = binReader.ReadInt16();\r
+                               int unknownMode = binReader.ReadInt16();\r
+\r
+                               TopicEntry newEntry = new TopicEntry(entryOffset, tocIdx, titleOffset, urltablOffset, visibilityMode, unknownMode, _associatedFile);\r
+                               _topicTable.Add( newEntry );\r
+\r
+                               nCurOffset = (int)memStream.Position;\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the arraylist containing all topic entries.\r
+               /// </summary>\r
+               public ArrayList TopicTable\r
+               {\r
+                       get\r
+                       {\r
+                               return _topicTable;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the topic entry of a given offset\r
+               /// </summary>\r
+               public TopicEntry this[int offset]\r
+               {\r
+                       get\r
+                       {\r
+                               foreach(TopicEntry curEntry in _topicTable)\r
+                                       if(curEntry.EntryOffset == offset)\r
+                                               return curEntry;\r
+\r
+                               return null;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Searches a topic by the locale name\r
+               /// </summary>\r
+               /// <param name="locale">locale name to search</param>\r
+               /// <returns>The topicentry instance if found, otherwise null</returns>\r
+               public TopicEntry GetByLocale(string locale)\r
+               {\r
+                       foreach(TopicEntry curEntry in TopicTable)\r
+                       {\r
+                               if(curEntry.Locale.ToLower() == locale.ToLower())\r
+                                       return curEntry;\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Searches the topics for all files with a given file extension\r
+               /// </summary>\r
+               /// <param name="fileExtension">extension to search</param>\r
+               /// <returns>An arraylist of TopicEntry instances or null if no topic was found</returns>\r
+               public ArrayList GetByExtension(string fileExtension)\r
+               {\r
+                       ArrayList arrRet = new ArrayList();\r
+\r
+                       foreach(TopicEntry curEntry in TopicTable)\r
+                       {\r
+                               if(curEntry.Locale.ToLower().EndsWith(fileExtension.ToLower()))\r
+                                       arrRet.Add(curEntry);\r
+                       }\r
+\r
+                       if(arrRet.Count > 0)\r
+                               return arrRet;\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                                       _topicTable=null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs
new file mode 100644 (file)
index 0000000..b87eeba
--- /dev/null
@@ -0,0 +1,308 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Collections.Specialized;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMUrlstr</c> implements a string collection storing the URL strings of the help file\r
+       /// </summary>\r
+       internal sealed class CHMUrlstr : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Constant specifying the size of the string blocks\r
+               /// </summary>\r
+               private const int BLOCK_SIZE = 0x1000;\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing the url dictionary\r
+               /// </summary>\r
+               private Hashtable _urlDictionary = new Hashtable();\r
+               /// <summary>\r
+               /// Internal member storing the framename dictionary\r
+               /// </summary>\r
+               private Hashtable _framenameDictionary = new Hashtable();\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #URLSTR file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMUrlstr(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+\r
+                       // clear internal binary data after extraction\r
+                       _binaryFileData = null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal CHMUrlstr()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _urlDictionary.Count );\r
+\r
+                       if (_urlDictionary.Count != 0)\r
+                       {\r
+                               IDictionaryEnumerator iDictionaryEnumerator = _urlDictionary.GetEnumerator();\r
+                               while (iDictionaryEnumerator.MoveNext())\r
+                               {\r
+                                       DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;\r
+                                       writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );\r
+                                       writer.Write( dictionaryEntry.Value.ToString() );\r
+                               }\r
+                       }\r
+\r
+                       writer.Write( _framenameDictionary.Count );\r
+\r
+                       if (_framenameDictionary.Count != 0)\r
+                       {\r
+                               IDictionaryEnumerator iDictionaryEnumerator = _framenameDictionary.GetEnumerator();\r
+                               while (iDictionaryEnumerator.MoveNext())\r
+                               {\r
+                                       DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;\r
+                                       writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );\r
+                                       writer.Write( dictionaryEntry.Value.ToString() );\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       int i=0;\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt;i++)\r
+                       {\r
+                               int nKey = reader.ReadInt32();\r
+                               string sValue = reader.ReadString();\r
+                               \r
+                               _urlDictionary[nKey.ToString()] = sValue;\r
+                       }\r
+\r
+                       nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt;i++)\r
+                       {\r
+                               int nKey = reader.ReadInt32();\r
+                               string sValue = reader.ReadString();\r
+                               \r
+                               _framenameDictionary[nKey.ToString()] = sValue;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+                       \r
+                       int nCurOffset = 0;\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               nCurOffset = (int)memStream.Position;\r
+                               byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);\r
+                               bRet &= DecodeBlock(dataBlock, ref nCurOffset);\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes a block of url-string data\r
+               /// </summary>\r
+               /// <param name="dataBlock">block of data</param>\r
+               /// <param name="nOffset">current file offset</param>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeBlock( byte[] dataBlock, ref int nOffset )\r
+               {\r
+                       bool bRet = true;\r
+                       int blockOffset = nOffset;\r
+\r
+                       MemoryStream memStream = new MemoryStream(dataBlock);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       if(nOffset==0)\r
+                               binReader.ReadByte(); // first block starts with an unknown byte\r
+\r
+                       while( (memStream.Position < (memStream.Length-8)) && (bRet) )\r
+                       {\r
+                               int entryOffset = blockOffset + (int)memStream.Position;\r
+\r
+                               int urlOffset = binReader.ReadInt32();\r
+                               int frameOffset = binReader.ReadInt32();\r
+\r
+\r
+                               // There is one way to tell where the end of the URL/FrameName \r
+                               // pairs occurs: Repeat the following: read 2 DWORDs and if both \r
+                               // are less than the current offset then this is the start of the Local \r
+                               // strings else skip two NT strings.\r
+                               //                              if(( (urlOffset < (entryOffset+8)) && (frameOffset < (entryOffset+8)) ))\r
+                               //                              {\r
+                               //                                      //TODO: add correct string reading if an offset has been found\r
+                               //                                      /*\r
+                               //                                      int curOffset = (int)memStream.Position;\r
+                               //\r
+                               //                                      memStream.Seek( (long)(blockOffset-urlOffset), SeekOrigin.Begin);\r
+                               //                                      string sTemp = CHMReader.ExtractString(ref binReader, 0, true);\r
+                               //\r
+                               //                                      memStream.Seek( (long)(blockOffset-frameOffset), SeekOrigin.Begin);\r
+                               //                                      sTemp = CHMReader.ExtractString(ref binReader, 0, true);\r
+                               //\r
+                               //                                      memStream.Seek((long)curOffset, SeekOrigin.Begin);\r
+                               //                                      */\r
+                               //\r
+                               //\r
+                               //                                      int curOffs = (int)memStream.Position;\r
+                               //                                      BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding);\r
+                               //                                      nOffset += (int)memStream.Position - curOffs;\r
+                               //\r
+                               //                                      curOffs = (int)memStream.Position;\r
+                               //                                      BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding);\r
+                               //                                      nOffset += (int)memStream.Position - curOffs;\r
+                               //                              } \r
+                               //                              else\r
+                       {\r
+                               bool bFoundTerminator = false;\r
+\r
+                               string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);\r
+\r
+                               if(sTemp == "")\r
+                               {\r
+                                       //nOffset = nOffset + 1 + (int)memStream.Length - (int)memStream.Position;\r
+                                       memStream.Seek(memStream.Length-1, SeekOrigin.Begin);\r
+                               } \r
+                               else \r
+                               {\r
+                                       _urlDictionary[entryOffset.ToString()] = sTemp.ToString();\r
+                                       _framenameDictionary[ entryOffset.ToString() ] =  sTemp.ToString() ;\r
+                               }\r
+                       }\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the url at a given offset\r
+               /// </summary>\r
+               /// <param name="offset">offset of url</param>\r
+               /// <returns>the url at the given offset</returns>\r
+               public string GetURLatOffset(int offset)\r
+               {\r
+                       if(offset == -1)\r
+                               return String.Empty;\r
+\r
+                       string sTemp = (string)_urlDictionary[ offset.ToString() ];\r
+\r
+                       if(sTemp == null)\r
+                               return String.Empty;\r
+\r
+                       return sTemp;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the framename at a given offset\r
+               /// </summary>\r
+               /// <param name="offset">offset of the framename</param>\r
+               /// <returns>the frame name at the given offset</returns>\r
+               public string GetFrameNameatOffset(int offset)\r
+               {\r
+                       if(offset == -1)\r
+                               return String.Empty;\r
+\r
+                       string sTemp = (string)_framenameDictionary[ offset.ToString() ];\r
+\r
+                       if(sTemp == null)\r
+                               return String.Empty;\r
+\r
+                       return sTemp;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                                       _urlDictionary = null;\r
+                                       _framenameDictionary = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs b/irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs
new file mode 100644 (file)
index 0000000..79f718b
--- /dev/null
@@ -0,0 +1,245 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>CHMUrltable</c> implements methods to decode the #URLTBL internal file.\r
+       /// </summary>\r
+       internal sealed class CHMUrltable : IDisposable\r
+       {\r
+               /// <summary>\r
+               /// Constant specifying the size of the data blocks\r
+               /// </summary>\r
+               private const int BLOCK_SIZE = 0x1000;\r
+               /// <summary>\r
+               /// Constant specifying the number of records per block\r
+               /// </summary>\r
+               private const int RECORDS_PER_BLOCK = 341;\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal member storing the url table\r
+               /// </summary>\r
+               private ArrayList _urlTable = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the #URLTBL file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public CHMUrltable(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+                       DecodeData();\r
+\r
+                       // clear internal binary data after extraction\r
+                       _binaryFileData = null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal CHMUrltable()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _urlTable.Count );\r
+                       foreach(UrlTableEntry curItem in _urlTable)\r
+                       {\r
+                               curItem.Dump(ref writer);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       int i=0;\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt;i++)\r
+                       {\r
+                               UrlTableEntry newItem = new UrlTableEntry();\r
+                               newItem.SetCHMFile(_associatedFile);\r
+                               newItem.ReadDump(ref reader);\r
+                               _urlTable.Add(newItem);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+\r
+                       foreach(UrlTableEntry curEntry in _urlTable)\r
+                       {\r
+                               curEntry.SetCHMFile(associatedFile);\r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Decodes the binary file data and fills the internal properties\r
+               /// </summary>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeData()\r
+               {\r
+                       bool bRet = true;\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+                       \r
+                       int nCurOffset = 0;\r
+\r
+                       while( (memStream.Position < memStream.Length) && (bRet) )\r
+                       {\r
+                               nCurOffset = (int)memStream.Position;\r
+                               byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);\r
+                               bRet &= DecodeBlock(dataBlock, ref nCurOffset);\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes a block of url-string data\r
+               /// </summary>\r
+               /// <param name="dataBlock">block of data</param>\r
+               /// <param name="nOffset">current file offset</param>\r
+               /// <returns>true if succeeded</returns>\r
+               private bool DecodeBlock( byte[] dataBlock, ref int nOffset )\r
+               {\r
+                       bool bRet = true;\r
+                       int blockOffset = nOffset;\r
+\r
+                       MemoryStream memStream = new MemoryStream(dataBlock);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       for(int i=0; i < RECORDS_PER_BLOCK; i++)\r
+                       {\r
+                               int recordOffset = blockOffset + (int)memStream.Position;\r
+\r
+                               uint nuniqueID = (uint) binReader.ReadInt32(); // unknown dword\r
+                               int ntopicsIdx = binReader.ReadInt32();\r
+                               int urlstrOffset = binReader.ReadInt32();\r
+\r
+                               UrlTableEntry newEntry = new UrlTableEntry(nuniqueID, recordOffset, ntopicsIdx, urlstrOffset, _associatedFile);\r
+                               _urlTable.Add(newEntry);\r
+\r
+                               if( memStream.Position >= memStream.Length)\r
+                                       break;\r
+                       }\r
+\r
+                       if(dataBlock.Length == BLOCK_SIZE)\r
+                               binReader.ReadInt32();\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the arraylist containing all urltable entries.\r
+               /// </summary>\r
+               public ArrayList UrlTable\r
+               {\r
+                       get\r
+                       {\r
+                               return _urlTable;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the urltable entry of a given offset\r
+               /// </summary>\r
+               public UrlTableEntry this[int offset]\r
+               {\r
+                       get\r
+                       {\r
+                               foreach(UrlTableEntry curEntry in _urlTable)\r
+                                       if(curEntry.EntryOffset == offset)\r
+                                               return curEntry;\r
+\r
+                               return null;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the urltable entry of a given uniqueID\r
+               /// </summary>\r
+               public UrlTableEntry GetByUniqueID(uint uniqueID)\r
+               {\r
+                       foreach(UrlTableEntry curEntry in UrlTable)\r
+                       {\r
+                               if(curEntry.UniqueID == uniqueID)\r
+                                       return curEntry;\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                                       _urlTable = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs b/irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs
new file mode 100644 (file)
index 0000000..f42fb98
--- /dev/null
@@ -0,0 +1,395 @@
+using System;\r
+using System.IO;\r
+using System.Text;\r
+using System.Diagnostics;\r
+using System.Collections.Specialized;\r
+\r
+using ICSharpCode.SharpZipLib;\r
+using ICSharpCode.SharpZipLib.Zip;\r
+using ICSharpCode.SharpZipLib.Zip.Compression;\r
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
+\r
+using HtmlHelp;\r
+// using HtmlHelp.Storage;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// Enumeration for specifying the dumping compression\r
+       /// </summary>\r
+       public enum DumpCompression\r
+       {\r
+               /// <summary>\r
+               /// None - no data compression will be used. \r
+               /// Fastest but most memory intensive\r
+               /// </summary>\r
+               None = 0,\r
+               /// <summary>\r
+               /// Minimum - a minimum data compression will be used.\r
+               /// Fast but not much data reduction\r
+               /// </summary>\r
+               Minimum = 1,\r
+               /// <summary>\r
+               /// Medium - a medium data compression will be used.\r
+               /// Slower but medium data reduction\r
+               /// </summary>\r
+               Medium = 2,\r
+               /// <summary>\r
+               /// Maximum - a maximum data compression will be used.\r
+               /// Slowest but maximum data reduction\r
+               /// </summary>\r
+               Maximum = 3\r
+       }\r
+\r
+       /// <summary>\r
+       /// Flags which specify which data should be dumped\r
+       /// </summary>\r
+       [FlagsAttribute()]\r
+       public enum DumpingFlags\r
+       {\r
+               /// <summary>\r
+               /// DumpTextTOC - if this flag is set, text-based TOCs (sitemap format) will be dumped\r
+               /// </summary>\r
+               DumpTextTOC = 1,\r
+               /// <summary>\r
+               /// DumpBinaryTOC - if this flag is set, binary TOCs will be dumped\r
+               /// </summary>\r
+               DumpBinaryTOC = 2,\r
+               /// <summary>\r
+               /// DumpTextIndex - if this flag is set, the text-based index (sitemap format) will be dumped\r
+               /// </summary>\r
+               DumpTextIndex = 4,\r
+               /// <summary>\r
+               /// DumpBinaryIndex - if this flag is set, the binary index will be dumped\r
+               /// </summary>\r
+               DumpBinaryIndex = 8,\r
+               /// <summary>\r
+               /// DumpStrings - if this flag is set, the internal #STRINGS file will be dumped\r
+               /// </summary>\r
+               DumpStrings = 16,\r
+               /// <summary>\r
+               /// DumpUrlStr - if this flag is set, the internal #URLSTR file will be dumped\r
+               /// </summary>\r
+               DumpUrlStr = 32,\r
+               /// <summary>\r
+               /// DumpUrlTbl - if this flag is set, the internal #URLTBL file will be dumped\r
+               /// </summary>\r
+               DumpUrlTbl = 64,\r
+               /// <summary>\r
+               /// DumpTopics - if this flag is set, the internal #TOPICS file will be dumped\r
+               /// </summary>\r
+               DumpTopics = 128,\r
+               /// <summary>\r
+               /// DumpFullText - if this flag is set, the internal $FIftiMain file will be dumped\r
+               /// </summary>\r
+               DumpFullText = 256\r
+       }\r
+\r
+       /// <summary>\r
+       /// The class <c>DumpingInfo</c> implements information properties for the CHMFile class \r
+       /// if and how data dumping should be used.\r
+       /// </summary>\r
+       public sealed class DumpingInfo\r
+       {\r
+               public bool m_bAllowSaveDump=true;\r
+\r
+               private readonly static BitVector32.Section DumpFlags = BitVector32.CreateSection(512);\r
+\r
+               private const string _dumpHeader = "HtmlHelpSystem dump file 1.0";\r
+\r
+               private string _outputDir = ""; // emtpy string means, same directory as chm file\r
+               private DumpCompression _compressionLevel = DumpCompression.Maximum;\r
+               private CHMFile _chmFile = null;\r
+\r
+               private DeflaterOutputStream _outputStream = null;\r
+               private InflaterInputStream _inputStream = null;\r
+\r
+               private BinaryWriter _writer = null;\r
+               private BinaryReader _reader = null;\r
+\r
+               private BitVector32 _flags;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="flags">Combine flag values to specify which data should be dumped.</param>\r
+               /// <param name="outputDir">output directory. emtpy string means, \r
+               /// same directory as chm file (only if destination = ExternalFile)</param>\r
+               /// <param name="compressionLevel">compression which should be used</param>\r
+               public DumpingInfo(DumpingFlags flags, string outputDir, DumpCompression compressionLevel)\r
+               {\r
+                       _flags = new BitVector32(0);\r
+                       int i = _flags[DumpFlags];\r
+                       _flags[DumpFlags] = i | (int)flags;\r
+\r
+                       _outputDir = outputDir;\r
+                       _compressionLevel = compressionLevel;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if text-based TOCs will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpTextTOC\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTextTOC) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if binary TOCs will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpBinaryTOC\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpBinaryTOC) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the text-based index will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpTextIndex\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTextIndex) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the binary index will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpBinaryIndex\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpBinaryIndex) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the #STRINGS file will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpStrings\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpStrings) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the #URLSTR file will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpUrlStr\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpUrlStr) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the #URLTBL file will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpUrlTbl\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpUrlTbl) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the #TOPICS file will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpTopics\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTopics) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the $FIftiMain file will be written to the dumping file\r
+               /// </summary>\r
+               public bool DumpFullText\r
+               {\r
+                       get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpFullText) != 0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the dump output directory.\r
+               /// </summary>\r
+               /// <value>emtpy string means, same directory as chm file</value>\r
+               /// <remarks>If Destination is set to DumpingOutput.InternalFile this property will be ignored</remarks>\r
+               public string OutputDir\r
+               {\r
+                       get { return _outputDir; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// The compression level used.\r
+               /// </summary>\r
+               public DumpCompression CompressionLevel\r
+               {\r
+                       get { return _compressionLevel; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the CHMFile instance associated with this object\r
+               /// </summary>\r
+               internal CHMFile ChmFile\r
+               {\r
+                       get { return _chmFile; }\r
+                       set { _chmFile = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Translates the compression level to the deflater constants\r
+               /// </summary>\r
+               private int CompLvl\r
+               {\r
+                       get\r
+                       {\r
+                               switch(CompressionLevel)\r
+                               {\r
+                                       case DumpCompression.None: return Deflater.NO_COMPRESSION;\r
+                                       case DumpCompression.Minimum: return Deflater.BEST_SPEED;\r
+                                       case DumpCompression.Medium: return Deflater.DEFAULT_COMPRESSION;\r
+                                       case DumpCompression.Maximum: return Deflater.BEST_COMPRESSION;\r
+                               }\r
+\r
+                               return Deflater.BEST_COMPRESSION;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if a dump exists\r
+               /// </summary>\r
+               internal bool DumpExists\r
+               {\r
+                       get\r
+                       {\r
+                               if(_flags[DumpFlags] == 0)\r
+                                       return false;\r
+\r
+                               // we have a reader or writer to the dump so it must exist\r
+                               if( (_reader != null) || (_writer != null) )\r
+                                       return true;\r
+\r
+                               string sDmpFile = _chmFile.ChmFilePath;\r
+                               sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");\r
+\r
+                               return File.Exists(sDmpFile);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a binary writer instance which allows you to write to the dump\r
+               /// </summary>\r
+               internal BinaryWriter Writer\r
+               {\r
+                       get\r
+                       {\r
+                               if (m_bAllowSaveDump==false)\r
+                                       return null;\r
+\r
+                               if(_flags[DumpFlags] == 0)\r
+                                       throw new InvalidOperationException("Nothing to dump. No flags have been set !");\r
+\r
+                               if(_reader != null)\r
+                                       throw new InvalidOperationException("Can't write and read at the same time !");\r
+\r
+                               if(_chmFile == null)\r
+                                       throw new InvalidOperationException("Only usable with an associated CHMFile instance !");\r
+\r
+                               if(_writer==null)\r
+                               {\r
+                                       string sDmpFile = _chmFile.ChmFilePath;\r
+                                       sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");\r
+                                       StreamWriter stream = new StreamWriter(sDmpFile, false, _chmFile.TextEncoding);\r
+\r
+                                       // write header info uncompressed\r
+                                       BinaryWriter _hwriter = new BinaryWriter(stream.BaseStream);\r
+                                       _hwriter.Write(_dumpHeader);\r
+                                       _hwriter.Write((int)CompressionLevel);\r
+\r
+                                       if(_compressionLevel == DumpCompression.None)\r
+                                       {\r
+                                               _writer = new BinaryWriter(stream.BaseStream);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               _outputStream = new DeflaterOutputStream(stream.BaseStream, new Deflater(CompLvl));\r
+\r
+                                               _writer = new BinaryWriter(_outputStream);\r
+                                       }\r
+                               }\r
+\r
+                               return _writer;\r
+\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a binary reader which allows you to read from the dump\r
+               /// </summary>\r
+               internal BinaryReader Reader\r
+               {\r
+                       get\r
+                       {\r
+                               if(_writer != null)\r
+                                       throw new InvalidOperationException("Can't write and read at the same time !");\r
+\r
+                               if(_chmFile == null)\r
+                                       throw new InvalidOperationException("Only usable with an associated CHMFile instance !");\r
+\r
+                               if(_reader==null)\r
+                               {\r
+                                       string sDmpFile = _chmFile.ChmFilePath;\r
+                                       sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");\r
+                                       StreamReader stream = new StreamReader(sDmpFile, _chmFile.TextEncoding);\r
+\r
+                                       BinaryReader _hReader = new BinaryReader(stream.BaseStream);\r
+                                       string sH = _hReader.ReadString();\r
+\r
+                                       if(sH != _dumpHeader)\r
+                                       {\r
+                                               _hReader.Close();\r
+                                               Debug.WriteLine("Unexpected dump-file header !");\r
+                                               throw new FormatException("DumpingInfo.Reader - Unexpected dump-file header !");\r
+                                       }\r
+\r
+                                       _compressionLevel = (DumpCompression)_hReader.ReadInt32();\r
+//                                     if(_compressionLevel != (DumpCompression)_hReader.ReadInt32())\r
+//                                     {\r
+//                                             _hReader.Close();\r
+//                                             return null;\r
+//                                     }\r
+\r
+                                       if(_compressionLevel == DumpCompression.None)\r
+                                       {\r
+                                               _reader = new BinaryReader(stream.BaseStream);\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               _inputStream = new InflaterInputStream(stream.BaseStream, new Inflater());\r
+\r
+                                               _reader = new BinaryReader(_inputStream);\r
+                                       }\r
+                               }\r
+\r
+                               return _reader;\r
+                       }\r
+               }\r
+                       \r
+               /// <summary>\r
+               /// Saves data and closes the dump\r
+               /// </summary>\r
+               /// <returns>true if succeed</returns>\r
+               internal bool SaveData()\r
+               {\r
+                       if (m_bAllowSaveDump==false)\r
+                               return true;\r
+\r
+                       if(_writer != null)\r
+                       {\r
+                               if(_writer!=null)\r
+                                       _writer.Close();\r
+                               _outputStream = null;\r
+                               _writer = null;\r
+                       }\r
+\r
+                       if(_reader != null)\r
+                       {\r
+                               if(_reader!=null)\r
+                                       _reader.Close();\r
+                               _inputStream = null;\r
+                               _reader = null;\r
+                       }\r
+\r
+                       return true;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs b/irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs
new file mode 100644 (file)
index 0000000..fc1d634
--- /dev/null
@@ -0,0 +1,1131 @@
+using System;\r
+using System.Data;\r
+using System.Diagnostics;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Globalization;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>FullTextSearcher</c> implements a fulltext searcher for a single chm file !\r
+       /// </summary>\r
+       internal sealed class FullTextEngine : IDisposable\r
+       {\r
+               #region Internal helper classes\r
+               /// <summary>\r
+               /// Internal class for decoding the header\r
+               /// </summary>\r
+               private sealed class FTHeader\r
+               {\r
+                       /// <summary>\r
+                       /// Internal member storing the number of indexed files\r
+                       /// </summary>\r
+                       private int _numberOfIndexFiles = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the offset of the root node\r
+                       /// </summary>\r
+                       private int _rootOffset = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the index-page count\r
+                       /// </summary>\r
+                       private int _pageCount = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the depth of the tree\r
+                       /// </summary>\r
+                       private int _depth = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the scale param for document index en-/decoding \r
+                       /// </summary>\r
+                       private byte _scaleDocIdx = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the scale param for code-count en-/decoding \r
+                       /// </summary>\r
+                       private byte _scaleCodeCnt = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the scale param for location codes en-/decoding \r
+                       /// </summary>\r
+                       private byte _scaleLocCodes = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the root param for document index en-/decoding \r
+                       /// </summary>\r
+                       private byte _rootDocIdx = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the root param for code-count en-/decoding \r
+                       /// </summary>\r
+                       private byte _rootCodeCnt = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the root param for location codes en-/decoding \r
+                       /// </summary>\r
+                       private byte _rootLocCodes = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the size of the nodes in bytes\r
+                       /// </summary>\r
+                       private int _nodeSize = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the length of the longest word\r
+                       /// </summary>\r
+                       private int _lengthOfLongestWord = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the total number of words\r
+                       /// </summary>\r
+                       private int _totalNumberOfWords = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the total number of unique words\r
+                       /// </summary>\r
+                       private int _numberOfUniqueWords = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the codepage identifier\r
+                       /// </summary>\r
+                       private int _codePage = 1252;\r
+                       /// <summary>\r
+                       /// Internal member storing the language code id\r
+                       /// </summary>\r
+                       private int _lcid = 1033;\r
+                       /// <summary>\r
+                       /// Internal member storing the text encoder\r
+                       /// </summary>\r
+                       private Encoding _textEncoder = Encoding.Default;\r
+\r
+                       /// <summary>\r
+                       /// Constructor of the header\r
+                       /// </summary>\r
+                       /// <param name="binaryData">binary data from which the header will be extracted</param>\r
+                       public FTHeader(byte[] binaryData)\r
+                       {\r
+                               DecodeHeader(binaryData);\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Internal constructor for reading from dump\r
+                       /// </summary>\r
+                       internal FTHeader()\r
+                       {\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Decodes the binary header information and fills the members\r
+                       /// </summary>\r
+                       /// <param name="binaryData">binary data from which the header will be extracted</param>\r
+                       private void DecodeHeader(byte[] binaryData)\r
+                       {\r
+                               MemoryStream memStream = new MemoryStream(binaryData);\r
+                               BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                               binReader.ReadBytes(4); // 4 unknown bytes\r
+                               \r
+                               _numberOfIndexFiles = binReader.ReadInt32();  // number of indexed files\r
+\r
+                               binReader.ReadInt32(); // unknown\r
+                               binReader.ReadInt32(); // unknown\r
+\r
+                               _pageCount = binReader.ReadInt32();  // page-count\r
+                               _rootOffset = binReader.ReadInt32(); // file offset of the root node\r
+                               _depth = binReader.ReadInt16(); // depth of the tree\r
+\r
+                               binReader.ReadInt32(); // unknown\r
+\r
+                               _scaleDocIdx = binReader.ReadByte();\r
+                               _rootDocIdx =  binReader.ReadByte();\r
+                               _scaleCodeCnt = binReader.ReadByte();\r
+                               _rootCodeCnt = binReader.ReadByte();\r
+                               _scaleLocCodes = binReader.ReadByte();\r
+                               _rootLocCodes = binReader.ReadByte();\r
+\r
+                               if( (_scaleDocIdx != 2) || ( _scaleCodeCnt != 2 ) || ( _scaleLocCodes != 2 ) )\r
+                               {\r
+                                       Debug.WriteLine("Unsupported scale for s/r encoding !");\r
+                                       throw new InvalidOperationException("Unsupported scale for s/r encoding !");\r
+                               }\r
+\r
+                               binReader.ReadBytes(10); // unknown\r
+\r
+                               _nodeSize = binReader.ReadInt32();\r
+\r
+                               binReader.ReadInt32(); // unknown\r
+                               binReader.ReadInt32(); // not important\r
+                               binReader.ReadInt32(); // not important\r
+\r
+                               _lengthOfLongestWord = binReader.ReadInt32();\r
+                               _totalNumberOfWords = binReader.ReadInt32();\r
+                               _numberOfUniqueWords = binReader.ReadInt32();\r
+\r
+                               binReader.ReadInt32(); // not important\r
+                               binReader.ReadInt32(); // not important\r
+                               binReader.ReadInt32(); // not important\r
+                               binReader.ReadInt32(); // not important\r
+                               binReader.ReadInt32(); // not important\r
+                               binReader.ReadInt32(); // not important\r
+\r
+                               binReader.ReadBytes(24); // not important\r
+\r
+                               _codePage = binReader.ReadInt32();\r
+                               _lcid = binReader.ReadInt32();\r
+\r
+                               CultureInfo ci = new CultureInfo(_lcid);\r
+                               _textEncoder = Encoding.GetEncoding( ci.TextInfo.ANSICodePage );\r
+\r
+                               // rest of header is not important for us\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Dump the class data to a binary writer\r
+                       /// </summary>\r
+                       /// <param name="writer">writer to write the data</param>\r
+                       internal void Dump(ref BinaryWriter writer)\r
+                       {\r
+                               writer.Write( _numberOfIndexFiles );\r
+                               writer.Write( _rootOffset );\r
+                               writer.Write( _pageCount );\r
+                               writer.Write( _depth );\r
+                               writer.Write( _scaleDocIdx );\r
+                               writer.Write( _rootDocIdx );\r
+                               writer.Write( _scaleCodeCnt );\r
+                               writer.Write( _rootCodeCnt );\r
+                               writer.Write( _scaleLocCodes );\r
+                               writer.Write( _rootLocCodes );\r
+                               writer.Write( _nodeSize );\r
+                               writer.Write( _lengthOfLongestWord );\r
+                               writer.Write( _totalNumberOfWords );\r
+                               writer.Write( _numberOfUniqueWords );\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Reads the object data from a dump store\r
+                       /// </summary>\r
+                       /// <param name="reader">reader to read the data</param>\r
+                       internal void ReadDump(ref BinaryReader reader)\r
+                       {\r
+                               _numberOfIndexFiles = reader.ReadInt32();\r
+                               _rootOffset = reader.ReadInt32();\r
+                               _pageCount = reader.ReadInt32();\r
+                               _depth = reader.ReadInt32();\r
+                               \r
+                               _scaleDocIdx = reader.ReadByte();\r
+                               _rootDocIdx = reader.ReadByte();\r
+                               _scaleCodeCnt = reader.ReadByte();\r
+                               _rootCodeCnt = reader.ReadByte();\r
+                               _scaleLocCodes = reader.ReadByte();\r
+                               _rootLocCodes = reader.ReadByte();\r
+\r
+                               _nodeSize = reader.ReadInt32();\r
+                               _lengthOfLongestWord = reader.ReadInt32();\r
+                               _totalNumberOfWords = reader.ReadInt32();\r
+                               _numberOfUniqueWords = reader.ReadInt32();\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the number of indexed files\r
+                       /// </summary>\r
+                       public int IndexedFileCount\r
+                       {\r
+                               get { return _numberOfIndexFiles; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the file offset of the root node\r
+                       /// </summary>\r
+                       public int RootOffset\r
+                       {\r
+                               get { return _rootOffset; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the page count\r
+                       /// </summary>\r
+                       public int PageCount\r
+                       {\r
+                               get { return _pageCount; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the index depth\r
+                       /// </summary>\r
+                       public int Depth\r
+                       {\r
+                               get { return _depth; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the scale param for document index en-/decoding\r
+                       /// </summary>\r
+                       /// <remarks>The scale and root method of integer encoding needs two parameters, \r
+                       /// which I'll call s (scale) and r (root size).\r
+                       /// The integer is encoded as two parts, p (prefix) and q (actual bits). \r
+                       /// p determines how many bits are stored, as well as implicitly determining \r
+                       /// the high-order bit of the integer. </remarks>\r
+                       public byte ScaleDocumentIndex\r
+                       {\r
+                               get { return _scaleDocIdx; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the root param for the document index en-/decoding\r
+                       /// </summary>\r
+                       /// <remarks>The scale and root method of integer encoding needs two parameters, \r
+                       /// which I'll call s (scale) and r (root size).\r
+                       /// The integer is encoded as two parts, p (prefix) and q (actual bits). \r
+                       /// p determines how many bits are stored, as well as implicitly determining \r
+                       /// the high-order bit of the integer. </remarks>\r
+                       public byte RootDocumentIndex\r
+                       {\r
+                               get { return _rootDocIdx; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the scale param for the code-count en-/decoding\r
+                       /// </summary>\r
+                       /// <remarks>The scale and root method of integer encoding needs two parameters, \r
+                       /// which I'll call s (scale) and r (root size).\r
+                       /// The integer is encoded as two parts, p (prefix) and q (actual bits). \r
+                       /// p determines how many bits are stored, as well as implicitly determining \r
+                       /// the high-order bit of the integer. </remarks>\r
+                       public byte ScaleCodeCount\r
+                       {\r
+                               get { return _scaleCodeCnt; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the root param for the code-count en-/decoding\r
+                       /// </summary>\r
+                       /// <remarks>The scale and root method of integer encoding needs two parameters, \r
+                       /// which I'll call s (scale) and r (root size).\r
+                       /// The integer is encoded as two parts, p (prefix) and q (actual bits). \r
+                       /// p determines how many bits are stored, as well as implicitly determining \r
+                       /// the high-order bit of the integer. </remarks>\r
+                       public byte RootCodeCount\r
+                       {\r
+                               get { return _rootCodeCnt; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the scale param for the location codes en-/decoding\r
+                       /// </summary>\r
+                       /// <remarks>The scale and root method of integer encoding needs two parameters, \r
+                       /// which I'll call s (scale) and r (root size).\r
+                       /// The integer is encoded as two parts, p (prefix) and q (actual bits). \r
+                       /// p determines how many bits are stored, as well as implicitly determining \r
+                       /// the high-order bit of the integer. </remarks>\r
+                       public byte ScaleLocationCodes\r
+                       {\r
+                               get { return _scaleLocCodes; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the root param for the location codes en-/decoding\r
+                       /// </summary>\r
+                       /// <remarks>The scale and root method of integer encoding needs two parameters, \r
+                       /// which I'll call s (scale) and r (root size).\r
+                       /// The integer is encoded as two parts, p (prefix) and q (actual bits). \r
+                       /// p determines how many bits are stored, as well as implicitly determining \r
+                       /// the high-order bit of the integer. </remarks>\r
+                       public byte RootLocationCodes\r
+                       {\r
+                               get { return _rootLocCodes; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the size in bytes of each index/leaf node\r
+                       /// </summary>\r
+                       public int NodeSize\r
+                       {\r
+                               get { return _nodeSize; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the length of the longest word in the index\r
+                       /// </summary>\r
+                       private int LengthOfLongestWord\r
+                       {\r
+                               get { return _lengthOfLongestWord; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the total number of words indexed (including duplicates)\r
+                       /// </summary>\r
+                       public int TotalWordCount\r
+                       {\r
+                               get { return _totalNumberOfWords; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the total number of unique words indexed (excluding duplicates)\r
+                       /// </summary>\r
+                       public int UniqueWordCount\r
+                       {\r
+                               get { return _numberOfUniqueWords; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the codepage identifier\r
+                       /// </summary>\r
+                       public int CodePage\r
+                       {\r
+                               get { return _codePage; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the language code id\r
+                       /// </summary>\r
+                       public int LCID\r
+                       {\r
+                               get { return _lcid; }\r
+                       }\r
+\r
+                       public Encoding TextEncoder\r
+                       {\r
+                               get \r
+                               { \r
+                                       return _textEncoder;\r
+                               }\r
+                       }\r
+               }\r
+\r
+\r
+               /// <summary>\r
+               /// Internal class for easier hit recording and rate-calculation\r
+               /// </summary>\r
+               private sealed class HitHelper : IComparable\r
+               {\r
+                       /// <summary>\r
+                       /// Internal member storing the associated document index\r
+                       /// </summary>\r
+                       private int _documentIndex = 0;\r
+                       /// <summary>\r
+                       /// Internal member storing the title\r
+                       /// </summary>\r
+                       private string _title = "";\r
+                       /// <summary>\r
+                       /// Internal member storing the locale\r
+                       /// </summary>\r
+                       private string _locale = "";\r
+                       /// <summary>\r
+                       /// Internal member storing the location\r
+                       /// </summary>\r
+                       private string _location = "";\r
+                       /// <summary>\r
+                       /// Internal member storing the url\r
+                       /// </summary>\r
+                       private string _url = "";\r
+                       /// <summary>\r
+                       /// Internal member storing the rating\r
+                       /// </summary>\r
+                       private double _rating = 0;\r
+                       /// <summary>\r
+                       /// Internal member used for rating calculation\r
+                       /// </summary>\r
+                       private Hashtable _partialRating = new Hashtable();\r
+\r
+                       /// <summary>\r
+                       /// Constructor of the class\r
+                       /// </summary>\r
+                       /// <param name="documentIndex">document index</param>\r
+                       /// <param name="title">title</param>\r
+                       /// <param name="locale">locale parameter</param>\r
+                       /// <param name="location">location</param>\r
+                       /// <param name="url">url of document</param>\r
+                       /// <param name="rating">rating</param>\r
+                       public HitHelper(int documentIndex, string title, string locale, string location, string url, double rating)\r
+                       {\r
+                               _documentIndex = documentIndex;\r
+                               _title = title;\r
+                               _locale = locale;\r
+                               _location = location;\r
+                               _url = url;\r
+                               _rating = rating;\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Updates the rating for a found word\r
+                       /// </summary>\r
+                       /// <param name="word">word found</param>\r
+                       public void UpdateRating(string word)\r
+                       {\r
+                               if( _partialRating[word] == null)\r
+                               {\r
+                                       _partialRating[word] = 100.0;\r
+                               }\r
+                               else \r
+                               {\r
+                                       _partialRating[word] = ((double)_partialRating[word])*1.01;\r
+                               }\r
+\r
+                               _rating = 0.0;\r
+\r
+                               foreach(double val in _partialRating.Values)\r
+                               {\r
+                                       _rating += val;\r
+                               }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Implements the CompareTo method of the IComparable interface. \r
+                       /// Allows an easy sort by the document rating\r
+                       /// </summary>\r
+                       /// <param name="obj">object to compare</param>\r
+                       /// <returns>0 ... equal, -1 ... this instance is less than obj, 1 ... this instance is greater than obj</returns>\r
+                       public int CompareTo(object obj)\r
+                       {\r
+                               if( obj is HitHelper )\r
+                               {\r
+                                       HitHelper hObj = (HitHelper)obj;\r
+\r
+                                       return this.Rating.CompareTo( hObj.Rating );\r
+                               }\r
+\r
+                               return -1;\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the internal hashtable used for counting word hits of the document\r
+                       /// </summary>\r
+                       internal Hashtable PartialRating\r
+                       {\r
+                               get { return _partialRating; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the document index of the hit helper instance\r
+                       /// </summary>\r
+                       public int DocumentIndex\r
+                       {\r
+                               get { return _documentIndex; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the title\r
+                       /// </summary>\r
+                       public string Title\r
+                       {\r
+                               get { return _title; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the locale\r
+                       /// </summary>\r
+                       public string Locale\r
+                       {\r
+                               get { return _locale; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the location\r
+                       /// </summary>\r
+                       public string Location\r
+                       {\r
+                               get { return _location; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the url\r
+                       /// </summary>\r
+                       public string URL\r
+                       {\r
+                               get { return _url; }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Gets the rating\r
+                       /// </summary>\r
+                       public double Rating\r
+                       {\r
+                               get { return _rating; }\r
+                       }\r
+\r
+               }\r
+\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Regular expression getting the text between to quotes\r
+               /// </summary>\r
+               private string RE_Quotes = @"\""(?<innerText>.*?)\""";\r
+               /// <summary>\r
+               /// Internal flag specifying if the object is going to be disposed\r
+               /// </summary>\r
+               private bool disposed = false;\r
+               /// <summary>\r
+               /// Internal member storing the binary file data\r
+               /// </summary>\r
+               private byte[] _binaryFileData = null;\r
+               /// <summary>\r
+               /// Internal datatable storing the search hits\r
+               /// </summary>\r
+               private DataTable _hits =null;\r
+               /// <summary>\r
+               /// Internal arraylist for hit management\r
+               /// </summary>\r
+               private ArrayList _hitsHelper = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the header of the file\r
+               /// </summary>\r
+               private FTHeader _header = null;\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="binaryFileData">binary file data of the $FIftiMain file</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public FullTextEngine(byte[] binaryFileData, CHMFile associatedFile)\r
+               {\r
+                       _binaryFileData = binaryFileData;\r
+                       _associatedFile = associatedFile;\r
+\r
+                       if(_associatedFile.SystemFile.FullTextSearch)\r
+                       {\r
+                               _header = new FTHeader(_binaryFileData); // reading header\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal FullTextEngine()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       _header.Dump(ref writer);\r
+                       writer.Write( _binaryFileData.Length );\r
+                       writer.Write(_binaryFileData);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       _header = new FTHeader();\r
+                       _header.ReadDump(ref reader);\r
+\r
+                       int nCnt = reader.ReadInt32();\r
+                       _binaryFileData = reader.ReadBytes(nCnt);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Gets a flag if full-text searching is available for this chm file.\r
+               /// </summary>\r
+               public bool CanSearch\r
+               {\r
+                       get { return (_associatedFile.SystemFile.FullTextSearch && (_header != null) ); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Performs a fulltext search of a single file.\r
+               /// </summary>\r
+               /// <param name="search">word(s) or phrase to search</param>\r
+               /// <param name="partialMatches">true if partial word should be matched also \r
+               /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>\r
+               /// <param name="titleOnly">true if only search in titles</param>\r
+               /// <remarks>Hits are available through the <see cref="Hits">Hists property</see>.</remarks>\r
+               public bool Search(string search, bool partialMatches, bool titleOnly)\r
+               {\r
+                       return  Search(search, -1, partialMatches, titleOnly);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Performs a fulltext search of a single file.\r
+               /// </summary>\r
+               /// <param name="search">word(s) or phrase to search</param>\r
+               /// <param name="MaxHits">max hits. If this number is reached, the search will be interrupted</param>\r
+               /// <param name="partialMatches">true if partial word should be matched also \r
+               /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>\r
+               /// <param name="titleOnly">true if only search in titles</param>\r
+               /// <remarks>Hits are available through the <see cref="Hits">Hists property</see>.</remarks>\r
+               public bool Search(string search, int MaxHits, bool partialMatches, bool titleOnly)\r
+               {\r
+                       if(CanSearch)\r
+                       {\r
+                               string searchString = search;\r
+\r
+                               // Check if this is a quoted string\r
+                               bool IsQuoted = (search.IndexOf("\"")>-1);\r
+\r
+                               if(IsQuoted)\r
+                                       searchString = search.Replace("\"",""); // remove the quotes during search\r
+\r
+                               bool bRet = true;\r
+\r
+                               _hitsHelper = null;\r
+                               _hitsHelper = new ArrayList();\r
+\r
+                               _hits = null;\r
+                               CreateHitsTable();\r
+\r
+                               string[] words = searchString.Split(new char[] {' '});\r
+\r
+                               for(int i=0; i<words.Length; i++)\r
+                               {\r
+                                       bRet &= SearchSingleWord(words[i], MaxHits, partialMatches, titleOnly);\r
+                                       if(_hitsHelper.Count >= MaxHits)\r
+                                               break;\r
+                               }\r
+\r
+                               if(bRet && IsQuoted)\r
+                               {\r
+                                       FinalizeQuoted(search);\r
+                               }\r
+\r
+                               if(bRet)\r
+                               {\r
+                                       _hitsHelper.Sort();\r
+\r
+                                       int nhCount = MaxHits;\r
+\r
+                                       if( MaxHits < 0)\r
+                                       {\r
+                                               nhCount = _hitsHelper.Count;\r
+                                       }\r
+\r
+                                       if( nhCount > _hitsHelper.Count )\r
+                                               nhCount = _hitsHelper.Count;\r
+\r
+                                       // create hits datatable\r
+                                       for(int i=nhCount; i > 0; i--)\r
+                                       {\r
+                                               HitHelper curHlp = (HitHelper)(_hitsHelper[i-1]);\r
+\r
+                                               DataRow newRow = _hits.NewRow();\r
+\r
+                                               newRow["Rating"] = curHlp.Rating;\r
+                                               newRow["Title"] = curHlp.Title;\r
+                                               newRow["Locale"] = curHlp.Locale;\r
+                                               newRow["Location"] = curHlp.Location;\r
+                                               newRow["URL"] = curHlp.URL;\r
+\r
+                                               _hits.Rows.Add( newRow );\r
+                                       }\r
+                               }\r
+                               return bRet;\r
+                       }\r
+\r
+                       return false;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets rid of all search hits which doesn't match the quoted phrase\r
+               /// </summary>\r
+               /// <param name="search">full search string entered by the user</param>\r
+               /// <remarks>Phrase search is not possible using the internal full-text index. We're just filtering all \r
+               /// documents which don't contain all words of the phrase.</remarks>\r
+               private void FinalizeQuoted(string search)\r
+               {\r
+                       Regex quoteRE = new Regex(RE_Quotes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       int innerTextIdx = quoteRE.GroupNumberFromName("innerText");\r
+                       int nIndex = 0;\r
+\r
+                       // get all phrases\r
+                       while( quoteRE.IsMatch(search, nIndex) )\r
+                       {\r
+                               Match m = quoteRE.Match(search, nIndex);\r
+\r
+                               string phrase = m.Groups["innerText"].Value;\r
+\r
+                               string[] wordsInPhrase = phrase.Split( new char[] {' '} );\r
+                               int nCnt = _hitsHelper.Count;\r
+\r
+                               for(int i=0; i < _hitsHelper.Count; i++)\r
+                               {\r
+                                       if( ! CheckHit( ((HitHelper)(_hitsHelper[i])), wordsInPhrase) )\r
+                                               _hitsHelper.RemoveAt(i--);\r
+                               }\r
+\r
+                               nIndex = m.Index+m.Length;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Eliminates all search hits where not all of the words have been found\r
+               /// </summary>\r
+               /// <param name="hit">hithelper instance to check</param>\r
+               /// <param name="wordsInPhrase">word list</param>\r
+               private bool CheckHit(HitHelper hit, string[] wordsInPhrase)\r
+               {\r
+\r
+                       for(int i=0; i<wordsInPhrase.Length;i++)\r
+                       {\r
+                               if( (hit.PartialRating[wordsInPhrase[i]] == null) || (((double)(hit.PartialRating[wordsInPhrase[i]])) == 0.0) )\r
+                                       return false;\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Performs a search for a single word in the index\r
+               /// </summary>\r
+               /// <param name="word">word to search</param>\r
+               /// <param name="MaxHits">maximal hits to return</param>\r
+               /// <param name="partialMatches">true if partial word should be matched also \r
+               /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>\r
+               /// <param name="titleOnly">true if only search in titles</param>\r
+               /// <returns>Returns true if succeeded</returns>\r
+               private bool SearchSingleWord(string word,int MaxHits, bool partialMatches, bool titleOnly)\r
+               {\r
+                       string wordLower = word.ToLower();\r
+\r
+                       MemoryStream memStream = new MemoryStream(_binaryFileData);\r
+                       BinaryReader binReader = new BinaryReader(memStream);\r
+\r
+                       // seek to root node\r
+                       binReader.BaseStream.Seek( _header.RootOffset, SeekOrigin.Begin );\r
+\r
+                       if( _header.Depth > 2 )\r
+                       {\r
+                               // unsupported index depth\r
+                               Debug.WriteLine("FullTextSearcher.SearchSingleWord() - Failed with message: Unsupported index depth !");\r
+                               Debug.WriteLine("File: " + _associatedFile.ChmFilePath);\r
+                               Debug.WriteLine(" ");\r
+                               return false;\r
+                       }\r
+\r
+                       if( _header.Depth > 1 )\r
+                       {\r
+                               // seek to the right leaf node ( if depth == 1, we are at the leaf node)\r
+                               int freeSpace = binReader.ReadInt16();\r
+\r
+                               for(int i=0; i < _header.PageCount; ++i)\r
+                               {\r
+                                       // exstract index entries\r
+                                       int nWLength = (int)binReader.ReadByte();\r
+                                       int nCPosition = (int)binReader.ReadByte();\r
+\r
+                                       string sName = BinaryReaderHelp.ExtractString(ref binReader, nWLength-1, 0, true, _header.TextEncoder);\r
+\r
+                                       int nLeafOffset = binReader.ReadInt32();\r
+                                       binReader.ReadInt16(); // unknown\r
+               \r
+                                       if( sName.CompareTo(wordLower) >= 0)\r
+                                       {\r
+                                               // store current position\r
+                                               long curPos = binReader.BaseStream.Position;\r
+\r
+                                               // seek to leaf offset\r
+                                               binReader.BaseStream.Seek( nLeafOffset, SeekOrigin.Begin );\r
+\r
+                                               // read leafnode\r
+                                               ReadLeafNode(ref binReader, word, MaxHits, partialMatches, titleOnly);\r
+\r
+                                               // return to current position and continue reading index nodes\r
+                                               binReader.BaseStream.Seek( curPos, SeekOrigin.Begin );\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       return true;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads a leaf node and extracts documents which holds the searched word\r
+               /// </summary>\r
+               /// <param name="binReader">reference to the reader</param>\r
+               /// <param name="word">word to search</param>\r
+               /// <param name="MaxHits">maximal hits to return</param>\r
+               /// <param name="partialMatches">true if partial word should be matched also \r
+               /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>\r
+               /// <param name="titleOnly">true if only search in titles</param>\r
+               private void ReadLeafNode(ref BinaryReader binReader, string word, int MaxHits, bool partialMatches, bool titleOnly) \r
+               {\r
+                       int nNextPageOffset = binReader.ReadInt32();\r
+                       binReader.ReadInt16(); // unknown\r
+                       int lfreeSpace = binReader.ReadInt16();\r
+                       string curFullWord = "";\r
+                       bool bFound = false;\r
+                       string wordLower = word.ToLower();\r
+\r
+                       for(;;)\r
+                       {\r
+                               if(binReader.BaseStream.Position >= binReader.BaseStream.Length)\r
+                                       break;\r
+\r
+                               int nWLength = (int)binReader.ReadByte();\r
+                               \r
+                               if(nWLength == 0)\r
+                                       break;\r
+\r
+                               int nCPosition = (int)binReader.ReadByte();\r
+\r
+                               string sName = BinaryReaderHelp.ExtractString(ref binReader, nWLength-1, 0, true, _header.TextEncoder);\r
+\r
+                               int Context = (int)binReader.ReadByte(); // 0...body tag, 1...title tag, others unknown\r
+\r
+                               long nrOfWCL = BinaryReaderHelp.ReadENCINT(ref binReader);\r
+                               int wclOffset = binReader.ReadInt32();\r
+                               \r
+                               binReader.ReadInt16(); // unknown\r
+\r
+                               long bytesOfWCL = BinaryReaderHelp.ReadENCINT(ref binReader);\r
+\r
+                               if( nCPosition > 0)\r
+                               {\r
+                                       curFullWord = CombineStrings(curFullWord, sName, nCPosition);\r
+                               } \r
+                               else \r
+                               {\r
+                                       curFullWord = sName;\r
+                               }\r
+\r
+                               bFound = false;\r
+                               if(partialMatches)\r
+                                       bFound = ( curFullWord.IndexOf(wordLower) >= 0 );\r
+                               else\r
+                                       bFound = (curFullWord == wordLower);\r
+\r
+                               if( bFound )\r
+                               {\r
+                                       if( (titleOnly && (Context==1)) || (!titleOnly) )\r
+                                       {                                               \r
+                                               // store actual offset\r
+                                               long curPos = binReader.BaseStream.Position;\r
+\r
+                                               // found the word, begin with WCL encoding\r
+                                               binReader.BaseStream.Seek(wclOffset, SeekOrigin.Begin );\r
+\r
+                                               byte[] wclBytes = binReader.ReadBytes((int)bytesOfWCL);\r
+\r
+                                               DecodeWCL(wclBytes, MaxHits, word);\r
+\r
+                                               // back and continue reading leafnodes\r
+                                               binReader.BaseStream.Seek(curPos, SeekOrigin.Begin );\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes the s/r encoded WordCodeList (=wcl) and creates hit entries\r
+               /// </summary>\r
+               /// <param name="wclBytes">wcl encoded byte array</param>\r
+               /// <param name="MaxHits">maximal hits</param>\r
+               /// <param name="word">the word to find</param>\r
+               private void DecodeWCL(byte[] wclBytes,int MaxHits, string word)\r
+               {\r
+                       byte[] wclBits = new byte[ wclBytes.Length*8 ];\r
+                       \r
+                       int nBitIdx=0;\r
+\r
+                       for(int i=0; i<wclBytes.Length; i++)\r
+                       {\r
+                               for(int j=0; j<8; j++)\r
+                               {\r
+                                       wclBits[nBitIdx] = ((byte)(wclBytes[i] & ((byte)( (byte)0x1 << (7-j) )))) > (byte)0 ? (byte)1 : (byte)0;\r
+                                       nBitIdx++;\r
+                               }\r
+                       }\r
+\r
+                       nBitIdx = 0;\r
+\r
+                       int nDocIdx = 0; // delta encoded\r
+\r
+                       while(nBitIdx < wclBits.Length)\r
+                       {\r
+                               nDocIdx += BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleDocumentIndex, _header.RootDocumentIndex, ref nBitIdx);\r
+                               int nCodeCnt = BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleCodeCount, _header.RootCodeCount, ref nBitIdx);\r
+                       \r
+                               int nWordLocation = 0; // delta encoded\r
+\r
+                               for(int locidx=0; locidx<nCodeCnt; locidx++)\r
+                               {\r
+                                        nWordLocation += BinaryReaderHelp.ReadSRItem(wclBits, _header.ScaleLocationCodes, _header.RootLocationCodes, ref nBitIdx);\r
+                               }\r
+                               // apply padding\r
+                               while( (nBitIdx % 8) != 0)\r
+                                       nBitIdx++;\r
+\r
+                               // Record hit\r
+                               HitHelper hitObj = DocumentHit(nDocIdx);\r
+\r
+                               if(hitObj == null)\r
+                               {\r
+                                       if(_hitsHelper.Count > MaxHits)\r
+                                               return;\r
+\r
+                                       hitObj = new HitHelper(nDocIdx, ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).Title,\r
+                                               ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).Locale, _associatedFile.CompileFile,\r
+                                               ((TopicEntry)(_associatedFile.TopicsFile.TopicTable[nDocIdx])).URL, 0.0);\r
+\r
+                                       for(int k=0;k<nCodeCnt;k++)\r
+                                               hitObj.UpdateRating(word);\r
+\r
+                                       _hitsHelper.Add(hitObj);\r
+                               } \r
+                               else \r
+                               {\r
+                                       for(int k=0;k<nCodeCnt;k++)\r
+                                               hitObj.UpdateRating(word);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Combines a "master" word with a partial word.\r
+               /// </summary>\r
+               /// <param name="word">the master word</param>\r
+               /// <param name="partial">the partial word</param>\r
+               /// <param name="partialPosition">position to place the parial word</param>\r
+               /// <returns>returns a combined string</returns>\r
+               private string CombineStrings(string word, string partial, int partialPosition)\r
+               {\r
+                       string sCombined = word;\r
+                       int i=0;\r
+\r
+                       for(i=0; i<partial.Length; i++)\r
+                       {\r
+                               if( (i+partialPosition) > (sCombined.Length-1) )\r
+                               {\r
+                                       sCombined += partial[i];\r
+                               } \r
+                               else \r
+                               {\r
+                                       StringBuilder sb = new StringBuilder(sCombined);\r
+\r
+                                       sb.Replace( sCombined[partialPosition+i], partial[i], partialPosition+i, 1);\r
+                                       sCombined = sb.ToString();\r
+                               }\r
+                       }\r
+\r
+                       if(! ((i+partialPosition) > (sCombined.Length-1)) )\r
+                       {\r
+                               sCombined = sCombined.Substring(0, partialPosition+partial.Length);\r
+                       }\r
+\r
+                       return sCombined;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the HitHelper instance for a specific document index\r
+               /// </summary>\r
+               /// <param name="index">document index</param>\r
+               /// <returns>The reference of the hithelper instance for this document index, otherwise null</returns>\r
+               private HitHelper DocumentHit(int index)\r
+               {\r
+                       foreach(HitHelper curObj in _hitsHelper)\r
+                       {\r
+                               if( curObj.DocumentIndex == index)\r
+                                       return curObj;\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Creates a DataTable for storing the hits\r
+               /// </summary>\r
+               private void CreateHitsTable()\r
+               {\r
+                       _hits = new DataTable("FT_Search_Hits");\r
+\r
+                       DataColumn ftColumn;\r
+\r
+                       ftColumn = new DataColumn();\r
+                       ftColumn.DataType = System.Type.GetType("System.Double");\r
+                       ftColumn.ColumnName = "Rating";\r
+                       ftColumn.ReadOnly = false;\r
+                       ftColumn.Unique = false;\r
+\r
+                       _hits.Columns.Add(ftColumn);\r
+\r
+                       ftColumn = new DataColumn();\r
+                       ftColumn.DataType = System.Type.GetType("System.String");\r
+                       ftColumn.ColumnName = "Title";\r
+                       ftColumn.ReadOnly = false;\r
+                       ftColumn.Unique = false;\r
+\r
+                       _hits.Columns.Add(ftColumn);\r
+\r
+                       ftColumn = new DataColumn();\r
+                       ftColumn.DataType = System.Type.GetType("System.String");\r
+                       ftColumn.ColumnName = "Locale";\r
+                       ftColumn.ReadOnly = false;\r
+                       ftColumn.Unique = false;\r
+\r
+                       _hits.Columns.Add(ftColumn);\r
+\r
+                       ftColumn = new DataColumn();\r
+                       ftColumn.DataType = System.Type.GetType("System.String");\r
+                       ftColumn.ColumnName = "Location";\r
+                       ftColumn.ReadOnly = false;\r
+                       ftColumn.Unique = false;\r
+\r
+                       _hits.Columns.Add(ftColumn);\r
+\r
+                       ftColumn = new DataColumn();\r
+                       ftColumn.DataType = System.Type.GetType("System.String");\r
+                       ftColumn.ColumnName = "URL";\r
+                       ftColumn.ReadOnly = false;\r
+                       ftColumn.Unique = false;\r
+\r
+                       _hits.Columns.Add(ftColumn);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an datatable containing the hits of the last search\r
+               /// </summary>\r
+               public DataTable Hits\r
+               {\r
+                       get { return _hits; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Implement IDisposable.\r
+               /// </summary>\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.\r
+                                       _binaryFileData = null;\r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs b/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs
new file mode 100644 (file)
index 0000000..7348271
--- /dev/null
@@ -0,0 +1,593 @@
+using System;\r
+using System.Collections;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>HHCParser</c> implements a parser for HHC contents files.\r
+       /// </summary>\r
+       internal sealed class HHCParser\r
+       {\r
+               /// <summary>\r
+               /// regular expressions for replacing the sitemap boundary tags \r
+               /// </summary>\r
+               private static string RE_ULOpening = @"\<ul\>";  // will be replaced by a '(' for nested parsing\r
+               private static string RE_ULClosing = @"\</ul\>"; // will be replaced by a ')' for nested parsing\r
+\r
+               /// <summary>\r
+               /// Matching ul-tags\r
+               /// </summary>\r
+               private static string RE_ULBoundaries = @"\<ul\>(?<innerText>.*)\</ul\>";\r
+               /// <summary>\r
+               /// Matching the nested tree structure.\r
+               /// </summary>\r
+               private static string RE_NestedBoundaries = @"\( (?> [^()]+ | \( (?<DEPTH>) | \) (?<-DEPTH>) )* (?(DEPTH)(?!)) \)";\r
+               /// <summary>\r
+               /// Matching object-tags\r
+               /// </summary>\r
+               private static string RE_ObjectBoundaries = @"\<object(?<innerText>.*?)\</object\>";\r
+               /// <summary>\r
+               /// Matching param tags\r
+               /// </summary>\r
+               private static string RE_ParamBoundaries = @"\<param(?<innerText>.*?)\>";\r
+               /// <summary>\r
+               /// Extracting tag attributes\r
+               /// </summary>\r
+               private const string RE_QuoteAttributes = @"( |\t)*(?<attributeName>[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?<attributeTD>[\""\'])?(?<attributeValue>.*?(?(attributeTD)\k<attributeTD>|([\s>]|.$)))";\r
+\r
+               /// <summary>\r
+               /// private regular expressionobjects \r
+               /// </summary>\r
+               private static Regex ulRE;\r
+               private static Regex NestedRE;\r
+               private static Regex ObjectRE;\r
+               private static Regex ParamRE;\r
+               private static Regex AttributesRE;\r
+\r
+               /// <summary>\r
+               /// Internal member storing the list of TOCItems which are holding merge links\r
+               /// </summary>\r
+               private static ArrayList _mergeItems = null;\r
+\r
+               /// <summary>\r
+               /// Internal member storing the last read regular topic item.\r
+               /// This is used to handle "Merge" entries and add them as child to this instance.\r
+               /// </summary>\r
+               private static TOCItem _lastTopicItem = null;\r
+\r
+               /// <summary>\r
+               /// Parses a HHC file and returns an ArrayList with the table of contents (TOC) tree\r
+               /// </summary>\r
+               /// <param name="hhcFile">string content of the hhc file</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               /// <returns>Returns an ArrayList with the table of contents (TOC) tree</returns>\r
+               public static ArrayList ParseHHC(string hhcFile, CHMFile chmFile)\r
+               {\r
+                       _lastTopicItem = null;\r
+                       _mergeItems = null; // clear merged item list\r
+                       ArrayList tocList = new ArrayList();\r
+\r
+                       ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       NestedRE = new Regex(RE_NestedBoundaries, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+\r
+                       int innerTextIdx = ulRE.GroupNumberFromName("innerText");\r
+\r
+                       if( ulRE.IsMatch(hhcFile, 0) )\r
+                       {\r
+                               Match m = ulRE.Match(hhcFile, 0);\r
+\r
+                               int nFirstUL = 0;\r
+\r
+                               nFirstUL = hhcFile.ToLower().IndexOf("<ul>");\r
+\r
+                               if(nFirstUL == -1)\r
+                                       nFirstUL = hhcFile.ToLower().IndexOf("<il>");\r
+\r
+                               if( ObjectRE.IsMatch(hhcFile, 0) ) // first object block contains information types and categories\r
+                               {\r
+                                       Match mO = ObjectRE.Match(hhcFile, 0);\r
+                                       int iOTxt = ObjectRE.GroupNumberFromName("innerText");\r
+\r
+                                       string globalText = mO.Groups[iOTxt].Value;\r
+\r
+                                       if( mO.Groups[iOTxt].Index <= nFirstUL)\r
+                                               ParseGlobalSettings( globalText, chmFile );\r
+                               }\r
+\r
+                               // parse toc tree\r
+                               string innerText = m.Groups["innerText"].Value;\r
+\r
+                               innerText = innerText.Replace("(", "&#040;");\r
+                               innerText = innerText.Replace(")", "&#041;");\r
+                               innerText = Regex.Replace(innerText, RE_ULOpening, "(", RegexOptions.IgnoreCase);\r
+                               innerText = Regex.Replace(innerText, RE_ULClosing, ")", RegexOptions.IgnoreCase);\r
+                       \r
+                               ParseTree( innerText, null, tocList, chmFile );\r
+                               \r
+                       }\r
+\r
+                       return tocList;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if the hhc file contains a global object tag.\r
+               /// </summary>\r
+               /// <param name="hhcFile">string content of the hhc file</param>\r
+               /// <param name="chmFile">chm file</param>\r
+               /// <returns>true if the hhc content contains a global object tag</returns>\r
+               public static bool HasGlobalObjectTag(string hhcFile, CHMFile chmFile)\r
+               {\r
+                       bool bRet = false;\r
+\r
+                       ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+\r
+                       int innerTextIdx = ulRE.GroupNumberFromName("innerText");\r
+\r
+                       if( ulRE.IsMatch(hhcFile, 0) )\r
+                       {\r
+                               Match m = ulRE.Match(hhcFile, 0);\r
+\r
+                               int nFirstUL = 0;\r
+\r
+                               nFirstUL = hhcFile.ToLower().IndexOf("<ul>");\r
+\r
+                               if(nFirstUL == -1)\r
+                                       nFirstUL = hhcFile.ToLower().IndexOf("<il>");\r
+\r
+                               if( ObjectRE.IsMatch(hhcFile, 0) ) // first object block contains information types and categories\r
+                               {\r
+                                       Match mO = ObjectRE.Match(hhcFile, 0);\r
+                                       int iOTxt = ObjectRE.GroupNumberFromName("innerText");\r
+\r
+                                       string globalText = mO.Groups[iOTxt].Value;\r
+\r
+                                       if( mO.Groups[iOTxt].Index <= nFirstUL)\r
+                                               bRet = true;\r
+                               }\r
+                       }\r
+\r
+                       return bRet;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets true if the previously done parsing found merge-links\r
+               /// </summary>\r
+               public static bool HasMergeLinks\r
+               {\r
+                       get \r
+                       {\r
+                               if(_mergeItems==null)\r
+                                       return false;\r
+\r
+                               return _mergeItems.Count > 0;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets all TOCItem references which are holding merge-links\r
+               /// </summary>\r
+               public static ArrayList MergeItems\r
+               {\r
+                       get { return _mergeItems; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Recursively parses a sitemap tree\r
+               /// </summary>\r
+               /// <param name="text">content text</param>\r
+               /// <param name="parent">Parent for all read items</param>\r
+               /// <param name="arrNodes">arraylist which receives the extracted nodes</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static void ParseTree( string text, TOCItem parent, ArrayList arrNodes, CHMFile chmFile )\r
+               {\r
+                       string strPreItems="", strPostItems="";\r
+                       string innerText = "";\r
+\r
+                       int nIndex = 0;\r
+\r
+                       while( NestedRE.IsMatch(text, nIndex) )\r
+                       {\r
+                               Match m = NestedRE.Match(text, nIndex);\r
+\r
+                               innerText = m.Value.Substring( 1, m.Length-2);\r
+\r
+                               strPreItems = text.Substring(nIndex,m.Index-nIndex);\r
+\r
+                               ParseItems(strPreItems, parent, arrNodes, chmFile);\r
+\r
+                               if((arrNodes.Count>0) && (innerText.Length > 0) )\r
+                               {\r
+                                       TOCItem p = ((TOCItem)(arrNodes[arrNodes.Count-1]));\r
+                                       ParseTree( innerText, p, p.Children, chmFile );\r
+                               }\r
+\r
+                               nIndex = m.Index+m.Length;\r
+                       } \r
+\r
+                       if( nIndex == 0)\r
+                       {\r
+                               strPostItems = text.Substring(nIndex, text.Length-nIndex);\r
+                               ParseItems(strPostItems, parent, arrNodes, chmFile);\r
+                       } \r
+                       else if( nIndex < text.Length-1)\r
+                       {\r
+                               strPostItems = text.Substring(nIndex, text.Length-nIndex);\r
+                               ParseTree(strPostItems, parent, arrNodes, chmFile);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Parses tree nodes from the text\r
+               /// </summary>\r
+               /// <param name="itemstext">text containing the items</param>\r
+               /// <param name="parent">Parent for all read items</param>\r
+               /// <param name="arrNodes">arraylist where the nodes should be added</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static void ParseItems( string itemstext, TOCItem parent, ArrayList arrNodes, CHMFile chmFile)\r
+               {\r
+                       int innerTextIdx = ObjectRE.GroupNumberFromName("innerText");\r
+                       int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");\r
+\r
+                       // get group-name indexes\r
+                       int nameIndex = AttributesRE.GroupNumberFromName("attributeName");\r
+                       int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");\r
+                       int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");\r
+\r
+                       int nObjStartIndex = 0;\r
+\r
+                       while( ObjectRE.IsMatch(itemstext, nObjStartIndex) )\r
+                       {\r
+                               Match m = ObjectRE.Match(itemstext, nObjStartIndex);\r
+\r
+                               string innerText = m.Groups[innerTextIdx].Value;\r
+\r
+                               TOCItem tocItem = new TOCItem();\r
+                               tocItem.TocMode = DataMode.TextBased;\r
+                               tocItem.AssociatedFile = chmFile;\r
+                               tocItem.Parent = parent;\r
+\r
+                               // read parameters\r
+                               int nParamIndex = 0;\r
+\r
+                               while( ParamRE.IsMatch(innerText, nParamIndex) )\r
+                               {\r
+                                       Match mP = ParamRE.Match(innerText, nParamIndex);\r
+                                       \r
+                                       string innerP = mP.Groups[innerPTextIdx].Value;\r
+\r
+                                       string paramName = "";\r
+                                       string paramValue = "";\r
+\r
+                                       int nAttrIdx = 0;\r
+\r
+                                       while( AttributesRE.IsMatch( innerP, nAttrIdx ) )\r
+                                       {\r
+                                               Match mA = AttributesRE.Match(innerP, nAttrIdx);\r
+\r
+                                               string attributeName = mA.Groups[nameIndex].Value;\r
+                                               string attributeValue = mA.Groups[valueIndex].Value;\r
+                                               string attributeTD = mA.Groups[tdIndex].Value;\r
+\r
+                                               if(attributeTD.Length > 0)\r
+                                               {\r
+                                                       // delete the trailing textqualifier\r
+                                                       if( attributeValue.Length > 0)\r
+                                                       {\r
+                                                               int ltqi = attributeValue.LastIndexOf( attributeTD );\r
+\r
+                                                               if(ltqi >= 0)\r
+                                                               {\r
+                                                                       attributeValue = attributeValue.Substring(0,ltqi);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+\r
+                                               if( attributeName.ToLower() == "name")\r
+                                               {\r
+                                                       paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                               }\r
+\r
+                                               if( attributeName.ToLower() == "value")\r
+                                               {\r
+                                                       paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                                       // delete trailing /\r
+                                                       while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))\r
+                                                               paramValue = paramValue.Substring(0,paramValue.Length-1);\r
+                                                       \r
+                                               }\r
+\r
+                                               nAttrIdx = mA.Index+mA.Length;\r
+                                       }\r
+\r
+                                       tocItem.Params[paramName] = paramValue;\r
+                                       switch(paramName.ToLower())\r
+                                       {\r
+                                               case "name":\r
+                                               {\r
+                                                       tocItem.Name = paramValue;\r
+                                               };break;\r
+                                               case "local":\r
+                                               {\r
+                                                       tocItem.Local = paramValue.Replace("../", "").Replace("./", "");\r
+                                               };break;\r
+                                               case "imagenumber":\r
+                                               {\r
+                                                       tocItem.ImageIndex = Int32.Parse(paramValue);\r
+                                                       tocItem.ImageIndex-=1;\r
+\r
+                                                       int nFolderAdd = 0;\r
+\r
+                                                       if((chmFile != null) && (chmFile.ImageTypeFolder))\r
+                                                       {\r
+                                                               // get the value which should be added, to display folders instead of books\r
+                                                               if(HtmlHelpSystem.UseHH2TreePics) \r
+                                                                       nFolderAdd = 8;\r
+                                                               else\r
+                                                                       nFolderAdd = 4;\r
+                                                       }\r
+\r
+                                                       if(tocItem.ImageIndex%2 != 0)\r
+                                                       {\r
+                                                               if(tocItem.ImageIndex==1)\r
+                                                                       tocItem.ImageIndex=0;\r
+                                                       }\r
+                                                       if(HtmlHelpSystem.UseHH2TreePics)\r
+                                                               if( tocItem.ImageIndex == 0)\r
+                                                                       tocItem.ImageIndex = TOCItem.STD_FOLDER_HH2+nFolderAdd;\r
+                                               };break;\r
+                                               case "merge": // this item contains topics or a full TOC from a merged CHM\r
+                                               {\r
+                                                       tocItem.MergeLink = paramValue;\r
+\r
+                                                       // "register" this item as merge-link\r
+                                                       if(_mergeItems==null)\r
+                                                               _mergeItems=new ArrayList();\r
+\r
+                                                       _mergeItems.Add(tocItem);\r
+\r
+                                               };break;\r
+                                               case "type":    // information type assignment for item\r
+                                               {\r
+                                                       tocItem.InfoTypeStrings.Add( paramValue );\r
+                                               };break;\r
+                                       }\r
+\r
+                                       nParamIndex = mP.Index+mP.Length;\r
+                               }\r
+\r
+                               tocItem.ChmFile = chmFile.ChmFilePath;\r
+\r
+                               if(tocItem.MergeLink.Length > 0)\r
+                               {\r
+                                       if(_lastTopicItem != null)\r
+                                       {\r
+                                               tocItem.Parent = _lastTopicItem;\r
+                                               _lastTopicItem.Children.Add(tocItem);\r
+                                       }\r
+                                       else\r
+                                               arrNodes.Add( tocItem );\r
+                               } \r
+                               else \r
+                               {\r
+                                       _lastTopicItem = tocItem;\r
+                                       arrNodes.Add( tocItem );\r
+                               }\r
+\r
+                               nObjStartIndex = m.Index+m.Length;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Parses the very first &lt;OBJECT&gt; tag in the sitemap file and extracts \r
+               /// information types and categories.\r
+               /// </summary>\r
+               /// <param name="sText">text of the object tag</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static void ParseGlobalSettings(string sText, CHMFile chmFile)\r
+               {\r
+                       int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");\r
+\r
+                       // get group-name indexes\r
+                       int nameIndex = AttributesRE.GroupNumberFromName("attributeName");\r
+                       int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");\r
+                       int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");\r
+\r
+                       // read parameters\r
+                       int nParamIndex = 0;\r
+                       \r
+                       // 0... unknown\r
+                       // 1... inclusinve info type name\r
+                       // 2... exclusive info type name\r
+                       // 3... hidden info type name\r
+                       // 4... category name\r
+                       // 5... incl infotype name for category\r
+                       // 6... excl infotype name for category\r
+                       // 7... hidden infotype name for category\r
+                       int prevItem = 0;\r
+\r
+                       string sName = "";\r
+                       string sDescription = "";\r
+                       string curCategory = "";\r
+\r
+                       while( ParamRE.IsMatch(sText, nParamIndex) )\r
+                       {\r
+                               Match mP = ParamRE.Match(sText, nParamIndex);\r
+                                       \r
+                               string innerP = mP.Groups[innerPTextIdx].Value;\r
+\r
+                               string paramName = "";\r
+                               string paramValue = "";\r
+\r
+                               int nAttrIdx = 0;\r
+\r
+                               while( AttributesRE.IsMatch( innerP, nAttrIdx ) )\r
+                               {\r
+                                       Match mA = AttributesRE.Match(innerP, nAttrIdx);\r
+\r
+                                       string attributeName = mA.Groups[nameIndex].Value;\r
+                                       string attributeValue = mA.Groups[valueIndex].Value;\r
+                                       string attributeTD = mA.Groups[tdIndex].Value;\r
+\r
+                                       if(attributeTD.Length > 0)\r
+                                       {\r
+                                               // delete the trailing textqualifier\r
+                                               if( attributeValue.Length > 0)\r
+                                               {\r
+                                                       int ltqi = attributeValue.LastIndexOf( attributeTD );\r
+\r
+                                                       if(ltqi >= 0)\r
+                                                       {\r
+                                                               attributeValue = attributeValue.Substring(0,ltqi);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       if( attributeName.ToLower() == "name")\r
+                                       {\r
+                                               paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                       }\r
+\r
+                                       if( attributeName.ToLower() == "value")\r
+                                       {\r
+                                               paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                               // delete trailing /\r
+                                               while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))\r
+                                                       paramValue = paramValue.Substring(0,paramValue.Length-1);\r
+                                                       \r
+                                       }\r
+\r
+                                       nAttrIdx = mA.Index+mA.Length;\r
+                               }\r
+\r
+                               switch(paramName.ToLower())\r
+                               {\r
+                                       case "savetype":        // inclusive information type name\r
+                                       {\r
+                                               prevItem = 1;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "savetypedesc":    // description of information type\r
+                                       {\r
+                                               InformationTypeMode mode = InformationTypeMode.Inclusive;\r
+                                               sDescription = paramValue;\r
+\r
+                                               if( prevItem == 1)\r
+                                                       mode = InformationTypeMode.Inclusive;\r
+                                               if( prevItem == 2)\r
+                                                       mode = InformationTypeMode.Exclusive;\r
+                                               if( prevItem == 3)\r
+                                                       mode = InformationTypeMode.Hidden;\r
+\r
+                                               if( chmFile.GetInformationType( sName ) == null)\r
+                                               {\r
+                                                       // check if the HtmlHelpSystem already holds such an information type\r
+                                                       if( chmFile.SystemInstance.GetInformationType( sName ) == null)\r
+                                                       {\r
+                                                               // info type not found yet\r
+\r
+                                                               InformationType newType = new InformationType(sName, sDescription, mode);\r
+                                                               chmFile.InformationTypes.Add(newType);\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               InformationType sysType = chmFile.SystemInstance.GetInformationType( sName );\r
+                                                               chmFile.InformationTypes.Add( sysType );\r
+                                                       }\r
+                                               }\r
+\r
+                                               prevItem = 0;\r
+                                       };break;\r
+                                       case "saveexclusive":   // exclusive information type name\r
+                                       {\r
+                                               prevItem = 2;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "savehidden":      // hidden information type name\r
+                                       {\r
+                                               prevItem = 3;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "category":        // category name\r
+                                       {\r
+                                               prevItem = 4;\r
+                                               sName = paramValue;\r
+                                               curCategory = sName;\r
+                                       };break;\r
+                                       case "categorydesc":    // category description\r
+                                       {\r
+                                               sDescription = paramValue;\r
+\r
+                                               if( chmFile.GetCategory( sName ) == null)\r
+                                               {\r
+                                                       // check if the HtmlHelpSystem already holds such a category\r
+                                                       if( chmFile.SystemInstance.GetCategory( sName ) == null)\r
+                                                       {\r
+                                                               // add category \r
+                                                               Category newCat = new Category(sName, sDescription);\r
+                                                               chmFile.Categories.Add(newCat);\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               Category sysCat = chmFile.SystemInstance.GetCategory( sName );\r
+                                                               chmFile.Categories.Add( sysCat );\r
+                                                       }\r
+                                               }\r
+\r
+                                               prevItem = 0;\r
+                                       };break;\r
+                                       case "type":    // inclusive information type which is member of the previously read category\r
+                                       {\r
+                                               prevItem = 5;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "typedesc":        // description of type for category\r
+                                       {\r
+                                               sDescription = paramValue;\r
+                                               Category cat = chmFile.GetCategory( curCategory );\r
+\r
+                                               if( cat != null)\r
+                                               {\r
+                                                       // category found\r
+                                                       InformationType infoType = chmFile.GetInformationType( sName );\r
+                                                       \r
+                                                       if( infoType != null)\r
+                                                       {\r
+                                                               if( !cat.ContainsInformationType(infoType))\r
+                                                               {\r
+                                                                       infoType.SetCategoryFlag(true);\r
+                                                                       cat.AddInformationType(infoType);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+\r
+                                               prevItem = 0;\r
+                                       };break;\r
+                                       case "typeexclusive":   // exclusive information type which is member of the previously read category\r
+                                       {\r
+                                               prevItem = 6;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "typehidden":      // hidden information type which is member of the previously read category\r
+                                       {\r
+                                               prevItem = 7;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       default:\r
+                                       {\r
+                                               prevItem = 0;\r
+                                               sName = "";\r
+                                               sDescription = "";\r
+                                       };break;\r
+                               }\r
+\r
+                               nParamIndex = mP.Index+mP.Length;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs b/irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs
new file mode 100644 (file)
index 0000000..bdb988a
--- /dev/null
@@ -0,0 +1,220 @@
+using System;\r
+using System.Collections;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{      \r
+       /// <summary>\r
+       /// The class <c>HHCParser</c> implements a parser for HHC contents files.\r
+       /// </summary>\r
+       // internal sealed class HHCParser : IHHCParser\r
+       public class HHCParser2\r
+       {\r
+               static private string m_text1="";\r
+               static private string m_text2="";\r
+               static private int m_CurrentPos=0;\r
+\r
+               /// <summary>\r
+               /// Parses a HHC file and returns an ArrayList with the table of contents (TOC) tree\r
+               /// </summary>\r
+               /// <param name="hhcFile">string content of the hhc file</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               /// <returns>Returns an ArrayList with the table of contents (TOC) tree</returns>\r
+               public static ArrayList ParseHHC(string hhcFile, CHMFile chmFile)\r
+               {\r
+                       DateTime StartTime=DateTime.Now;\r
+\r
+                       ArrayList tocList = new ArrayList();\r
+\r
+                       m_text2=hhcFile;\r
+                       m_text1=hhcFile.ToLower();\r
+\r
+                       int idx=m_text1.IndexOf("<ul>");\r
+                       if (idx==-1)\r
+                               return null;                    \r
+                       m_CurrentPos=idx+4;\r
+\r
+                       ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+\r
+                       ParseTree(tocList,chmFile);\r
+\r
+                       DateTime EndTime=DateTime.Now;\r
+                       TimeSpan Diff=EndTime-StartTime;\r
+                       string x=Diff.ToString();\r
+\r
+                       return tocList;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Recursively parses a sitemap tree\r
+               /// </summary>\r
+               /// <param name="text">content text</param>\r
+               /// <param name="arrNodes">arraylist which receives the extracted nodes</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               static private void ParseTree( ArrayList arrNodes, CHMFile chmFile )\r
+               {       \r
+                       bool bProcessing=true;\r
+                       do\r
+                       {\r
+                               bProcessing=false;\r
+\r
+                               // Indent\r
+                               int idxa=m_text1.IndexOf("<ul>",m_CurrentPos);\r
+                               int idxb=m_text1.IndexOf("<li>",m_CurrentPos);\r
+                               int idxc=m_text1.IndexOf("</ul>",m_CurrentPos);\r
+                               \r
+                               if ((idxa<idxb) && (idxa<idxc) && (idxa>-1))                            \r
+                               {\r
+                                       bProcessing=true;\r
+                                       m_CurrentPos=idxa+4;\r
+                                       if (arrNodes.Count<1)\r
+                                       {\r
+                                               ParseTree(arrNodes,chmFile);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               ParseTree(((TOCItem)(arrNodes[arrNodes.Count-1])).Children,chmFile);\r
+                                       }\r
+                                       continue;\r
+                               }\r
+\r
+                               // new item\r
+                               if ((idxb<idxa) && (idxb<idxc) && (idxb>-1))\r
+                               {\r
+\r
+                                       bProcessing=true;\r
+                                       m_CurrentPos=idxb+4;\r
+                               \r
+                                       int idx2=m_text1.IndexOf("<object",m_CurrentPos);\r
+                                       if (idx2!=-1)\r
+                                       {                               \r
+                                               int idx3=m_text1.IndexOf("</object>",idx2+7);\r
+                                               if (idx3!=-1)\r
+                                               {\r
+                                                       string text=m_text2.Substring(idx2,idx3-idx2);\r
+\r
+                                                       m_CurrentPos=idx3+9;                                                    \r
+\r
+                                                       // Parse items in text.\r
+                                                       TOCItem tocItem=ParseItems(text, chmFile);                                                      \r
+                                                       if (tocItem!=null)\r
+                                                       {\r
+                                                               arrNodes.Add(tocItem);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               // Undent\r
+                               if ((idxc<idxa) && (idxc<idxb) && (idxc>-1))\r
+                               {\r
+                                       m_CurrentPos=idxc+5;\r
+                                       bProcessing=true;\r
+                                       return;\r
+                               }                               \r
+                       }\r
+                       while (bProcessing);                    \r
+               }\r
+\r
+               \r
+               private static string RE_ParamBoundaries = @"\<param(?<innerText>.*?)\>";\r
+               private const string RE_QuoteAttributes = @"( |\t)*(?<attributeName>[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?<attributeTD>[\""\'])?(?<attributeValue>.*?(?(attributeTD)\k<attributeTD>|([\s>]|.$)))";            \r
+               private static Regex ParamRE;\r
+               private static Regex AttributesRE;\r
+\r
+               /// <summary>\r
+               /// Parses tree nodes from the text\r
+               /// </summary>\r
+               /// <param name="itemstext">text containing the items</param>\r
+               /// <param name="arrNodes">arraylist where the nodes should be added</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static TOCItem ParseItems( string itemstext, CHMFile chmFile)\r
+               {                       \r
+                       int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");\r
+\r
+                       // get group-name indexes\r
+                       int nameIndex = AttributesRE.GroupNumberFromName("attributeName");\r
+                       int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");\r
+                       int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");\r
+\r
+                       TOCItem tocItem = new TOCItem();\r
+\r
+                       // read parameters\r
+                       int nParamIndex = 0;\r
+\r
+                       while( ParamRE.IsMatch(itemstext, nParamIndex) )\r
+                       {\r
+                               Match mP = ParamRE.Match(itemstext, nParamIndex);\r
+                               \r
+                               string innerP = mP.Groups[innerPTextIdx].Value;\r
+\r
+                               string paramName = "";\r
+                               string paramValue = "";\r
+\r
+                               int nAttrIdx = 0;\r
+\r
+                               while( AttributesRE.IsMatch( innerP, nAttrIdx ) )\r
+                               {\r
+                                       Match mA = AttributesRE.Match(innerP, nAttrIdx);\r
+\r
+                                       string attributeName = mA.Groups[nameIndex].Value;\r
+                                       string attributeValue = mA.Groups[valueIndex].Value;\r
+                                       string attributeTD = mA.Groups[tdIndex].Value;\r
+\r
+                                       if(attributeTD.Length > 0)\r
+                                       {\r
+                                               // delete the trailing textqualifier\r
+                                               if( attributeValue.Length > 0)\r
+                                               {\r
+                                                       int ltqi = attributeValue.LastIndexOf( attributeTD );\r
+\r
+                                                       if(ltqi >= 0)\r
+                                                       {\r
+                                                               attributeValue = attributeValue.Substring(0,ltqi);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       if( attributeName.ToLower() == "name")\r
+                                       {\r
+                                               paramName = attributeValue;\r
+                                       }\r
+\r
+                                       if( attributeName.ToLower() == "value")\r
+                                       {\r
+                                               paramValue = attributeValue;\r
+                                       }\r
+\r
+                                       nAttrIdx = mA.Index+mA.Length;\r
+                               }\r
+\r
+                               tocItem.Params[paramName] = paramValue;\r
+                               switch(paramName.ToLower())\r
+                               {\r
+                                       case "name":\r
+                                       {\r
+                                               tocItem.Name = paramValue;\r
+                                       };break;\r
+                                       case "local":\r
+                                       {\r
+                                               tocItem.Local = paramValue;\r
+                                       };break;\r
+                                       case "imagenumber":\r
+                                       {\r
+                                               tocItem.ImageIndex = Int32.Parse(paramValue);\r
+\r
+                                               if( tocItem.ImageIndex == 2)\r
+                                                       tocItem.ImageIndex = TOCItem.STD_FOLDER_HH1;\r
+                                       };break;\r
+                               }\r
+\r
+                               nParamIndex = mP.Index+mP.Length;\r
+                       }\r
+\r
+                       tocItem.ChmFile = chmFile.ChmFilePath;\r
+                       return tocItem;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/HHKParser.cs b/irc/TechBot/CHMLibrary/CHMDecoding/HHKParser.cs
new file mode 100644 (file)
index 0000000..63f1c13
--- /dev/null
@@ -0,0 +1,550 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>HHKParser</c> implements a parser for HHK contents files.\r
+       /// </summary>\r
+       internal sealed class HHKParser\r
+       {\r
+               /// <summary>\r
+               /// regular expressions for replacing the sitemap boundary tags \r
+               /// </summary>\r
+               private static string RE_ULOpening = @"\<ul\>";  // will be replaced by a '(' for nested parsing\r
+               private static string RE_ULClosing = @"\</ul\>"; // will be replaced by a ')' for nested parsing\r
+\r
+               /// <summary>\r
+               /// Matching ul-tags\r
+               /// </summary>\r
+               private static string RE_ULBoundaries = @"\<ul\>(?<innerText>.*)\</ul\>";\r
+               /// <summary>\r
+               /// Matching the nested tree structure.\r
+               /// </summary>\r
+               private static string RE_NestedBoundaries = @"\( (?> [^()]+ | \( (?<DEPTH>) | \) (?<-DEPTH>) )* (?(DEPTH)(?!)) \)";\r
+               /// <summary>\r
+               /// Matching object-tags\r
+               /// </summary>\r
+               private static string RE_ObjectBoundaries = @"\<object(?<innerText>.*?)\</object\>";\r
+               /// <summary>\r
+               /// Matching param tags\r
+               /// </summary>\r
+               private static string RE_ParamBoundaries = @"\<param(?<innerText>.*?)\>";\r
+               /// <summary>\r
+               /// Extracting tag attributes\r
+               /// </summary>\r
+               private const string RE_QuoteAttributes = @"( |\t)*(?<attributeName>[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?<attributeTD>[\""\'])?(?<attributeValue>.*?(?(attributeTD)\k<attributeTD>|([\s>]|.$)))";\r
+\r
+               /// <summary>\r
+               /// private regular expressionobjects \r
+               /// </summary>\r
+               private static Regex ulRE;\r
+               private static Regex NestedRE;\r
+               private static Regex ObjectRE;\r
+               private static Regex ParamRE;\r
+               private static Regex AttributesRE;\r
+\r
+               /// <summary>\r
+               /// Parses a HHK file and returns an ArrayList with the index tree\r
+               /// </summary>\r
+               /// <param name="hhkFile">string content of the hhk file</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               /// <returns>Returns an ArrayList with the index tree</returns>\r
+               public static ArrayList ParseHHK(string hhkFile, CHMFile chmFile)\r
+               {\r
+                       ArrayList indexList = new ArrayList();\r
+\r
+                       ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       NestedRE = new Regex(RE_NestedBoundaries, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+                       AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);\r
+\r
+                       int innerTextIdx = ulRE.GroupNumberFromName("innerText");\r
+\r
+                       if( ulRE.IsMatch(hhkFile, 0) )\r
+                       {\r
+                               Match m = ulRE.Match(hhkFile, 0);\r
+\r
+                               if( ObjectRE.IsMatch(hhkFile, 0) ) // first object block contains information types and categories\r
+                               {\r
+                                       Match mO = ObjectRE.Match(hhkFile, 0);\r
+                                       int iOTxt = ObjectRE.GroupNumberFromName("innerText");\r
+\r
+                                       string globalText = mO.Groups[iOTxt].Value;\r
+\r
+                                       ParseGlobalSettings( globalText, chmFile );\r
+                               }\r
+\r
+                               string innerText = m.Groups["innerText"].Value;\r
+\r
+                               innerText = innerText.Replace("(", "&#040;");\r
+                               innerText = innerText.Replace(")", "&#041;");\r
+                               innerText = Regex.Replace(innerText, RE_ULOpening, "(", RegexOptions.IgnoreCase);\r
+                               innerText = Regex.Replace(innerText, RE_ULClosing, ")", RegexOptions.IgnoreCase);\r
+                       \r
+                               ParseTree( innerText, null, indexList, chmFile );\r
+                       }\r
+\r
+                       return indexList;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Recursively parses a sitemap tree\r
+               /// </summary>\r
+               /// <param name="text">content text</param>\r
+               /// <param name="parent">Parent for all read items</param>\r
+               /// <param name="arrNodes">arraylist which receives the extracted nodes</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static void ParseTree( string text, IndexItem parent, ArrayList arrNodes, CHMFile chmFile )\r
+               {\r
+                       string strPreItems="", strPostItems="";\r
+                       string innerText = "";\r
+\r
+                       int nIndex = 0;\r
+\r
+                       while( NestedRE.IsMatch(text, nIndex) )\r
+                       {\r
+                               Match m = NestedRE.Match(text, nIndex);\r
+\r
+                               innerText = m.Value.Substring( 1, m.Length-2);\r
+\r
+                               strPreItems = text.Substring(nIndex,m.Index-nIndex);\r
+\r
+                               ParseItems(strPreItems, parent, arrNodes, chmFile);\r
+\r
+                               if((arrNodes.Count>0) && (innerText.Length > 0) )\r
+                               {\r
+                                       IndexItem p = ((IndexItem)(arrNodes[arrNodes.Count-1]));\r
+                                       ParseTree( innerText, p, arrNodes, chmFile );\r
+                               }\r
+\r
+                               nIndex = m.Index+m.Length;\r
+                       } \r
+\r
+                       if( nIndex == 0)\r
+                       {\r
+                               strPostItems = text.Substring(nIndex, text.Length-nIndex);\r
+                               ParseItems(strPostItems, parent, arrNodes, chmFile);\r
+                       } \r
+                       else if( nIndex < text.Length-1)\r
+                       {\r
+                               strPostItems = text.Substring(nIndex, text.Length-nIndex);\r
+                               ParseTree(strPostItems, parent, arrNodes, chmFile);\r
+                       }\r
+               }\r
+\r
+\r
+               /// <summary>\r
+               /// Parses nodes from the text\r
+               /// </summary>\r
+               /// <param name="itemstext">text containing the items</param>\r
+               /// <param name="parentItem">parent index item</param>\r
+               /// <param name="arrNodes">arraylist where the nodes should be added</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static void ParseItems( string itemstext, IndexItem parentItem, ArrayList arrNodes, CHMFile chmFile)\r
+               {\r
+                       int innerTextIdx = ObjectRE.GroupNumberFromName("innerText");\r
+                       int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");\r
+\r
+                       // get group-name indexes\r
+                       int nameIndex = AttributesRE.GroupNumberFromName("attributeName");\r
+                       int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");\r
+                       int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");\r
+\r
+                       int nObjStartIndex = 0;\r
+                       int nLastObjStartIndex = 0;\r
+                       string sKeyword = "";\r
+\r
+                       while( ObjectRE.IsMatch(itemstext, nObjStartIndex) )\r
+                       {\r
+                               Match m = ObjectRE.Match(itemstext, nObjStartIndex);\r
+\r
+                               string innerText = m.Groups[innerTextIdx].Value;\r
+\r
+                               IndexItem idxItem = new IndexItem();\r
+\r
+                               // read parameters\r
+                               int nParamIndex = 0;\r
+                               int nNameCnt = 0;\r
+\r
+                               string paramTitle = "";\r
+                               string paramLocal = "";\r
+                               bool bAdded = false;\r
+\r
+                               while( ParamRE.IsMatch(innerText, nParamIndex) )\r
+                               {\r
+                                       Match mP = ParamRE.Match(innerText, nParamIndex);\r
+                                       \r
+                                       string innerP = mP.Groups[innerPTextIdx].Value;\r
+\r
+                                       string paramName = "";\r
+                                       string paramValue = "";\r
+\r
+                                       int nAttrIdx = 0;\r
+                                       //sKeyword = "";\r
+\r
+                                       while( AttributesRE.IsMatch( innerP, nAttrIdx ) )\r
+                                       {\r
+                                               Match mA = AttributesRE.Match(innerP, nAttrIdx);\r
+\r
+                                               string attributeName = mA.Groups[nameIndex].Value;\r
+                                               string attributeValue = mA.Groups[valueIndex].Value;\r
+                                               string attributeTD = mA.Groups[tdIndex].Value;\r
+\r
+                                               if(attributeTD.Length > 0)\r
+                                               {\r
+                                                       // delete the trailing textqualifier\r
+                                                       if( attributeValue.Length > 0)\r
+                                                       {\r
+                                                               int ltqi = attributeValue.LastIndexOf( attributeTD );\r
+\r
+                                                               if(ltqi >= 0)\r
+                                                               {\r
+                                                                       attributeValue = attributeValue.Substring(0,ltqi);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+\r
+                                               if( attributeName.ToLower() == "name")\r
+                                               {\r
+                                                       paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                                       nNameCnt++;\r
+                                               }\r
+\r
+                                               if( attributeName.ToLower() == "value")\r
+                                               {\r
+                                                       paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                                       // delete trailing /\r
+                                                       while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))\r
+                                                               paramValue = paramValue.Substring(0,paramValue.Length-1);\r
+                                               }\r
+\r
+                                               nAttrIdx = mA.Index+mA.Length;\r
+                                       }\r
+\r
+                                       if( nNameCnt == 1) // first "Name" param = keyword\r
+                                       {\r
+                                               sKeyword = "";\r
+\r
+                                               if(parentItem != null)\r
+                                                       sKeyword = parentItem.KeyWordPath + ",";\r
+\r
+                                               string sOldKW = sKeyword;\r
+\r
+                                               sKeyword += paramValue;\r
+\r
+                                               IndexItem idxFind = FindByKeyword(arrNodes, sKeyword);\r
+\r
+                                               if(idxFind != null)\r
+                                               {\r
+                                                       idxItem = idxFind;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if( sKeyword.Split(new char[] {','}).Length > 1 )\r
+                                                       {\r
+                                                               idxItem.CharIndex = sKeyword.Length - paramValue.Length;\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               sKeyword = paramValue;\r
+                                                               sOldKW = sKeyword;\r
+                                                               idxItem.CharIndex = 0;\r
+                                                       }\r
+\r
+                                                       idxItem.KeyWordPath = sKeyword;\r
+                                                       idxItem.Indent = sKeyword.Split(new char[] {','}).Length - 1;\r
+                                                       idxItem.IsSeeAlso = false;\r
+                                                       \r
+                                                       sKeyword = sOldKW;\r
+                                               }\r
+                                       } \r
+                                       else \r
+                                       {\r
+\r
+                                               if( (nNameCnt > 2) && (paramName.ToLower()=="name") )\r
+                                               {\r
+                                                       bAdded = true;\r
+                                                       IndexTopic idxTopic = new IndexTopic(paramTitle, paramLocal, chmFile.CompileFile, chmFile.ChmFilePath);\r
+\r
+                                                       idxItem.Topics.Add( idxTopic );\r
+\r
+                                                       paramTitle = "";\r
+                                                       paramLocal = "";\r
+                                               } \r
+\r
+                                               switch(paramName.ToLower())\r
+                                               {\r
+                                                       case "name":\r
+                                                               //case "keyword":\r
+                                                       {\r
+                                                               paramTitle = paramValue;\r
+                                                       };break;\r
+                                                       case "local":\r
+                                                       {\r
+                                                               paramLocal = paramValue.Replace("../", "").Replace("./", "");\r
+                                                       };break;\r
+                                                       case "type":    // information type assignment for item\r
+                                                       {\r
+                                                               idxItem.InfoTypeStrings.Add( paramValue );\r
+                                                       };break;\r
+                                                       case "see also":\r
+                                                       {\r
+                                                               idxItem.AddSeeAlso(paramValue);\r
+                                                               idxItem.IsSeeAlso = true;\r
+                                                               bAdded = true;\r
+                                                       };break;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       nParamIndex = mP.Index+mP.Length;\r
+                               }\r
+\r
+                               if(!bAdded)\r
+                               {\r
+                                       bAdded=false;\r
+                                       IndexTopic idxTopic = new IndexTopic(paramTitle, paramLocal, chmFile.CompileFile, chmFile.ChmFilePath);\r
+\r
+                                       idxItem.Topics.Add( idxTopic );\r
+\r
+                                       paramTitle = "";\r
+                                       paramLocal = "";\r
+                               }\r
+\r
+                               idxItem.ChmFile = chmFile;\r
+                               arrNodes.Add( idxItem );\r
+\r
+                               nLastObjStartIndex = nObjStartIndex;\r
+                               nObjStartIndex = m.Index+m.Length;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Searches an index-keyword in the index list\r
+               /// </summary>\r
+               /// <param name="indexList">index list to search</param>\r
+               /// <param name="Keyword">keyword to find</param>\r
+               /// <returns>Returns an <see cref="IndexItem">IndexItem</see> instance if found, otherwise null.</returns>\r
+               private static IndexItem FindByKeyword(ArrayList indexList, string Keyword)\r
+               {\r
+                       foreach(IndexItem curItem in indexList)\r
+                       {\r
+                               if( curItem.KeyWordPath == Keyword)\r
+                                       return curItem;\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Parses the very first &lt;OBJECT&gt; tag in the sitemap file and extracts \r
+               /// information types and categories.\r
+               /// </summary>\r
+               /// <param name="sText">text of the object tag</param>\r
+               /// <param name="chmFile">CHMFile instance</param>\r
+               private static void ParseGlobalSettings(string sText, CHMFile chmFile)\r
+               {\r
+                       int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");\r
+\r
+                       // get group-name indexes\r
+                       int nameIndex = AttributesRE.GroupNumberFromName("attributeName");\r
+                       int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");\r
+                       int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");\r
+\r
+                       // read parameters\r
+                       int nParamIndex = 0;\r
+                       \r
+                       // 0... unknown\r
+                       // 1... inclusinve info type name\r
+                       // 2... exclusive info type name\r
+                       // 3... hidden info type name\r
+                       // 4... category name\r
+                       // 5... incl infotype name for category\r
+                       // 6... excl infotype name for category\r
+                       // 7... hidden infotype name for category\r
+                       int prevItem = 0;\r
+\r
+                       string sName = "";\r
+                       string sDescription = "";\r
+                       string curCategory = "";\r
+\r
+                       while( ParamRE.IsMatch(sText, nParamIndex) )\r
+                       {\r
+                               Match mP = ParamRE.Match(sText, nParamIndex);\r
+                                       \r
+                               string innerP = mP.Groups[innerPTextIdx].Value;\r
+\r
+                               string paramName = "";\r
+                               string paramValue = "";\r
+\r
+                               int nAttrIdx = 0;\r
+\r
+                               while( AttributesRE.IsMatch( innerP, nAttrIdx ) )\r
+                               {\r
+                                       Match mA = AttributesRE.Match(innerP, nAttrIdx);\r
+\r
+                                       string attributeName = mA.Groups[nameIndex].Value;\r
+                                       string attributeValue = mA.Groups[valueIndex].Value;\r
+                                       string attributeTD = mA.Groups[tdIndex].Value;\r
+\r
+                                       if(attributeTD.Length > 0)\r
+                                       {\r
+                                               // delete the trailing textqualifier\r
+                                               if( attributeValue.Length > 0)\r
+                                               {\r
+                                                       int ltqi = attributeValue.LastIndexOf( attributeTD );\r
+\r
+                                                       if(ltqi >= 0)\r
+                                                       {\r
+                                                               attributeValue = attributeValue.Substring(0,ltqi);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       if( attributeName.ToLower() == "name")\r
+                                       {\r
+                                               paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                       }\r
+\r
+                                       if( attributeName.ToLower() == "value")\r
+                                       {\r
+                                               paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values\r
+                                               // delete trailing /\r
+                                               while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))\r
+                                                       paramValue = paramValue.Substring(0,paramValue.Length-1);\r
+                                                       \r
+                                       }\r
+\r
+                                       nAttrIdx = mA.Index+mA.Length;\r
+                               }\r
+\r
+                               switch(paramName.ToLower())\r
+                               {\r
+                                       case "savetype":        // inclusive information type name\r
+                                       {\r
+                                               prevItem = 1;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "savetypedesc":    // description of information type\r
+                                       {\r
+                                               InformationTypeMode mode = InformationTypeMode.Inclusive;\r
+                                               sDescription = paramValue;\r
+\r
+                                               if( prevItem == 1)\r
+                                                       mode = InformationTypeMode.Inclusive;\r
+                                               if( prevItem == 2)\r
+                                                       mode = InformationTypeMode.Exclusive;\r
+                                               if( prevItem == 3)\r
+                                                       mode = InformationTypeMode.Hidden;\r
+\r
+                                               if( chmFile.GetInformationType( sName ) == null)\r
+                                               {\r
+                                                       // check if the HtmlHelpSystem already holds such an information type\r
+                                                       if( chmFile.SystemInstance.GetInformationType( sName ) == null)\r
+                                                       {\r
+                                                               // info type not found yet\r
+\r
+                                                               InformationType newType = new InformationType(sName, sDescription, mode);\r
+                                                               chmFile.InformationTypes.Add(newType);\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               InformationType sysType = chmFile.SystemInstance.GetInformationType( sName );\r
+                                                               chmFile.InformationTypes.Add( sysType );\r
+                                                       }\r
+                                               }\r
+\r
+                                               prevItem = 0;\r
+                                       };break;\r
+                                       case "saveexclusive":   // exclusive information type name\r
+                                       {\r
+                                               prevItem = 2;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "savehidden":      // hidden information type name\r
+                                       {\r
+                                               prevItem = 3;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "category":        // category name\r
+                                       {\r
+                                               prevItem = 4;\r
+                                               sName = paramValue;\r
+                                               curCategory = sName;\r
+                                       };break;\r
+                                       case "categorydesc":    // category description\r
+                                       {\r
+                                               sDescription = paramValue;\r
+\r
+                                               if( chmFile.GetCategory( sName ) == null)\r
+                                               {\r
+                                                       // check if the HtmlHelpSystem already holds such a category\r
+                                                       if( chmFile.SystemInstance.GetCategory( sName ) == null)\r
+                                                       {\r
+                                                               // add category \r
+                                                               Category newCat = new Category(sName, sDescription);\r
+                                                               chmFile.Categories.Add(newCat);\r
+                                                       } \r
+                                                       else \r
+                                                       {\r
+                                                               Category sysCat = chmFile.SystemInstance.GetCategory( sName );\r
+                                                               chmFile.Categories.Add( sysCat );\r
+                                                       }\r
+                                               }\r
+\r
+                                               prevItem = 0;\r
+                                       };break;\r
+                                       case "type":    // inclusive information type which is member of the previously read category\r
+                                       {\r
+                                               prevItem = 5;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "typedesc":        // description of type for category\r
+                                       {\r
+                                               sDescription = paramValue;\r
+                                               Category cat = chmFile.GetCategory( curCategory );\r
+\r
+                                               if( cat != null)\r
+                                               {\r
+                                                       // category found\r
+                                                       InformationType infoType = chmFile.GetInformationType( sName );\r
+                                                       \r
+                                                       if( infoType != null)\r
+                                                       {\r
+                                                               if( !cat.ContainsInformationType(infoType))\r
+                                                               {\r
+                                                                       infoType.SetCategoryFlag(true);\r
+                                                                       cat.AddInformationType(infoType);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+\r
+                                               prevItem = 0;\r
+                                       };break;\r
+                                       case "typeexclusive":   // exclusive information type which is member of the previously read category\r
+                                       {\r
+                                               prevItem = 6;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       case "typehidden":      // hidden information type which is member of the previously read category\r
+                                       {\r
+                                               prevItem = 7;\r
+                                               sName = paramValue;\r
+                                       };break;\r
+                                       default:\r
+                                       {\r
+                                               prevItem = 0;\r
+                                               sName = "";\r
+                                               sDescription = "";\r
+                                       };break;\r
+                               }\r
+\r
+                               nParamIndex = mP.Index+mP.Length;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/TopicEntry.cs b/irc/TechBot/CHMLibrary/CHMDecoding/TopicEntry.cs
new file mode 100644 (file)
index 0000000..16bde7d
--- /dev/null
@@ -0,0 +1,245 @@
+using System;\r
+using System.IO;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>TopicEntry</c> stores the data for one topic entry\r
+       /// </summary>\r
+       internal sealed class TopicEntry\r
+       {\r
+               /// <summary>\r
+               /// Internal member storing the offset of this topic entry\r
+               /// </summary>\r
+               private int _entryOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the index of the binary toc\r
+               /// </summary>\r
+               private int _tocidxOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the string offset of the title\r
+               /// </summary>\r
+               private int _titleOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storuing the urltable offset\r
+               /// </summary>\r
+               private int _urltableOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the visibility mode\r
+               /// </summary>\r
+               private int _visibilityMode = 0;\r
+               /// <summary>\r
+               /// Internal member storing an unknown mode\r
+               /// </summary>\r
+               private int _unknownMode = 0;\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="entryOffset">offset of this entry</param>\r
+               /// <param name="tocidxOffset">offset in the binary toc index</param>\r
+               /// <param name="titleOffset">offset of the title (in the #STRINGS file)</param>\r
+               /// <param name="urltableOffset">offset in the urltable containing the urlstr offset for the url</param>\r
+               /// <param name="visibilityMode">visibility mode 2 indicates not in contents, 6 indicates that it is in the contents, 0/4 something else (unknown)</param>\r
+               /// <param name="unknownMode">0, 2, 4, 8, 10, 12, 16, 32 (unknown)</param>\r
+               public TopicEntry(int entryOffset, int tocidxOffset, int titleOffset, int urltableOffset, int visibilityMode, int unknownMode) :this(entryOffset, tocidxOffset, titleOffset, urltableOffset, visibilityMode, unknownMode, null)\r
+               {\r
+                       \r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="entryOffset">offset of this entry</param>\r
+               /// <param name="tocidxOffset">offset in the binary toc index</param>\r
+               /// <param name="titleOffset">offset of the title (in the #STRINGS file)</param>\r
+               /// <param name="urltableOffset">offset in the urltable containing the urlstr offset for the url</param>\r
+               /// <param name="visibilityMode">visibility mode 2 indicates not in contents, 6 indicates that it is in the contents, 0/4 something else (unknown)</param>\r
+               /// <param name="unknownMode">0, 2, 4, 8, 10, 12, 16, 32 (unknown)</param>\r
+               /// <param name="associatedFile">associated chmfile object</param>\r
+               internal TopicEntry(int entryOffset, int tocidxOffset, int titleOffset, int urltableOffset, int visibilityMode, int unknownMode, CHMFile associatedFile)\r
+               {\r
+                       _entryOffset = entryOffset;\r
+                       _tocidxOffset = tocidxOffset;\r
+                       _titleOffset = titleOffset;\r
+                       _urltableOffset = urltableOffset;\r
+                       _visibilityMode = visibilityMode;\r
+                       _unknownMode = unknownMode;\r
+                       _associatedFile = associatedFile;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal TopicEntry()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _entryOffset );\r
+                       writer.Write( _tocidxOffset );\r
+                       writer.Write( _titleOffset );\r
+                       writer.Write( _urltableOffset );\r
+                       writer.Write( _visibilityMode );\r
+                       writer.Write( _unknownMode );\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       _entryOffset = reader.ReadInt32();\r
+                       _tocidxOffset = reader.ReadInt32();\r
+                       _titleOffset = reader.ReadInt32();\r
+                       _urltableOffset = reader.ReadInt32();\r
+                       _visibilityMode = reader.ReadInt32();\r
+                       _unknownMode = reader.ReadInt32();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Gets the associated chm file\r
+               /// </summary>\r
+               internal CHMFile ChmFile\r
+               {\r
+                       get { return _associatedFile; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the offset of this entry\r
+               /// </summary>\r
+               internal int EntryOffset\r
+               {\r
+                       get { return _entryOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the tocidx offset\r
+               /// </summary>\r
+               internal int TOCIdxOffset\r
+               {\r
+                       get { return _tocidxOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the title offset of the #STRINGS file\r
+               /// </summary>\r
+               internal int TitleOffset\r
+               {\r
+                       get { return _titleOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the urltable offset\r
+               /// </summary>\r
+               internal int UrlTableOffset\r
+               {\r
+                       get { return _urltableOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the title of the topic entry\r
+               /// </summary>\r
+               public string Title\r
+               {\r
+                       get\r
+                       {\r
+                               if( _associatedFile == null)\r
+                                       return String.Empty;\r
+\r
+                               if( _associatedFile.StringsFile == null)\r
+                                       return String.Empty;\r
+\r
+                               string sTemp = (string)_associatedFile.StringsFile[ _titleOffset ];\r
+\r
+                               if(sTemp == null)\r
+                                       return String.Empty;\r
+\r
+                               return sTemp;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the url of the topic\r
+               /// </summary>\r
+               public string Locale\r
+               {\r
+                       get\r
+                       {\r
+                               if( _associatedFile == null)\r
+                                       return String.Empty;\r
+\r
+                               if( _associatedFile.UrltblFile == null)\r
+                                       return String.Empty;\r
+\r
+                               UrlTableEntry utEntry = (UrlTableEntry)_associatedFile.UrltblFile[ _urltableOffset ];\r
+\r
+                               if(utEntry == null)\r
+                                       return String.Empty;\r
+\r
+                               if(utEntry.URL == "")\r
+                                       return String.Empty;\r
+\r
+                               return utEntry.URL;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the URL of this topic\r
+               /// </summary>\r
+               public string URL\r
+               {\r
+                       get\r
+                       {\r
+                               if(Locale.Length <= 0)\r
+                                       return "about:blank";\r
+\r
+                               if( (Locale.ToLower().IndexOf("http://") >= 0) ||\r
+                                       (Locale.ToLower().IndexOf("https://") >= 0) ||\r
+                                       (Locale.ToLower().IndexOf("mailto:") >= 0) ||\r
+                                       (Locale.ToLower().IndexOf("ftp://") >= 0) ||\r
+                                       (Locale.ToLower().IndexOf("ms-its:") >= 0))\r
+                                       return Locale;\r
+\r
+                               return HtmlHelpSystem.UrlPrefix + _associatedFile.ChmFilePath + "::/" + Locale;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the visibility mode\r
+               /// </summary>\r
+               public int VisibilityMode\r
+               {\r
+                       get { return _visibilityMode; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the unknown mode\r
+               /// </summary>\r
+               public int UknownMode\r
+               {\r
+                       get { return _unknownMode; }\r
+               }               \r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/UrlTableEntry.cs b/irc/TechBot/CHMLibrary/CHMDecoding/UrlTableEntry.cs
new file mode 100644 (file)
index 0000000..53391ce
--- /dev/null
@@ -0,0 +1,175 @@
+using System;\r
+using System.IO;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// The class <c>UrlTableEntry</c> stores data for an URL-Table entry\r
+       /// </summary>\r
+       internal sealed class UrlTableEntry\r
+       {\r
+               /// <summary>\r
+               /// Internal member storing the offset of this entry\r
+               /// </summary>\r
+               private int _entryOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing a unique id\r
+               /// </summary>\r
+               private uint _uniqueID = 0;\r
+               /// <summary>\r
+               /// Internal member storing the topics index\r
+               /// </summary>\r
+               private int _topicsIndex = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset in the urlstr table\r
+               /// </summary>\r
+               private int _urlStrOffset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="uniqueID">unique id</param>\r
+               /// <param name="entryOffset">offset of the entry</param>\r
+               /// <param name="topicIndex">topic index</param>\r
+               /// <param name="urlstrOffset">urlstr offset for filename</param>\r
+               public UrlTableEntry(uint uniqueID, int entryOffset, int topicIndex, int urlstrOffset) : this(uniqueID, entryOffset, topicIndex, urlstrOffset, null)\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="uniqueID">unique id</param>\r
+               /// <param name="entryOffset">offset of the entry</param>\r
+               /// <param name="topicIndex">topic index</param>\r
+               /// <param name="urlstrOffset">urlstr offset for filename</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               internal UrlTableEntry(uint uniqueID, int entryOffset, int topicIndex, int urlstrOffset, CHMFile associatedFile)\r
+               {\r
+                       _uniqueID = uniqueID;\r
+                       _entryOffset = entryOffset;\r
+                       _topicsIndex = topicIndex;\r
+                       _urlStrOffset = urlstrOffset;\r
+                       _associatedFile = associatedFile;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               internal UrlTableEntry()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _urlStrOffset );\r
+                       writer.Write( _entryOffset );\r
+                       writer.Write( _topicsIndex );\r
+                       writer.Write( _urlStrOffset );\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       _urlStrOffset = reader.ReadInt32();\r
+                       _entryOffset = reader.ReadInt32();\r
+                       _topicsIndex = reader.ReadInt32();\r
+                       _urlStrOffset = reader.ReadInt32();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Sets the associated CHMFile instance\r
+               /// </summary>\r
+               /// <param name="associatedFile">instance to set</param>\r
+               internal void SetCHMFile(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Gets the unique id of the entry\r
+               /// </summary>\r
+               internal uint UniqueID\r
+               {\r
+                       get {return _uniqueID; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the offset of the entry\r
+               /// </summary>\r
+               internal int EntryOffset\r
+               {\r
+                       get {return _entryOffset; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the topics index\r
+               /// </summary>\r
+               internal int TopicIndex\r
+               {\r
+                       get {return _topicsIndex; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the urlstr offset\r
+               /// </summary>\r
+               internal int UrlstrOffset\r
+               {\r
+                       get { return _urlStrOffset; }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the url of the entry\r
+               /// </summary>\r
+               public string URL\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile == null)\r
+                                       return String.Empty;\r
+\r
+                               if(_associatedFile.UrlstrFile == null)\r
+                                       return String.Empty;\r
+\r
+                               string sTemp = (string)_associatedFile.UrlstrFile.GetURLatOffset( _urlStrOffset );\r
+\r
+                               if( sTemp == null)\r
+                                       return String.Empty;\r
+\r
+                               return sTemp;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the associated topic for this url entry\r
+               /// </summary>\r
+               internal TopicEntry Topic\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile == null)\r
+                                       return null;\r
+\r
+                               if(_associatedFile.TopicsFile == null)\r
+                                       return null;\r
+\r
+                               TopicEntry tentry = _associatedFile.TopicsFile[ _topicsIndex*16 ];\r
+\r
+                               return tentry;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMDecoding/enumerations.cs b/irc/TechBot/CHMLibrary/CHMDecoding/enumerations.cs
new file mode 100644 (file)
index 0000000..0e2d314
--- /dev/null
@@ -0,0 +1,19 @@
+using System;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       /// <summary>\r
+       /// Enumeration for specifying the extraction mode of an toc or index item.\r
+       /// </summary>\r
+       public enum DataMode\r
+       {\r
+               /// <summary>\r
+               /// TextBased - this item comes from a text-based sitemap file\r
+               /// </summary>\r
+               TextBased = 0,\r
+               /// <summary>\r
+               /// Binary - this item was extracted out of a binary stream\r
+               /// </summary>\r
+               Binary = 1\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/CHMLibrary.cmbx b/irc/TechBot/CHMLibrary/CHMLibrary.cmbx
new file mode 100644 (file)
index 0000000..7209cbb
--- /dev/null
@@ -0,0 +1,16 @@
+<Combine fileversion="1.0" name="CHMLibrary" description="">\r
+  <StartMode startupentry="CHMLibrary" single="True">\r
+    <Execute entry="CHMLibrary" type="None" />\r
+  </StartMode>\r
+  <Entries>\r
+    <Entry filename=".\.\CHMLibrary.prjx" />\r
+  </Entries>\r
+  <Configurations active="Debug">\r
+    <Configuration name="Release">\r
+      <Entry name="CHMLibrary" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+    <Configuration name="Debug">\r
+      <Entry name="CHMLibrary" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/CHMLibrary/CHMLibrary.prjx b/irc/TechBot/CHMLibrary/CHMLibrary.prjx
new file mode 100644 (file)
index 0000000..3a888b4
--- /dev/null
@@ -0,0 +1,58 @@
+<Project name="CHMLibrary" standardNamespace="CHMLibrary" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">\r
+  <Contents>\r
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Category.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\ChmFileInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+    <File name=".\HtmlHelpSystem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\HttpUtility.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Index.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IndexItem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IndexTopic.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\InformationType.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\TableOfContents.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\TOCItem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Storage" subtype="Directory" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Storage\CHMStream.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding" subtype="Directory" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\BinaryReaderHelp.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMBtree.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMFile.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMIdxhdr.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMStrings.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMSystem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMTocidx.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMTopics.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMUrlstr.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\CHMUrltable.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\DumpingInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\enumerations.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\FullTextEngine.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\HHCParser2.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\HHCParser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\HHKParser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\TopicEntry.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\CHMDecoding\UrlTableEntry.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+  </Contents>\r
+  <References>\r
+    <Reference type="Project" refto="Compression" localcopy="True" />\r
+  </References>\r
+  <DeploymentInformation target="" script="" strategy="File" />\r
+  <Configuration runwithwarnings="True" name="Debug">\r
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+    <Execution commandlineparameters="" consolepause="False" />\r
+    <Output directory="..\bin\Debug" assembly="CHMLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+  </Configuration>\r
+  <Configurations active="Debug">\r
+    <Configuration runwithwarnings="True" name="Debug">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Debug" assembly="CHMLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+    <Configuration runwithwarnings="True" name="Release">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Release" assembly="CHMLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Project>
\ No newline at end of file
diff --git a/irc/TechBot/CHMLibrary/Category.cs b/irc/TechBot/CHMLibrary/Category.cs
new file mode 100644 (file)
index 0000000..d1ae4e7
--- /dev/null
@@ -0,0 +1,201 @@
+using System;\r
+using System.Collections;\r
+using System.IO;\r
+\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>Category</c> implements methods/properties for handling an information category\r
+       /// </summary>\r
+       /// <remarks>Note: Information types and categories allow users to filter help contents. \r
+       /// They are only supported if using sitemap TOC and/or sitemap Index.</remarks>\r
+       public class Category\r
+       {\r
+               private string _name = "";\r
+               private string _description = "";\r
+               private ArrayList _infoTypes = null;\r
+               private int _referenceCount = 1;\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               public Category() : this("","")\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               /// <param name="name">name of the category</param>\r
+               /// <param name="description">description</param>\r
+               public Category(string name, string description) : this(name, description, new ArrayList())\r
+               {\r
+               }\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               /// <param name="name">name of the category</param>\r
+               /// <param name="description">description</param>\r
+               /// <param name="linkedInformationTypes">Arraylist of InformationType instances which applies to this category</param>\r
+               public Category(string name, string description, ArrayList linkedInformationTypes)\r
+               {\r
+                       _name = name;\r
+                       _description = description;\r
+                       _infoTypes = linkedInformationTypes;\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( _name );\r
+                       writer.Write( _description );\r
+\r
+                       writer.Write( _infoTypes.Count );\r
+\r
+                       for(int i=0; i<_infoTypes.Count;i++)\r
+                       {\r
+                               InformationType curType = _infoTypes[i] as InformationType;\r
+                               writer.Write( curType.Name );\r
+                       }\r
+                               \r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               /// <param name="chmFile">current CHMFile instance which reads from dump</param>\r
+               internal void ReadDump(ref BinaryReader reader, CHMFile chmFile)\r
+               {\r
+                       _name = reader.ReadString();\r
+                       _description = reader.ReadString();\r
+\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(int i=0; i<nCnt; i++)\r
+                       {\r
+                               string sITName = reader.ReadString();\r
+\r
+                               InformationType linkedType = chmFile.GetInformationType( sITName );\r
+\r
+                               if(linkedType != null)\r
+                               {\r
+                                       linkedType.SetCategoryFlag(true);\r
+                                       _infoTypes.Add(linkedType);\r
+                               }\r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Merges the lineked information types from cat into this instance\r
+               /// </summary>\r
+               /// <param name="cat">category instance</param>\r
+               internal void MergeInfoTypes(Category cat)\r
+               {\r
+                       if(cat!=null)\r
+                       {\r
+                               if(cat.InformationTypes.Count > 0)\r
+                               {\r
+                                       for(int i=0;i<cat.InformationTypes.Count;i++)\r
+                                       {\r
+                                               InformationType curType = cat.InformationTypes[i] as InformationType;\r
+\r
+                                               if(!ContainsInformationType(curType.Name))\r
+                                               {\r
+                                                       curType.SetCategoryFlag(true);\r
+                                                       _infoTypes.Add(curType);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the reference count of this information type instance\r
+               /// </summary>\r
+               internal int ReferenceCount\r
+               {\r
+                       get { return _referenceCount; }\r
+                       set { _referenceCount = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the name of the information type\r
+               /// </summary>\r
+               public string Name\r
+               {\r
+                       get { return _name; }\r
+                       set { _name = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the description of the information type\r
+               /// </summary>\r
+               public string Description\r
+               {\r
+                       get { return _description; }\r
+                       set { _name = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an ArrayList with the linked Information types\r
+               /// </summary>\r
+               public ArrayList InformationTypes\r
+               {\r
+                       get { return _infoTypes; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Adds a new information type to the category\r
+               /// </summary>\r
+               /// <param name="type"></param>\r
+               public void AddInformationType(InformationType type)\r
+               {\r
+                       _infoTypes.Add(type);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Removes an information type from the category\r
+               /// </summary>\r
+               /// <param name="type"></param>\r
+               public void RemoveInformationType(InformationType type)\r
+               {\r
+                       _infoTypes.Remove(type);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if the category contains an information type\r
+               /// </summary>\r
+               /// <param name="type">information type instance to check</param>\r
+               /// <returns>Return true if the information type is part of this category</returns>\r
+               public bool ContainsInformationType(InformationType type)\r
+               {\r
+                       return _infoTypes.Contains(type);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if the category contains an information type\r
+               /// </summary>\r
+               /// <param name="name">name of the information type</param>\r
+               /// <returns>Return true if the information type is part of this category</returns>\r
+               public bool ContainsInformationType(string name)\r
+               {\r
+                       for(int i=0;i<_infoTypes.Count;i++)\r
+                       {\r
+                               InformationType curType = _infoTypes[i] as InformationType;\r
+\r
+                               if(curType.Name == name)\r
+                                       return true;\r
+                       }\r
+\r
+                       return false;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/ChmFileInfo.cs b/irc/TechBot/CHMLibrary/ChmFileInfo.cs
new file mode 100644 (file)
index 0000000..15f6a1e
--- /dev/null
@@ -0,0 +1,478 @@
+using System;\r
+using System.Collections;\r
+using System.Text;\r
+using System.IO;\r
+using System.Globalization;\r
+using System.Diagnostics;\r
+using System.ComponentModel;\r
+\r
+using HtmlHelp.ChmDecoding;\r
+// using HtmlHelp.Storage;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>ChmFileInfo</c> only extracts system information from a CHM file. \r
+       /// It doesn't build the index and table of contents.\r
+       /// </summary>\r
+       public class ChmFileInfo\r
+       {\r
+               /// <summary>\r
+               /// Internal member storing the full filename\r
+               /// </summary>\r
+               private string _chmFileName = "";\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Constructor for extrating the file information of the provided file. \r
+               /// The constructor opens the chm-file and reads its system data.\r
+               /// </summary>\r
+               /// <param name="chmFile">full file name which information should be extracted</param>\r
+               public ChmFileInfo(string chmFile)\r
+               {\r
+                       if(!File.Exists(chmFile))\r
+                               throw new ArgumentException("Chm file must exist on disk !", "chmFileName");\r
+\r
+                       if( ! chmFile.ToLower().EndsWith(".chm") )\r
+                               throw new ArgumentException("HtmlHelp file must have the extension .chm !", "chmFile");\r
+\r
+                       _chmFileName = chmFile;\r
+                       _associatedFile = new CHMFile(null, chmFile, true); // only load system data of chm\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal constructor used in the class <see cref="HtmlHelp.ChmDecoding.CHMFile">CHMFile</see>.\r
+               /// </summary>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               internal ChmFileInfo(CHMFile associatedFile)\r
+               {\r
+                       _associatedFile = associatedFile;\r
+\r
+                       if( _associatedFile == null)\r
+                               throw new ArgumentException("Associated CHMFile instance must not be null !", "associatedFile");\r
+               }\r
+\r
+               #region default info properties\r
+               /// <summary>\r
+               /// Gets the full filename of the chm file\r
+               /// </summary>          \r
+               public string ChmFileName\r
+               {\r
+                       get \r
+                       { \r
+                               return _associatedFile.ChmFilePath; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a <see cref="System.IO.FileInfo">FileInfo</see> instance for the chm file.\r
+               /// </summary>\r
+               public FileInfo FileInfo\r
+               {\r
+                       get { return new FileInfo(_associatedFile.ChmFilePath); }\r
+               }\r
+               #endregion\r
+\r
+               #region #SYSTEM properties\r
+               /// <summary>\r
+               /// Gets the file version of the chm file. \r
+               /// 2 for Compatibility=1.0,  3 for Compatibility=1.1\r
+               /// </summary>          \r
+               public int FileVersion\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.FileVersion;\r
+                                       \r
+                               return 0; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the contents file name\r
+               /// </summary>\r
+       \r
+               public string ContentsFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.ContentsFile;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the index file name\r
+               /// </summary>\r
+       \r
+               public string IndexFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.IndexFile;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default help topic\r
+               /// </summary>\r
+       \r
+               public string DefaultTopic\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.DefaultTopic;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the title of the help window\r
+               /// </summary>\r
+       \r
+               public string HelpWindowTitle\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.HelpWindowTitle;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if DBCS is in use\r
+               /// </summary>\r
+       \r
+               public bool DBCS\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.DBCS;\r
+                                       \r
+                               return false; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if full-text-search is available\r
+               /// </summary>\r
+       \r
+               public bool FullTextSearch\r
+               {\r
+                       get \r
+                       {\r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.FullTextSearch;\r
+                                       \r
+                               return false; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the file has ALinks\r
+               /// </summary>\r
+       \r
+               public bool HasALinks\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.HasALinks;\r
+                                       \r
+                               return false; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the file has KLinks\r
+               /// </summary>\r
+       \r
+               public bool HasKLinks\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.HasKLinks;\r
+                                       \r
+                               return false; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default window name\r
+               /// </summary>\r
+       \r
+               public string DefaultWindow\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.DefaultWindow;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the file name of the compile file\r
+               /// </summary>\r
+       \r
+               public string CompileFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.CompileFile;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary index file\r
+               /// </summary>\r
+       \r
+               public bool BinaryIndex\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.BinaryIndex;\r
+                                       \r
+                               return false; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary index file\r
+               /// </summary>\r
+       \r
+               public string CompilerVersion\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.CompilerVersion;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the flag if the chm has a binary toc file\r
+               /// </summary>\r
+       \r
+               public bool BinaryTOC\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.BinaryTOC;\r
+                                       \r
+                               return false; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the font face of the read font property.\r
+               /// Empty string for default font.\r
+               /// </summary>\r
+       \r
+               public string FontFace\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.FontFace;\r
+                                       \r
+                               return ""; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the font size of the read font property.\r
+               /// 0 for default font size\r
+               /// </summary>\r
+       \r
+               public double FontSize\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.FontSize;\r
+                                       \r
+                               return 0.0; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the character set of the read font property\r
+               /// 1 for default\r
+               /// </summary>\r
+       \r
+               public int CharacterSet\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.CharacterSet;\r
+                                       \r
+                               return 1; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the codepage depending on the read font property\r
+               /// </summary>\r
+       \r
+               public int CodePage\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.CodePage;\r
+                                       \r
+                               return 0; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the assiciated culture info\r
+               /// </summary>          \r
+               public CultureInfo Culture\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.Culture;\r
+                                       \r
+                               return CultureInfo.CurrentCulture; \r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               #region #IDXHDR properties\r
+               /// <summary>\r
+               /// Gets the number of topic nodes including the contents and index files\r
+               /// </summary>\r
+       \r
+               public int NumberOfTopicNodes\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.NumberOfTopicNodes;\r
+                               \r
+                               return 0;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the ImageList string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+       \r
+               public string ImageList\r
+               {\r
+                       get \r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.ImageList;\r
+\r
+                               return "";\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the background setting \r
+               /// </summary>\r
+       \r
+               public int Background\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.Background;\r
+\r
+                               return 0;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the foreground setting \r
+               /// </summary>\r
+       \r
+               public int Foreground\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.Foreground;\r
+\r
+                               return 0;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the FrameName string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+       \r
+               public string FrameName\r
+               {\r
+                       get \r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.FrameName;\r
+\r
+                               return "";\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the WindowName string specyfied in the #IDXHDR file.\r
+               /// </summary>\r
+               /// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>\r
+       \r
+               public string WindowName\r
+               {\r
+                       get \r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.WindowName;\r
+\r
+                               return "";\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a string array containing the merged file names\r
+               /// </summary>\r
+               public string[] MergedFiles\r
+               {\r
+                       get\r
+                       {\r
+                               if(_associatedFile != null) \r
+                                       return _associatedFile.MergedFiles;\r
+\r
+                               return new string[0];\r
+                       }\r
+               }\r
+\r
+               #endregion\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/Default.build b/irc/TechBot/CHMLibrary/Default.build
new file mode 100644 (file)
index 0000000..6363074
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>\r
+<project name="CHMLibrary" default="build">\r
+\r
+       <property name="output.dir" value="..\bin" />\r
+\r
+       <target name="build" description="Build component">\r
+               <mkdir dir="${output.dir}" />\r
+               <csc target="library"\r
+                       output="${output.dir}\CHMLibrary.dll"\r
+                       optimize="true"\r
+                       debug="true"\r
+                       doc="${output.dir}\CHMLibrary.xml"\r
+                       warninglevel="0">\r
+                       <sources>\r
+                               <include name="**/*.cs" />\r
+                       </sources>\r
+                       <references>\r
+                               <include name="${output.dir}\Compression.dll" />\r
+                       </references>\r
+               </csc>\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/CHMLibrary/HtmlHelpSystem.cs b/irc/TechBot/CHMLibrary/HtmlHelpSystem.cs
new file mode 100644 (file)
index 0000000..966cd3d
--- /dev/null
@@ -0,0 +1,894 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Data;\r
+\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>HtmlHelpSystem</c> implements the main object for reading chm files\r
+       /// </summary>\r
+       public sealed class HtmlHelpSystem\r
+       {\r
+               /// <summary>\r
+               /// Private shared instance of current HtmlHelpSystem class\r
+               /// </summary>\r
+               private static HtmlHelpSystem _current=null;\r
+               /// <summary>\r
+               /// Internal member storing the attached files\r
+               /// </summary>\r
+               private ArrayList _chmFiles = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing a merged table of contents\r
+               /// </summary>\r
+               private TableOfContents _toc = new TableOfContents();\r
+               /// <summary>\r
+               /// Internal member storing a merged index\r
+               /// </summary>\r
+               private Index _index = new Index();\r
+               /// <summary>\r
+               /// URL prefix for specifying a chm destination\r
+               /// </summary>\r
+               private static string _urlPrefix = "ms-its:";\r
+               /// <summary>\r
+               /// Internal flag specifying if the system should use the tree-images list\r
+               /// from HtmlHelp2. If false the standard CHM-Viewer pics will be used.\r
+               /// </summary>\r
+               private static bool _useHH2TreePics = false;\r
+               /// <summary>\r
+               /// Internal member storing the read information types\r
+               /// </summary>\r
+               private ArrayList _informationTypes = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the read categories\r
+               /// </summary>\r
+               private ArrayList _categories = new ArrayList();\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the url prefix for specifying a chm destination\r
+               /// </summary>\r
+               public static string UrlPrefix\r
+               {\r
+                       get { return _urlPrefix; }\r
+                       set { _urlPrefix = value; }\r
+               }\r
+\r
+               public CHMStream.CHMStream BaseStream\r
+               {\r
+                       get \r
+                       {\r
+                               CHMFile chm=(CHMFile)_chmFiles[0];\r
+                               return chm.BaseStream; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the flag specifying if the system should use the tree-images list\r
+               /// from HtmlHelp2. If false the standard CHM-Viewer pics will be used.\r
+               /// </summary>\r
+               public static bool UseHH2TreePics\r
+               {\r
+                       get { return _useHH2TreePics; }\r
+                       set { _useHH2TreePics = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the current HtmlHelpSystem instance\r
+               /// </summary>\r
+               public static HtmlHelpSystem Current\r
+               {\r
+                       get \r
+                       {\r
+                               return _current;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               public HtmlHelpSystem() : this("")\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the reader class\r
+               /// </summary>\r
+               /// <param name="chmFile">chm file to attach with the reader</param>\r
+               public HtmlHelpSystem(string chmFile)\r
+               {\r
+                       _current = this;\r
+                       OpenFile(chmFile);\r
+               }\r
+\r
+\r
+               /// <summary>\r
+               /// Opens a chm file and creates\r
+               /// </summary>\r
+               /// <param name="chmFile">full file path of the chm file to open</param>\r
+               /// <remarks>If you call this method, all existing merged files will be cleared.</remarks>\r
+               public void OpenFile(string chmFile)\r
+               {\r
+                       OpenFile(chmFile, null);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Opens a chm file and creates\r
+               /// </summary>\r
+               /// <param name="chmFile">full file path of the chm file to open</param>\r
+               /// <param name="dmpInfo">dumping info</param>\r
+               /// <remarks>If you call this method, all existing merged files will be cleared.</remarks>\r
+               public void OpenFile(string chmFile, DumpingInfo dmpInfo)\r
+               {\r
+                       if( File.Exists(chmFile ) )\r
+                       {\r
+                               _chmFiles.Clear();\r
+                               _toc.Clear();\r
+                               _index.Clear();\r
+                               _informationTypes.Clear();\r
+                               _categories.Clear();\r
+\r
+                               CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);\r
+\r
+                               _toc = new TableOfContents( newFile.TOC );\r
+                               _index = new Index( newFile.IndexKLinks, newFile.IndexALinks );\r
+\r
+                               _chmFiles.Add(newFile);\r
+                               // add all infotypes and categories of the read file to this system instance\r
+                               MergeFileInfoTypesCategories(newFile);\r
+\r
+                               // check if the file has a merged files list\r
+                               if( newFile.MergedFiles.Length > 0 )\r
+                               {\r
+                                       // extract the path of the chm file (usually merged files are in the same path)\r
+                                       FileInfo fi = new FileInfo(chmFile);\r
+                                       string sPath = fi.DirectoryName;\r
+\r
+                                       for(int i=0; i<newFile.MergedFiles.Length; i++)\r
+                                       {\r
+                                               string sFile = newFile.MergedFiles[i];\r
+\r
+                                               if(sFile.Length > 0)\r
+                                               {\r
+                                                       if(sFile[1] != ':') // no full path setting\r
+                                                       {\r
+                                                               sFile = Path.Combine(sPath, sFile);\r
+                                                       }\r
+\r
+                                                       MergeFile(sFile, dmpInfo, true);\r
+                                               }\r
+                                       }\r
+\r
+                                       // if (newFile.MergLinks.Count>0)\r
+                                       //      RecalculateMergeLinks(newFile);\r
+\r
+                                       RemoveMergeLinks(); // clear all merge-links which have no target !\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges a chm file to the current help contents\r
+               /// </summary>\r
+               /// <param name="chmFile">full file path of the chm file to merge</param>\r
+               public void MergeFile(string chmFile)\r
+               {\r
+                       MergeFile(chmFile, null);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges a chm file to the current help contents\r
+               /// </summary>\r
+               /// <param name="chmFile">full file path of the chm file to merge</param>\r
+               /// <param name="dmpInfo">dumping info</param>\r
+               public void MergeFile(string chmFile, DumpingInfo dmpInfo)\r
+               {\r
+                       MergeFile(chmFile, dmpInfo, false);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges a chm file to the current help contents\r
+               /// </summary>\r
+               /// <param name="chmFile">full file path of the chm file to merge</param>\r
+               /// <param name="dmpInfo">dumping info</param>\r
+               /// <param name="mergedFileList">true if the merge is done because a merged file list \r
+               /// was found in the previously loaded CHM.</param>\r
+               internal void MergeFile(string chmFile, DumpingInfo dmpInfo, bool mergedFileList)\r
+               {\r
+                       if( File.Exists(chmFile ) )\r
+                       {\r
+                               if( _chmFiles.Count == 1)\r
+                               {\r
+                                       // if we open the first file, we directly point into the toc and index of this file.\r
+                                       // So that we don't merge the new toc's indexe's into the first file, we have to \r
+                                       // clone the internal arraylists first to a new instance of the toc/index holder classes.\r
+                                       ArrayList atoc = _toc.TOC;\r
+                                       ArrayList alinks = _index.ALinks;\r
+                                       ArrayList klinks = _index.KLinks;\r
+\r
+                                       _toc = new TableOfContents();\r
+                                       _index = new Index();\r
+\r
+                                       _toc.MergeToC( atoc );\r
+                                       _index.MergeIndex( alinks, IndexType.AssiciativeLinks );\r
+                                       _index.MergeIndex( klinks, IndexType.KeywordLinks );\r
+                               }\r
+\r
+                               CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);\r
+\r
+                               if(mergedFileList) // if we've called this method due to a merged file list merge\r
+                               {\r
+                                       RecalculateMergeLinks(newFile);\r
+\r
+                                       _toc.MergeToC( newFile.TOC, _chmFiles );\r
+                                       _index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );\r
+                                       _index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );\r
+\r
+                                       _chmFiles.Add(newFile);\r
+\r
+                                       // add all infotypes and categories of the read file to this system instance\r
+                                       MergeFileInfoTypesCategories(newFile);\r
+                               }  \r
+                               else \r
+                               {\r
+                                       _toc.MergeToC( newFile.TOC, _chmFiles );\r
+                                       _index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );\r
+                                       _index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );\r
+\r
+                                       _chmFiles.Add(newFile);\r
+\r
+                                       // add all infotypes and categories of the read file to this system instance\r
+                                       MergeFileInfoTypesCategories(newFile);\r
+\r
+                                       // check if the file has a merged files list\r
+                                       if( newFile.MergedFiles.Length > 0 )\r
+                                       {\r
+                                               // extract the path of the chm file (usually merged files are in the same path)\r
+                                               FileInfo fi = new FileInfo(chmFile);\r
+                                               string sPath = fi.DirectoryName;\r
+\r
+                                               for(int i=0; i<newFile.MergedFiles.Length; i++)\r
+                                               {\r
+                                                       string sFile = newFile.MergedFiles[i];\r
+\r
+                                                       if(sFile.Length > 0)\r
+                                                       {\r
+                                                               if(sFile[1] != ':') // no full path setting\r
+                                                               {\r
+                                                                       sFile = Path.Combine(sPath, sFile);\r
+                                                               }\r
+\r
+                                                               MergeFile(sFile, dmpInfo, true);\r
+                                                       }\r
+                                               }\r
+\r
+                                               RemoveMergeLinks(); // clear all merge-links which have no target !\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks all Merg-links read till now. Checks if the merg-link points to the \r
+               /// file <c>currentFile</c>. If yes the link will be replaced by the contents of the \r
+               /// merged file.\r
+               /// </summary>\r
+               /// <param name="currentFile">Current CHMFile instance</param>\r
+               internal void RecalculateMergeLinks(CHMFile currentFile)\r
+               {\r
+                       foreach(CHMFile curFile in _chmFiles)\r
+                       {\r
+                               if( curFile.MergLinks.Count > 0)\r
+                               {\r
+                                       for(int i=0; i<curFile.MergLinks.Count; i++)\r
+                                       {\r
+                                               TOCItem curItem = curFile.MergLinks[i] as TOCItem;\r
+\r
+                                               string sMerge = curItem.MergeLink;\r
+                                               string [] sSplit = sMerge.Split( new char[]{':'} );\r
+\r
+                                               string sFName = "";\r
+                                               string sTarget = "";\r
+\r
+                                               if( sSplit.Length > 3) // merge info contains path name\r
+                                               {\r
+                                                       sFName = sSplit[0] + ":" + sSplit[1];\r
+                                                       sTarget = sSplit[3];\r
+                                               } \r
+                                               else if( sSplit.Length == 3)// merge info contains only file name\r
+                                               {\r
+                                                       FileInfo fi = new FileInfo(currentFile.ChmFilePath);\r
+                                                       string sPath = fi.DirectoryName;\r
+\r
+                                                       string sFile = sSplit[0];\r
+\r
+                                                       if(sFile.Length > 0)\r
+                                                       {\r
+                                                               if(sFile[1] != ':') // no full path setting\r
+                                                               {\r
+                                                                       sFile = Path.Combine(sPath, sFile);\r
+                                                               }\r
+                                                       }\r
+\r
+                                                       sFName = sFile;\r
+                                                       sTarget = sSplit[2];\r
+                                               }\r
+\r
+                                               ArrayList arrToc = null;\r
+                                               if( (sFName.Length>0) && (sTarget.Length>0) )\r
+                                               {\r
+                                               // if this link points into the current file\r
+                                                       if( sFName.ToLower() == currentFile.ChmFilePath.ToLower() )\r
+                                                       {\r
+                                                               if(sTarget.ToLower().IndexOf(".hhc") >= 0)\r
+                                                               {\r
+                                                                       string sfCheck = sTarget;\r
+\r
+                                                                       // remove prefixing ./\r
+                                                                       while( (sfCheck[0]=='.') || (sfCheck[0]=='/') )\r
+                                                                       {\r
+                                                                               sfCheck = sfCheck.Substring(1);\r
+                                                                       }\r
+\r
+                                                                       if( currentFile.ContentsFile.ToLower() != sfCheck )\r
+                                                                       {\r
+                                                                               arrToc = currentFile.ParseHHC( sTarget );\r
+\r
+                                                                               if( arrToc.Count > 0)\r
+                                                                               {\r
+                                                                               }\r
+                                                                       } \r
+                                                                       else \r
+                                                                       {\r
+                                                                               arrToc = currentFile.TOC;\r
+                                                                       }\r
+\r
+                                                                       // target points to a complete TOC\r
+                                                                       int nCnt = 0;\r
+\r
+                                                                       foreach(TOCItem chkItem in arrToc)\r
+                                                                       {\r
+                                                                               if(nCnt == 0)\r
+                                                                               {\r
+                                                                                       curItem.AssociatedFile = currentFile;\r
+                                                                                       curItem.Children = chkItem.Children;\r
+                                                                                       curItem.ChmFile = currentFile.ChmFilePath;\r
+                                                                                       curItem.ImageIndex = chkItem.ImageIndex;\r
+                                                                                       curItem.Local = chkItem.Local;\r
+                                                                                       curItem.MergeLink = chkItem.MergeLink;\r
+                                                                                       curItem.Name = chkItem.Name;\r
+                                                                                       curItem.TocMode = chkItem.TocMode;\r
+                                                                                       curItem.TopicOffset = chkItem.TopicOffset;\r
+\r
+                                                                                       MarkChildrenAdded(chkItem.Children, curFile.MergLinks);\r
+                                                                               } \r
+                                                                               else \r
+                                                                               {\r
+                                                                                       ArrayList checkList = null;\r
+\r
+                                                                                       if(curItem.Parent != null)\r
+                                                                                               checkList = curItem.Parent.Children;\r
+                                                                                       else\r
+                                                                                               checkList = curFile.TOC;\r
+                                                                                       \r
+                                                                                       int nIdx = checkList.IndexOf(curItem);\r
+                                                                                       if((nIdx+nCnt)>checkList.Count)\r
+                                                                                               checkList.Add(chkItem);\r
+                                                                                       else\r
+                                                                                               checkList.Insert(nIdx+nCnt, chkItem);\r
+                                                                                       \r
+                                                                                       curFile.MergLinks.Add(chkItem);\r
+                                                                                       MarkChildrenAdded(chkItem.Children, curFile.MergLinks);\r
+                                                                               }\r
+\r
+                                                                               nCnt++;\r
+                                                                       }\r
+                                                               } \r
+                                                               else \r
+                                                               {\r
+                                                               \r
+                                                                       // target points to a single topic\r
+                                                                       TOCItem chkItem = currentFile.GetTOCItemByLocal(sTarget);\r
+                                                                       if(chkItem != null)\r
+                                                                       {\r
+                                                                               curItem.AssociatedFile = currentFile;\r
+                                                                               curItem.Children = chkItem.Children;\r
+                                                                               curItem.ChmFile = currentFile.ChmFilePath;\r
+                                                                               curItem.ImageIndex = chkItem.ImageIndex;\r
+                                                                               curItem.Local = chkItem.Local;\r
+                                                                               curItem.MergeLink = chkItem.MergeLink;\r
+                                                                               curItem.Name = chkItem.Name;\r
+                                                                               curItem.TocMode = chkItem.TocMode;\r
+                                                                               curItem.TopicOffset = chkItem.TopicOffset;\r
+\r
+                                                                               curFile.MergLinks.Add(chkItem);\r
+                                                                               MarkChildrenAdded(chkItem.Children, curFile.MergLinks);\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Adds sub-items of an TOC-entry to the merg-linked list. \r
+               /// This will mark this item as "added" during the extra merge run \r
+               /// of the HtmlHelpSystem class.\r
+               /// </summary>\r
+               /// <param name="tocs">TOCItem list</param>\r
+               /// <param name="merged">Arraylist which holds the merged-items</param>\r
+               internal void MarkChildrenAdded(ArrayList tocs, ArrayList merged)\r
+               {\r
+                       foreach(TOCItem curItem in tocs)\r
+                       {\r
+                               if(!merged.Contains(curItem))\r
+                               {\r
+                                       merged.Add(curItem);\r
+\r
+                                       MarkChildrenAdded(curItem.Children, merged);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Removes merge-links from the toc of files which were not loaded\r
+               /// </summary>\r
+               internal void RemoveMergeLinks()\r
+               {\r
+                       foreach(CHMFile curFile in _chmFiles)\r
+                       {\r
+                               if( curFile.MergLinks.Count > 0)\r
+                               {\r
+                                       while(curFile.MergLinks.Count > 0)\r
+                                       {\r
+                                               TOCItem curItem = curFile.MergLinks[0] as TOCItem;\r
+                                               if(curItem.MergeLink.Length > 0)\r
+                                                       curFile.RemoveTOCItem(curItem);\r
+\r
+                                               curFile.MergLinks.RemoveAt(0);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges the information types and categories read by the CHMFile instance \r
+               /// into the system instance\r
+               /// </summary>\r
+               /// <param name="chmFile">file instance</param>\r
+               private void MergeFileInfoTypesCategories(CHMFile chmFile)\r
+               {\r
+                       if(chmFile.HasInformationTypes)\r
+                       {\r
+                               for(int i=0; i<chmFile.InformationTypes.Count;i++)\r
+                               {\r
+                                       InformationType curType = chmFile.InformationTypes[i] as InformationType;\r
+                                       InformationType sysType = GetInformationType( curType.Name );\r
+\r
+                                       if( sysType == null)\r
+                                               _informationTypes.Add(curType);\r
+                                       else\r
+                                               curType.ReferenceCount++;\r
+                               }\r
+                       }\r
+\r
+                       if(chmFile.HasCategories)\r
+                       {\r
+                               for(int i=0; i<chmFile.Categories.Count;i++)\r
+                               {\r
+                                       Category curCat = chmFile.Categories[i] as Category;\r
+                                       Category sysCat = GetCategory( curCat.Name );\r
+\r
+                                       if(sysCat == null)\r
+                                               _categories.Add(curCat);\r
+                                       else\r
+                                               curCat.ReferenceCount++;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Removes the information types and categories read by the CHMFile instance \r
+               /// </summary>\r
+               /// <param name="chmFile">file instance</param>\r
+               private void RemoveFileInfoTypesCategories(CHMFile chmFile)\r
+               {\r
+                       if(chmFile.HasInformationTypes)\r
+                       {\r
+                               for(int i=0; i<chmFile.InformationTypes.Count;i++)\r
+                               {\r
+                                       InformationType curType = chmFile.InformationTypes[i] as InformationType;\r
+                                       InformationType sysType = GetInformationType( curType.Name );\r
+\r
+                                       if(sysType != null)\r
+                                       {\r
+                                               sysType.ReferenceCount--;\r
+\r
+                                               if(sysType.ReferenceCount<=0)\r
+                                                       _informationTypes.Remove(sysType);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if(chmFile.HasCategories)\r
+                       {\r
+                               for(int i=0; i<chmFile.Categories.Count;i++)\r
+                               {\r
+                                       Category curCat = chmFile.Categories[i] as Category;\r
+                                       Category sysCat = GetCategory( curCat.Name );\r
+\r
+                                       if(sysCat != null)\r
+                                       {\r
+                                               sysCat.ReferenceCount--;\r
+\r
+                                               if(sysCat.ReferenceCount<=0)\r
+                                                       _categories.Remove(sysCat);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Removes a chm file from the internal file collection\r
+               /// </summary>\r
+               /// <param name="chmFile">full file path of the chm file to remove</param>\r
+               public void RemoveFile(string chmFile)\r
+               {\r
+                       int nIdx = -1;\r
+                       CHMFile removeInstance=null;\r
+\r
+                       foreach(CHMFile curFile in _chmFiles)\r
+                       {\r
+                               nIdx++;\r
+\r
+                               if( curFile.ChmFilePath.ToLower() == chmFile.ToLower() )\r
+                               {\r
+                                       removeInstance = curFile;\r
+                                       break;\r
+                               }\r
+                       }\r
+\r
+                       if(nIdx >= 0)\r
+                       {\r
+                               _toc.Clear(); // forces a rebuild of the merged toc\r
+                               _index.Clear(); // force a rebuild of the merged index\r
+                               \r
+                               RemoveFileInfoTypesCategories(removeInstance);\r
+                               _chmFiles.RemoveAt(nIdx);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Closes all files and destroys TOC/index\r
+               /// </summary>\r
+               public void CloseAllFiles()\r
+               {\r
+                       for(int i=0; i < _chmFiles.Count; i++)\r
+                       {\r
+                               CHMFile curFile = _chmFiles[i] as CHMFile;\r
+\r
+                               _chmFiles.RemoveAt(i);\r
+                               curFile.Dispose();\r
+                               i--;\r
+                       }\r
+\r
+                       _chmFiles.Clear();\r
+                       _toc.Clear();\r
+                       _index.Clear();\r
+                       _informationTypes.Clear();\r
+                       _categories.Clear();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an array of loaded chm files. \r
+               /// </summary>\r
+               public CHMFile[] FileList\r
+               {\r
+                       get\r
+                       {\r
+                               CHMFile[] ret = new CHMFile[ _chmFiles.Count ];\r
+                               for(int i=0;i<_chmFiles.Count;i++)\r
+                                       ret[i] = (CHMFile)_chmFiles[i];\r
+\r
+                               return ret;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Returns true if the HtmlHelpSystem instance contains 1 or more information types\r
+               /// </summary>\r
+               public bool HasInformationTypes\r
+               {\r
+                       get { return (_informationTypes.Count>0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Returns true if the HtmlHelpSystem instance contains 1 or more categories\r
+               /// </summary>\r
+               public bool HasCategories\r
+               {\r
+                       get { return (_categories.Count>0); }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an ArrayList of <see cref="InformationType">InformationType</see> items\r
+               /// </summary>\r
+               public ArrayList InformationTypes\r
+               {\r
+                       get { return _informationTypes; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an ArrayList of <see cref="Category">Category</see> items\r
+               /// </summary>\r
+               public ArrayList Categories\r
+               {\r
+                       get { return _categories; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the information type specified by its name\r
+               /// </summary>\r
+               /// <param name="name">name of the information type to receive</param>\r
+               /// <returns>Returns the Instance for the name or null if not found</returns>\r
+               public InformationType GetInformationType(string name)\r
+               {\r
+                       if(HasInformationTypes)\r
+                       {\r
+                               for(int i=0; i<_informationTypes.Count;i++)\r
+                               {\r
+                                       InformationType iT = _informationTypes[i] as InformationType;\r
+\r
+                                       if(iT.Name == name)\r
+                                               return iT;\r
+                               }\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the category specifiyd by its name\r
+               /// </summary>\r
+               /// <param name="name">name of the category</param>\r
+               /// <returns>Returns the Instance for the name or null if not found</returns>\r
+               public Category GetCategory(string name)\r
+               {\r
+                       if(HasCategories)\r
+                       {\r
+                               for(int i=0; i<_categories.Count;i++)\r
+                               {\r
+                                       Category cat = _categories[i] as Category;\r
+\r
+                                       if(cat.Name == name)\r
+                                               return cat;\r
+                               }\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the default topic\r
+               /// </summary>\r
+               public string DefaultTopic\r
+               {\r
+                       get\r
+                       {\r
+                               if( _chmFiles.Count > 0 )\r
+                               {\r
+                                       foreach(CHMFile curFile in _chmFiles)\r
+                                       {\r
+                                               if( curFile.DefaultTopic.Length > 0)\r
+                                               {\r
+                                                       return curFile.FormURL( curFile.DefaultTopic );\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return "about:blank";\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a merged table of contents of all opened chm files\r
+               /// </summary>\r
+               public TableOfContents TableOfContents\r
+               {\r
+                       get\r
+                       {\r
+                               if( _chmFiles.Count > 0 )\r
+                               {\r
+                                       if( _toc.Count() <= 0)\r
+                                       {\r
+                                               // merge toc of files\r
+                                               foreach(CHMFile curFile in _chmFiles)\r
+                                               {\r
+                                                       _toc.MergeToC( curFile.TOC );\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _toc;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a merged index  of all opened chm files\r
+               /// </summary>\r
+               public Index Index\r
+               {\r
+                       get\r
+                       {\r
+                               if( _chmFiles.Count > 0 )\r
+                               {\r
+                                       if( (_index.Count(IndexType.KeywordLinks)+_index.Count(IndexType.AssiciativeLinks)) <= 0)\r
+                                       {\r
+                                               // merge index files\r
+                                               foreach(CHMFile curFile in _chmFiles)\r
+                                               {\r
+                                                       _index.MergeIndex( curFile.IndexKLinks, IndexType.KeywordLinks);\r
+                                                       _index.MergeIndex( curFile.IndexALinks, IndexType.AssiciativeLinks);\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _index;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a flag if the current instance offers a table of contents\r
+               /// </summary>\r
+               public bool HasTableOfContents\r
+               {\r
+                       get \r
+                       {\r
+                               return (TableOfContents.Count() > 0);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a flag if the current instance offers an index\r
+               /// </summary>\r
+               public bool HasIndex\r
+               {\r
+                       get \r
+                       {\r
+                               return (HasALinks || HasKLinks);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a flag if the index holds klinks\r
+               /// </summary>\r
+               public bool HasKLinks\r
+               {\r
+                       get\r
+                       {\r
+                               return (_index.Count(IndexType.KeywordLinks) > 0);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a flag if the index holds alinks\r
+               /// </summary>\r
+               public bool HasALinks\r
+               {\r
+                       get\r
+                       {\r
+                               return (_index.Count(IndexType.AssiciativeLinks) > 0);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets a flag if the current instance supports fulltext searching\r
+               /// </summary>\r
+               public bool FullTextSearch\r
+               {\r
+                       get \r
+                       {\r
+                               bool bRet = false;\r
+\r
+                               foreach(CHMFile curFile in _chmFiles)\r
+                               {\r
+                                       bRet |= curFile.FullTextSearch;\r
+                               }\r
+\r
+                               return bRet;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Performs a full-text search over the chm files\r
+               /// </summary>\r
+               /// <param name="words">words to search</param>\r
+               /// <param name="partialMatches">true if partial word should be matched also \r
+               /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>\r
+               /// <param name="titleOnly">true if titles only</param>\r
+               /// <returns>A DataTable containing the search hits</returns>\r
+               public DataTable PerformSearch(string words, bool partialMatches, bool titleOnly)\r
+               {\r
+                       return PerformSearch(words, -1, partialMatches, titleOnly);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Performs a full-text search over the chm files\r
+               /// </summary>\r
+               /// <param name="words">words to search</param>\r
+               /// <param name="MaxResults">maximal number of hits to return</param>\r
+               /// <param name="partialMatches">true if partial word should be matched also \r
+               /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>\r
+               /// <param name="titleOnly">true if titles only</param>\r
+               /// <returns>A DataTable containing the search hits</returns>\r
+               public DataTable PerformSearch(string words, int MaxResults, bool partialMatches, bool titleOnly)\r
+               {\r
+                       if( ! FullTextSearch )\r
+                               return null;\r
+\r
+                       DataTable dtResult = null;\r
+\r
+                       int nCnt = 0;\r
+\r
+                       foreach(CHMFile curFile in _chmFiles)\r
+                       {\r
+                               if(nCnt == 0)\r
+                               {\r
+                                       if(curFile.FullTextSearchEngine.CanSearch)\r
+                                       {\r
+                                               if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))\r
+                                               {\r
+                                                       dtResult = curFile.FullTextSearchEngine.Hits;\r
+                                                       dtResult.DefaultView.Sort = "Rating DESC";\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       if(curFile.FullTextSearchEngine.CanSearch)\r
+                                       {\r
+                                               if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))\r
+                                               {\r
+                                                       DataTable table = curFile.FullTextSearchEngine.Hits;\r
+                                                       \r
+                                                       // append rows from 2nd file\r
+                                                       foreach(DataRow curRow in table.Rows)\r
+                                                       {\r
+                                                               dtResult.ImportRow( curRow );\r
+                                                       }\r
+\r
+                                                       dtResult.DefaultView.Sort = "Rating DESC";\r
+\r
+                                                       // adjust max hits\r
+                                                       if(MaxResults >= 0)\r
+                                                       {\r
+                                                               if(dtResult.DefaultView.Count > MaxResults)\r
+                                                               {\r
+                                                                       for(int i=MaxResults-1; i<dtResult.DefaultView.Count-1;i++)\r
+                                                                       {\r
+                                                                               if(dtResult.DefaultView[i].Row.RowState != DataRowState.Deleted)\r
+                                                                               {\r
+                                                                                       dtResult.DefaultView[i].Row.Delete();\r
+                                                                                       dtResult.DefaultView[i].Row.AcceptChanges();\r
+                                                                                       i--;\r
+                                                                               }\r
+                                                                       }\r
+\r
+                                                                       dtResult.AcceptChanges();\r
+                                                                       dtResult.DefaultView.Sort = "Rating DESC";\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               nCnt++;\r
+                       }\r
+\r
+                       return dtResult;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/HttpUtility.cs b/irc/TechBot/CHMLibrary/HttpUtility.cs
new file mode 100644 (file)
index 0000000..ed723a1
--- /dev/null
@@ -0,0 +1,855 @@
+// \r
+// System.Web.HttpUtility\r
+//\r
+// Authors:\r
+//   Patrik Torstensson (Patrik.Torstensson@labs2.com)\r
+//   Wictor Wilén (decode/encode functions) (wictor@ibizkit.se)\r
+//   Tim Coleman (tim@timcoleman.com)\r
+//   Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
+//\r
+\r
+//\r
+// Permission is hereby granted, free of charge, to any person obtaining\r
+// a copy of this software and associated documentation files (the\r
+// "Software"), to deal in the Software without restriction, including\r
+// without limitation the rights to use, copy, modify, merge, publish,\r
+// distribute, sublicense, and/or sell copies of the Software, and to\r
+// permit persons to whom the Software is furnished to do so, subject to\r
+// the following conditions:\r
+// \r
+// The above copyright notice and this permission notice shall be\r
+// included in all copies or substantial portions of the Software.\r
+// \r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+//\r
+using System;\r
+using System.Collections;\r
+using System.Globalization;\r
+using System.IO;\r
+using System.Text;\r
+// using System.Web.Util;\r
+\r
+namespace HtmlHelp.ChmDecoding\r
+{\r
+       public sealed class HttpUtility {\r
+\r
+               #region Fields\r
+       \r
+               const string _hex = "0123456789ABCDEF";\r
+               const string _chars = "<>;:.?=&@*+%/\\";\r
+               static Hashtable entities;\r
+               static object lock_ = new object ();\r
+       \r
+               #endregion // Fields\r
+       \r
+               static Hashtable Entities {\r
+                       get {\r
+                               lock (lock_) {\r
+                                       if (entities == null)\r
+                                               InitEntities ();\r
+\r
+                                       return entities;\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               #region Constructors\r
+\r
+               static void InitEntities ()\r
+               {\r
+                       // Build the hash table of HTML entity references.  This list comes\r
+                       // from the HTML 4.01 W3C recommendation.\r
+                       entities = new Hashtable ();\r
+                       entities.Add ("nbsp", '\u00A0');\r
+                       entities.Add ("iexcl", '\u00A1');\r
+                       entities.Add ("cent", '\u00A2');\r
+                       entities.Add ("pound", '\u00A3');\r
+                       entities.Add ("curren", '\u00A4');\r
+                       entities.Add ("yen", '\u00A5');\r
+                       entities.Add ("brvbar", '\u00A6');\r
+                       entities.Add ("sect", '\u00A7');\r
+                       entities.Add ("uml", '\u00A8');\r
+                       entities.Add ("copy", '\u00A9');\r
+                       entities.Add ("ordf", '\u00AA');\r
+                       entities.Add ("laquo", '\u00AB');\r
+                       entities.Add ("not", '\u00AC');\r
+                       entities.Add ("shy", '\u00AD');\r
+                       entities.Add ("reg", '\u00AE');\r
+                       entities.Add ("macr", '\u00AF');\r
+                       entities.Add ("deg", '\u00B0');\r
+                       entities.Add ("plusmn", '\u00B1');\r
+                       entities.Add ("sup2", '\u00B2');\r
+                       entities.Add ("sup3", '\u00B3');\r
+                       entities.Add ("acute", '\u00B4');\r
+                       entities.Add ("micro", '\u00B5');\r
+                       entities.Add ("para", '\u00B6');\r
+                       entities.Add ("middot", '\u00B7');\r
+                       entities.Add ("cedil", '\u00B8');\r
+                       entities.Add ("sup1", '\u00B9');\r
+                       entities.Add ("ordm", '\u00BA');\r
+                       entities.Add ("raquo", '\u00BB');\r
+                       entities.Add ("frac14", '\u00BC');\r
+                       entities.Add ("frac12", '\u00BD');\r
+                       entities.Add ("frac34", '\u00BE');\r
+                       entities.Add ("iquest", '\u00BF');\r
+                       entities.Add ("Agrave", '\u00C0');\r
+                       entities.Add ("Aacute", '\u00C1');\r
+                       entities.Add ("Acirc", '\u00C2');\r
+                       entities.Add ("Atilde", '\u00C3');\r
+                       entities.Add ("Auml", '\u00C4');\r
+                       entities.Add ("Aring", '\u00C5');\r
+                       entities.Add ("AElig", '\u00C6');\r
+                       entities.Add ("Ccedil", '\u00C7');\r
+                       entities.Add ("Egrave", '\u00C8');\r
+                       entities.Add ("Eacute", '\u00C9');\r
+                       entities.Add ("Ecirc", '\u00CA');\r
+                       entities.Add ("Euml", '\u00CB');\r
+                       entities.Add ("Igrave", '\u00CC');\r
+                       entities.Add ("Iacute", '\u00CD');\r
+                       entities.Add ("Icirc", '\u00CE');\r
+                       entities.Add ("Iuml", '\u00CF');\r
+                       entities.Add ("ETH", '\u00D0');\r
+                       entities.Add ("Ntilde", '\u00D1');\r
+                       entities.Add ("Ograve", '\u00D2');\r
+                       entities.Add ("Oacute", '\u00D3');\r
+                       entities.Add ("Ocirc", '\u00D4');\r
+                       entities.Add ("Otilde", '\u00D5');\r
+                       entities.Add ("Ouml", '\u00D6');\r
+                       entities.Add ("times", '\u00D7');\r
+                       entities.Add ("Oslash", '\u00D8');\r
+                       entities.Add ("Ugrave", '\u00D9');\r
+                       entities.Add ("Uacute", '\u00DA');\r
+                       entities.Add ("Ucirc", '\u00DB');\r
+                       entities.Add ("Uuml", '\u00DC');\r
+                       entities.Add ("Yacute", '\u00DD');\r
+                       entities.Add ("THORN", '\u00DE');\r
+                       entities.Add ("szlig", '\u00DF');\r
+                       entities.Add ("agrave", '\u00E0');\r
+                       entities.Add ("aacute", '\u00E1');\r
+                       entities.Add ("acirc", '\u00E2');\r
+                       entities.Add ("atilde", '\u00E3');\r
+                       entities.Add ("auml", '\u00E4');\r
+                       entities.Add ("aring", '\u00E5');\r
+                       entities.Add ("aelig", '\u00E6');\r
+                       entities.Add ("ccedil", '\u00E7');\r
+                       entities.Add ("egrave", '\u00E8');\r
+                       entities.Add ("eacute", '\u00E9');\r
+                       entities.Add ("ecirc", '\u00EA');\r
+                       entities.Add ("euml", '\u00EB');\r
+                       entities.Add ("igrave", '\u00EC');\r
+                       entities.Add ("iacute", '\u00ED');\r
+                       entities.Add ("icirc", '\u00EE');\r
+                       entities.Add ("iuml", '\u00EF');\r
+                       entities.Add ("eth", '\u00F0');\r
+                       entities.Add ("ntilde", '\u00F1');\r
+                       entities.Add ("ograve", '\u00F2');\r
+                       entities.Add ("oacute", '\u00F3');\r
+                       entities.Add ("ocirc", '\u00F4');\r
+                       entities.Add ("otilde", '\u00F5');\r
+                       entities.Add ("ouml", '\u00F6');\r
+                       entities.Add ("divide", '\u00F7');\r
+                       entities.Add ("oslash", '\u00F8');\r
+                       entities.Add ("ugrave", '\u00F9');\r
+                       entities.Add ("uacute", '\u00FA');\r
+                       entities.Add ("ucirc", '\u00FB');\r
+                       entities.Add ("uuml", '\u00FC');\r
+                       entities.Add ("yacute", '\u00FD');\r
+                       entities.Add ("thorn", '\u00FE');\r
+                       entities.Add ("yuml", '\u00FF');\r
+                       entities.Add ("fnof", '\u0192');\r
+                       entities.Add ("Alpha", '\u0391');\r
+                       entities.Add ("Beta", '\u0392');\r
+                       entities.Add ("Gamma", '\u0393');\r
+                       entities.Add ("Delta", '\u0394');\r
+                       entities.Add ("Epsilon", '\u0395');\r
+                       entities.Add ("Zeta", '\u0396');\r
+                       entities.Add ("Eta", '\u0397');\r
+                       entities.Add ("Theta", '\u0398');\r
+                       entities.Add ("Iota", '\u0399');\r
+                       entities.Add ("Kappa", '\u039A');\r
+                       entities.Add ("Lambda", '\u039B');\r
+                       entities.Add ("Mu", '\u039C');\r
+                       entities.Add ("Nu", '\u039D');\r
+                       entities.Add ("Xi", '\u039E');\r
+                       entities.Add ("Omicron", '\u039F');\r
+                       entities.Add ("Pi", '\u03A0');\r
+                       entities.Add ("Rho", '\u03A1');\r
+                       entities.Add ("Sigma", '\u03A3');\r
+                       entities.Add ("Tau", '\u03A4');\r
+                       entities.Add ("Upsilon", '\u03A5');\r
+                       entities.Add ("Phi", '\u03A6');\r
+                       entities.Add ("Chi", '\u03A7');\r
+                       entities.Add ("Psi", '\u03A8');\r
+                       entities.Add ("Omega", '\u03A9');\r
+                       entities.Add ("alpha", '\u03B1');\r
+                       entities.Add ("beta", '\u03B2');\r
+                       entities.Add ("gamma", '\u03B3');\r
+                       entities.Add ("delta", '\u03B4');\r
+                       entities.Add ("epsilon", '\u03B5');\r
+                       entities.Add ("zeta", '\u03B6');\r
+                       entities.Add ("eta", '\u03B7');\r
+                       entities.Add ("theta", '\u03B8');\r
+                       entities.Add ("iota", '\u03B9');\r
+                       entities.Add ("kappa", '\u03BA');\r
+                       entities.Add ("lambda", '\u03BB');\r
+                       entities.Add ("mu", '\u03BC');\r
+                       entities.Add ("nu", '\u03BD');\r
+                       entities.Add ("xi", '\u03BE');\r
+                       entities.Add ("omicron", '\u03BF');\r
+                       entities.Add ("pi", '\u03C0');\r
+                       entities.Add ("rho", '\u03C1');\r
+                       entities.Add ("sigmaf", '\u03C2');\r
+                       entities.Add ("sigma", '\u03C3');\r
+                       entities.Add ("tau", '\u03C4');\r
+                       entities.Add ("upsilon", '\u03C5');\r
+                       entities.Add ("phi", '\u03C6');\r
+                       entities.Add ("chi", '\u03C7');\r
+                       entities.Add ("psi", '\u03C8');\r
+                       entities.Add ("omega", '\u03C9');\r
+                       entities.Add ("thetasym", '\u03D1');\r
+                       entities.Add ("upsih", '\u03D2');\r
+                       entities.Add ("piv", '\u03D6');\r
+                       entities.Add ("bull", '\u2022');\r
+                       entities.Add ("hellip", '\u2026');\r
+                       entities.Add ("prime", '\u2032');\r
+                       entities.Add ("Prime", '\u2033');\r
+                       entities.Add ("oline", '\u203E');\r
+                       entities.Add ("frasl", '\u2044');\r
+                       entities.Add ("weierp", '\u2118');\r
+                       entities.Add ("image", '\u2111');\r
+                       entities.Add ("real", '\u211C');\r
+                       entities.Add ("trade", '\u2122');\r
+                       entities.Add ("alefsym", '\u2135');\r
+                       entities.Add ("larr", '\u2190');\r
+                       entities.Add ("uarr", '\u2191');\r
+                       entities.Add ("rarr", '\u2192');\r
+                       entities.Add ("darr", '\u2193');\r
+                       entities.Add ("harr", '\u2194');\r
+                       entities.Add ("crarr", '\u21B5');\r
+                       entities.Add ("lArr", '\u21D0');\r
+                       entities.Add ("uArr", '\u21D1');\r
+                       entities.Add ("rArr", '\u21D2');\r
+                       entities.Add ("dArr", '\u21D3');\r
+                       entities.Add ("hArr", '\u21D4');\r
+                       entities.Add ("forall", '\u2200');\r
+                       entities.Add ("part", '\u2202');\r
+                       entities.Add ("exist", '\u2203');\r
+                       entities.Add ("empty", '\u2205');\r
+                       entities.Add ("nabla", '\u2207');\r
+                       entities.Add ("isin", '\u2208');\r
+                       entities.Add ("notin", '\u2209');\r
+                       entities.Add ("ni", '\u220B');\r
+                       entities.Add ("prod", '\u220F');\r
+                       entities.Add ("sum", '\u2211');\r
+                       entities.Add ("minus", '\u2212');\r
+                       entities.Add ("lowast", '\u2217');\r
+                       entities.Add ("radic", '\u221A');\r
+                       entities.Add ("prop", '\u221D');\r
+                       entities.Add ("infin", '\u221E');\r
+                       entities.Add ("ang", '\u2220');\r
+                       entities.Add ("and", '\u2227');\r
+                       entities.Add ("or", '\u2228');\r
+                       entities.Add ("cap", '\u2229');\r
+                       entities.Add ("cup", '\u222A');\r
+                       entities.Add ("int", '\u222B');\r
+                       entities.Add ("there4", '\u2234');\r
+                       entities.Add ("sim", '\u223C');\r
+                       entities.Add ("cong", '\u2245');\r
+                       entities.Add ("asymp", '\u2248');\r
+                       entities.Add ("ne", '\u2260');\r
+                       entities.Add ("equiv", '\u2261');\r
+                       entities.Add ("le", '\u2264');\r
+                       entities.Add ("ge", '\u2265');\r
+                       entities.Add ("sub", '\u2282');\r
+                       entities.Add ("sup", '\u2283');\r
+                       entities.Add ("nsub", '\u2284');\r
+                       entities.Add ("sube", '\u2286');\r
+                       entities.Add ("supe", '\u2287');\r
+                       entities.Add ("oplus", '\u2295');\r
+                       entities.Add ("otimes", '\u2297');\r
+                       entities.Add ("perp", '\u22A5');\r
+                       entities.Add ("sdot", '\u22C5');\r
+                       entities.Add ("lceil", '\u2308');\r
+                       entities.Add ("rceil", '\u2309');\r
+                       entities.Add ("lfloor", '\u230A');\r
+                       entities.Add ("rfloor", '\u230B');\r
+                       entities.Add ("lang", '\u2329');\r
+                       entities.Add ("rang", '\u232A');\r
+                       entities.Add ("loz", '\u25CA');\r
+                       entities.Add ("spades", '\u2660');\r
+                       entities.Add ("clubs", '\u2663');\r
+                       entities.Add ("hearts", '\u2665');\r
+                       entities.Add ("diams", '\u2666');\r
+                       entities.Add ("quot", '\u0022');\r
+                       entities.Add ("amp", '\u0026');\r
+                       entities.Add ("lt", '\u003C');\r
+                       entities.Add ("gt", '\u003E');\r
+                       entities.Add ("OElig", '\u0152');\r
+                       entities.Add ("oelig", '\u0153');\r
+                       entities.Add ("Scaron", '\u0160');\r
+                       entities.Add ("scaron", '\u0161');\r
+                       entities.Add ("Yuml", '\u0178');\r
+                       entities.Add ("circ", '\u02C6');\r
+                       entities.Add ("tilde", '\u02DC');\r
+                       entities.Add ("ensp", '\u2002');\r
+                       entities.Add ("emsp", '\u2003');\r
+                       entities.Add ("thinsp", '\u2009');\r
+                       entities.Add ("zwnj", '\u200C');\r
+                       entities.Add ("zwj", '\u200D');\r
+                       entities.Add ("lrm", '\u200E');\r
+                       entities.Add ("rlm", '\u200F');\r
+                       entities.Add ("ndash", '\u2013');\r
+                       entities.Add ("mdash", '\u2014');\r
+                       entities.Add ("lsquo", '\u2018');\r
+                       entities.Add ("rsquo", '\u2019');\r
+                       entities.Add ("sbquo", '\u201A');\r
+                       entities.Add ("ldquo", '\u201C');\r
+                       entities.Add ("rdquo", '\u201D');\r
+                       entities.Add ("bdquo", '\u201E');\r
+                       entities.Add ("dagger", '\u2020');\r
+                       entities.Add ("Dagger", '\u2021');\r
+                       entities.Add ("permil", '\u2030');\r
+                       entities.Add ("lsaquo", '\u2039');\r
+                       entities.Add ("rsaquo", '\u203A');\r
+                       entities.Add ("euro", '\u20AC');\r
+               }\r
+       \r
+               public HttpUtility () \r
+               {\r
+               }\r
+       \r
+               #endregion // Constructors\r
+       \r
+               #region Methods\r
+       \r
+               public static void HtmlAttributeEncode (string s, TextWriter output) \r
+               {\r
+                       output.Write(HtmlAttributeEncode(s));\r
+               }\r
+       \r
+               public static string HtmlAttributeEncode (string s) \r
+               {\r
+                       if (null == s) \r
+                               return null;\r
+       \r
+                       if (s.IndexOf ('&') == -1 && s.IndexOf ('"') == -1)\r
+                               return s;\r
+\r
+                       StringBuilder output = new StringBuilder ();\r
+                       foreach (char c in s) \r
+                               switch (c) {\r
+                               case '&' : \r
+                                       output.Append ("&amp;");\r
+                                       break;\r
+                               case '"' :\r
+                                       output.Append ("&quot;");\r
+                                       break;\r
+                               default:\r
+                                       output.Append (c);\r
+                                       break;\r
+                               }\r
+       \r
+                       return output.ToString();\r
+               }\r
+       \r
+               public static string UrlDecode (string str) \r
+               {\r
+                       return UrlDecode(str, Encoding.UTF8);\r
+               }\r
+       \r
+               private static char [] GetChars (MemoryStream b, Encoding e)\r
+               {\r
+                       return e.GetChars (b.GetBuffer (), 0, (int) b.Length);\r
+               }\r
+               \r
+               public static string UrlDecode (string s, Encoding e)\r
+               {\r
+                       if (null == s) \r
+                               return null;\r
+\r
+                       if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)\r
+                               return s;\r
+\r
+                       if (e == null)\r
+                               e = Encoding.UTF8;\r
+       \r
+                       StringBuilder output = new StringBuilder ();\r
+                       long len = s.Length;\r
+                       NumberStyles hexa = NumberStyles.HexNumber;\r
+                       MemoryStream bytes = new MemoryStream ();\r
+       \r
+                       for (int i = 0; i < len; i++) {\r
+                               if (s [i] == '%' && i + 2 < len) {\r
+                                       if (s [i + 1] == 'u' && i + 5 < len) {\r
+                                               if (bytes.Length > 0) {\r
+                                                       output.Append (GetChars (bytes, e));\r
+                                                       bytes.SetLength (0);\r
+                                               }\r
+                                               output.Append ((char) Int32.Parse (s.Substring (i + 2, 4), hexa));\r
+                                               i += 5;\r
+                                       } else {\r
+                                               bytes.WriteByte ((byte) Int32.Parse (s.Substring (i + 1, 2), hexa));\r
+                                               i += 2;\r
+                                       }\r
+                                       continue;\r
+                               }\r
+\r
+                               if (bytes.Length > 0) {\r
+                                       output.Append (GetChars (bytes, e));\r
+                                       bytes.SetLength (0);\r
+                               }\r
+\r
+                               if (s [i] == '+') {\r
+                                       output.Append (' ');\r
+                               } else {\r
+                                       output.Append (s [i]);\r
+                               }\r
+                       }\r
+       \r
+                       if (bytes.Length > 0) {\r
+                               output.Append (GetChars (bytes, e));\r
+                       }\r
+\r
+                       bytes = null;\r
+                       return output.ToString ();\r
+               }\r
+       \r
+               public static string UrlDecode (byte [] bytes, Encoding e)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       return UrlDecode (bytes, 0, bytes.Length, e);\r
+               }\r
+\r
+               private static int GetInt (byte b)\r
+               {\r
+                       char c = Char.ToUpper ((char) b);\r
+                       if (c >= '0' && c <= '9')\r
+                               return c - '0';\r
+\r
+                       if (c < 'A' || c > 'F')\r
+                               return 0;\r
+\r
+                       return (c - 'A' + 10);\r
+               }\r
+\r
+               private static char GetChar (byte [] bytes, int offset, int length)\r
+               {\r
+                       int value = 0;\r
+                       int end = length + offset;\r
+                       for (int i = offset; i < end; i++)\r
+                               value = (value << 4) + GetInt (bytes [offset]);\r
+\r
+                       return (char) value;\r
+               }\r
+               \r
+               public static string UrlDecode (byte [] bytes, int offset, int count, Encoding e)\r
+               {\r
+                       if (bytes == null || count == 0)\r
+                               return null;\r
+\r
+                       if (bytes == null)\r
+                               throw new ArgumentNullException ("bytes");\r
+\r
+                       if (offset < 0 || offset > bytes.Length)\r
+                               throw new ArgumentOutOfRangeException ("offset");\r
+\r
+                       if (count < 0 || offset + count > bytes.Length)\r
+                               throw new ArgumentOutOfRangeException ("count");\r
+\r
+                       StringBuilder output = new StringBuilder ();\r
+                       MemoryStream acc = new MemoryStream ();\r
+\r
+                       int end = count + offset;\r
+                       for (int i = offset; i < end; i++) {\r
+                               if (bytes [i] == '%' && i + 2 < count) {\r
+                                       if (bytes [i + 1] == (byte) 'u' && i + 5 < end) {\r
+                                               if (acc.Length > 0) {\r
+                                                       output.Append (GetChars (acc, e));\r
+                                                       acc.SetLength (0);\r
+                                               }\r
+                                               output.Append (GetChar (bytes, offset + 2, 4));\r
+                                               i += 5;\r
+                                       } else {\r
+                                               acc.WriteByte ((byte) GetChar (bytes, offset + 1, 2));\r
+                                               i += 2;\r
+                                       }\r
+                                       continue;\r
+                               }\r
+\r
+                               if (acc.Length > 0) {\r
+                                       output.Append (GetChars (acc, e));\r
+                                       acc.SetLength (0);\r
+                               }\r
+\r
+                               if (bytes [i] == '+') {\r
+                                       output.Append (' ');\r
+                               } else {\r
+                                       output.Append ((char) bytes [i]);\r
+                               }\r
+                       }\r
+\r
+                       if (acc.Length > 0) {\r
+                               output.Append (GetChars (acc, e));\r
+                       }\r
+                       \r
+                       acc = null;\r
+                       return output.ToString ();\r
+               }\r
+       \r
+               public static byte [] UrlDecodeToBytes (byte [] bytes)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       return UrlDecodeToBytes (bytes, 0, bytes.Length);\r
+               }\r
+\r
+               public static byte [] UrlDecodeToBytes (string str)\r
+               {\r
+                       return UrlDecodeToBytes (str, Encoding.UTF8);\r
+               }\r
+\r
+               public static byte [] UrlDecodeToBytes (string str, Encoding e)\r
+               {\r
+                       if (str == null)\r
+                               return null;\r
+\r
+                       if (e == null)\r
+                               throw new ArgumentNullException ("e");\r
+\r
+                       return UrlDecodeToBytes (e.GetBytes (str));\r
+               }\r
+\r
+               public static byte [] UrlDecodeToBytes (byte [] bytes, int offset, int count)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       int len = bytes.Length;\r
+                       if (offset < 0 || offset >= len)\r
+                               throw new ArgumentOutOfRangeException("offset");\r
+\r
+                       if (count < 0 || offset > len - count)\r
+                               throw new ArgumentOutOfRangeException("count");\r
+\r
+                       MemoryStream result = new MemoryStream ();\r
+                       int end = offset + count;\r
+                       for (int i = offset; i < end; i++){\r
+                               char c = (char) bytes [i];\r
+                               if (c == '+')\r
+                                       c = ' ';\r
+                               else if (c == '%' && i < end - 2) {\r
+                                       c = GetChar (bytes, i, 2);\r
+                                       i += 2;\r
+                               }\r
+                               result.WriteByte ((byte) c);\r
+                       }\r
+\r
+                       return result.ToArray ();\r
+               }\r
+\r
+               public static string UrlEncode(string str) \r
+               {\r
+                       return UrlEncode(str, Encoding.UTF8);\r
+               }\r
+       \r
+               public static string UrlEncode (string s, Encoding Enc) \r
+               {\r
+                       if (s == null)\r
+                               return null;\r
+\r
+                       if (s == "")\r
+                               return "";\r
+\r
+                       byte [] bytes = Enc.GetBytes (s);\r
+                       byte [] b =UrlEncodeToBytes (bytes, 0, bytes.Length);\r
+                       return Encoding.ASCII.GetString (b,0,b.Length);\r
+               }\r
+         \r
+               public static string UrlEncode (byte [] bytes)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       if (bytes.Length == 0)\r
+                               return "";\r
+\r
+                       byte []b=UrlEncodeToBytes(bytes, 0, bytes.Length);\r
+                       return Encoding.ASCII.GetString (b,0,b.Length);\r
+               }\r
+\r
+               public static string UrlEncode (byte [] bytes, int offset, int count)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       if (bytes.Length == 0)\r
+                               return "";\r
+\r
+                       byte []b=UrlEncodeToBytes(bytes, offset, count);\r
+                       return Encoding.ASCII.GetString (b,0,b.Length);\r
+               }\r
+\r
+               public static byte [] UrlEncodeToBytes (string str)\r
+               {\r
+                       return UrlEncodeToBytes (str, Encoding.UTF8);\r
+               }\r
+\r
+               public static byte [] UrlEncodeToBytes (string str, Encoding e)\r
+               {\r
+                       if (str == null)\r
+                               return null;\r
+\r
+                       if (str == "")\r
+                               return new byte [0];\r
+\r
+                       byte [] bytes = e.GetBytes (str);\r
+                       return UrlEncodeToBytes (bytes, 0, bytes.Length);\r
+               }\r
+\r
+               public static byte [] UrlEncodeToBytes (byte [] bytes)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       if (bytes.Length == 0)\r
+                               return new byte [0];\r
+\r
+                       return UrlEncodeToBytes (bytes, 0, bytes.Length);\r
+               }\r
+\r
+               static char [] hexChars = "0123456789abcdef".ToCharArray ();\r
+\r
+               public static byte [] UrlEncodeToBytes (byte [] bytes, int offset, int count)\r
+               {\r
+                       if (bytes == null)\r
+                               return null;\r
+\r
+                       int len = bytes.Length;\r
+                       if (len == 0)\r
+                               return new byte [0];\r
+\r
+                       if (offset < 0 || offset >= len)\r
+                               throw new ArgumentOutOfRangeException("offset");\r
+\r
+                       if (count < 0 || count > len - offset)\r
+                               throw new ArgumentOutOfRangeException("count");\r
+\r
+                       MemoryStream result = new MemoryStream ();\r
+                       int end = offset + count;\r
+                       for (int i = offset; i < end; i++) {\r
+                               char c = (char) bytes [i];\r
+                               if ((c == ' ') || (c < '0' && c != '-' && c != '.') ||\r
+                                   (c < 'A' && c > '9') ||\r
+                                   (c > 'Z' && c < 'a' && c != '_') ||\r
+                                   (c > 'z')) {\r
+                                       result.WriteByte ((byte) '%');\r
+                                       int idx = ((int) c) >> 4;\r
+                                       result.WriteByte ((byte) hexChars [idx]);\r
+                                       idx = ((int) c) & 0x0F;\r
+                                       result.WriteByte ((byte) hexChars [idx]);\r
+                               } else {\r
+                                       result.WriteByte ((byte) c);\r
+                               }\r
+                       }\r
+\r
+                       return result.ToArray ();\r
+               }\r
+\r
+               public static string UrlEncodeUnicode (string str)\r
+               {\r
+                       if (str == null)\r
+                               return null;\r
+\r
+                       StringBuilder result = new StringBuilder ();\r
+                       int end = str.Length;\r
+                       for (int i = 0; i < end; i++) {\r
+                               int idx;\r
+                               char c = str [i];\r
+                               if (c > 255) {\r
+                                       result.Append ("%u");\r
+                                       idx = ((int) c) >> 24;\r
+                                       result.Append (hexChars [idx]);\r
+                                       idx = (((int) c) >> 16) & 0x0F;\r
+                                       result.Append (hexChars [idx]);\r
+                                       idx = (((int) c) >> 8) & 0x0F;\r
+                                       result.Append (hexChars [idx]);\r
+                                       idx = ((int) c) & 0x0F;\r
+                                       result.Append (hexChars [idx]);\r
+                                       continue;\r
+                               }\r
+                               \r
+                               if ((c == ' ') || (c < '0' && c != '-' && c != '.') ||\r
+                                   (c < 'A' && c > '9') ||\r
+                                   (c > 'Z' && c < 'a' && c != '_') ||\r
+                                   (c > 'z')) {\r
+                                       result.Append ('%');\r
+                                       idx = ((int) c) >> 4;\r
+                                       result.Append (hexChars [idx]);\r
+                                       idx = ((int) c) & 0x0F;\r
+                                       result.Append (hexChars [idx]);\r
+                                       continue;\r
+                               }\r
+\r
+                               result.Append (c);\r
+                       }\r
+\r
+                       return result.ToString ();\r
+               }\r
+\r
+               public static byte [] UrlEncodeUnicodeToBytes (string str)\r
+               {\r
+                       if (str == null)\r
+                               return null;\r
+\r
+                       if (str == "")\r
+                               return new byte [0];\r
+\r
+                       return Encoding.ASCII.GetBytes (UrlEncodeUnicode (str));\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decodes an HTML-encoded string and returns the decoded string.\r
+               /// </summary>\r
+               /// <param name="s">The HTML string to decode. </param>\r
+               /// <returns>The decoded text.</returns>\r
+               public static string HtmlDecode (string s) \r
+               {\r
+                       if (s == null)\r
+                               throw new ArgumentNullException ("s");\r
+\r
+                       if (s.IndexOf ('&') == -1)\r
+                               return s;\r
+\r
+                       bool insideEntity = false; // used to indicate that we are in a potential entity\r
+                       string entity = String.Empty;\r
+                       StringBuilder output = new StringBuilder ();\r
+                       int len = s.Length;\r
+       \r
+                       for (int i = 0; i < len; i++) {\r
+                               char c = s [i];\r
+                               switch (c) {\r
+                               case '&' :\r
+                                       output.Append (entity);\r
+                                       entity = "&";\r
+                                       insideEntity = true;\r
+                                       break;\r
+                               case ';' :\r
+                                       if (!insideEntity) {\r
+                                               output.Append (c);\r
+                                               break;\r
+                                       }\r
+\r
+                                       entity += c;\r
+                                       int length = entity.Length;\r
+                                       if (length >= 2 && entity[1] == '#' && entity[2] != ';')\r
+                                               entity = ((char) Int32.Parse (entity.Substring (2, entity.Length - 3))).ToString();\r
+                                       else if (length > 1 && Entities.ContainsKey (entity.Substring (1, entity.Length - 2)))\r
+                                               entity = Entities [entity.Substring (1, entity.Length - 2)].ToString ();\r
+                                       \r
+                                       output.Append (entity);\r
+                                       entity = String.Empty;\r
+                                       insideEntity = false;\r
+                                       break;\r
+                               default :\r
+                                       if (insideEntity)\r
+                                               entity += c;\r
+                                       else\r
+                                               output.Append (c);\r
+                                       break;\r
+                               }\r
+                       }\r
+                       output.Append (entity);\r
+                       return output.ToString ();\r
+               }\r
+       \r
+               /// <summary>\r
+               /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.\r
+               /// </summary>\r
+               /// <param name="s">The HTML string to decode</param>\r
+               /// <param name="output">The TextWriter output stream containing the decoded string. </param>\r
+               public static void HtmlDecode(string s, TextWriter output) \r
+               {\r
+                       if (s != null)\r
+                               output.Write (HtmlDecode (s));\r
+               }\r
+       \r
+               /// <summary>\r
+               /// HTML-encodes a string and returns the encoded string.\r
+               /// </summary>\r
+               /// <param name="s">The text string to encode. </param>\r
+               /// <returns>The HTML-encoded text.</returns>\r
+               public static string HtmlEncode (string s) \r
+               {\r
+                       if (s == null)\r
+                               return null;\r
+\r
+                       StringBuilder output = new StringBuilder ();\r
+                       \r
+                       foreach (char c in s) \r
+                               switch (c) {\r
+                               case '&' :\r
+                                       output.Append ("&amp;");\r
+                                       break;\r
+                               case '>' : \r
+                                       output.Append ("&gt;");\r
+                                       break;\r
+                               case '<' :\r
+                                       output.Append ("&lt;");\r
+                                       break;\r
+                               case '"' :\r
+                                       output.Append ("&quot;");\r
+                                       break;\r
+                               default:\r
+                                       if ((int) c > 128) {\r
+                                               output.Append ("&#");\r
+                                               output.Append (((int) c).ToString ());\r
+                                               output.Append (";");\r
+                                       }\r
+                                       else\r
+                                               output.Append (c);\r
+                                       break;\r
+                               }\r
+                       return output.ToString ();\r
+               }\r
+       \r
+               /// <summary>\r
+               /// HTML-encodes a string and sends the resulting output to a TextWriter output stream.\r
+               /// </summary>\r
+               /// <param name="s">The string to encode. </param>\r
+               /// <param name="output">The TextWriter output stream containing the encoded string. </param>\r
+               public static void HtmlEncode(string s, TextWriter output) \r
+               {\r
+                       if (s != null)\r
+                               output.Write (HtmlEncode (s));\r
+               }\r
+\r
+#if NET_1_1\r
+               public string UrlPathEncode (string s)\r
+               {\r
+                       if (s == null)\r
+                               return null;\r
+\r
+                       int idx = s.IndexOf ("?");\r
+                       string s2 = null;\r
+                       if (idx != -1) {\r
+                               s2 = s.Substring (0, idx-1);\r
+                               s2 = UrlEncode (s2) + s.Substring (idx);\r
+                       } else {\r
+                               s2 = UrlEncode (s);\r
+                       }\r
+\r
+                       return s2;\r
+               }\r
+#endif\r
+               #endregion // Methods\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/Index.cs b/irc/TechBot/CHMLibrary/Index.cs
new file mode 100644 (file)
index 0000000..76f7301
--- /dev/null
@@ -0,0 +1,322 @@
+using System;\r
+using System.Diagnostics;\r
+using System.Collections;\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// Enumeration for specifying the index type\r
+       /// </summary>\r
+       public enum IndexType\r
+       {\r
+               /// <summary>\r
+               /// Keyword links should be used\r
+               /// </summary>\r
+               KeywordLinks = 0,\r
+               /// <summary>\r
+               /// Associative links should be used\r
+               /// </summary>\r
+               AssiciativeLinks = 1\r
+       }\r
+\r
+       /// <summary>\r
+       /// The class <c>Index</c> holds the (keyword links) KLinks and (associative links) ALinks of the htmlhelp \r
+       /// system. It implements methods for easy index-based searching.\r
+       /// </summary>\r
+       public class Index\r
+       {\r
+               private ArrayList _kLinks = new ArrayList();\r
+               private ArrayList _aLinks = new ArrayList();\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               public Index()\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="kLinks">arraylist with keyword links</param>\r
+               /// <param name="aLinks">arraylist with associative links</param>\r
+               public Index(ArrayList kLinks, ArrayList aLinks)\r
+               {\r
+                       _kLinks= kLinks;\r
+                       _aLinks = aLinks;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Clears the current toc\r
+               /// </summary>\r
+               public void Clear()\r
+               {\r
+                       if(_aLinks != null)\r
+                               _aLinks.Clear();\r
+                       if(_kLinks != null)\r
+                               _kLinks.Clear();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the number of index items for a specific type\r
+               /// </summary>\r
+               /// <param name="typeOfIndex">type of index</param>\r
+               /// <returns>Returns the number of index items for a specific type</returns>\r
+               public int Count(IndexType typeOfIndex)\r
+               {\r
+                       ArrayList _index = null;\r
+\r
+                       switch( typeOfIndex )\r
+                       {\r
+                               case IndexType.AssiciativeLinks: _index = _aLinks; break;\r
+                               case IndexType.KeywordLinks: _index = _kLinks; break;\r
+                       }\r
+\r
+                       if(_index != null)\r
+                               return _index.Count;\r
+\r
+                       return 0;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal index list of keyword links\r
+               /// </summary>\r
+               public ArrayList KLinks\r
+               {\r
+                       get \r
+                       {\r
+                               if(_kLinks==null)\r
+                                       _kLinks = new ArrayList();\r
+\r
+                               return _kLinks;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal index list of associative links\r
+               /// </summary>\r
+               public ArrayList ALinks\r
+               {\r
+                       get \r
+                       {\r
+                               if(_aLinks==null)\r
+                                       _aLinks = new ArrayList();\r
+\r
+                               return _aLinks;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges the the index list <c>arrIndex</c> into the current one\r
+               /// </summary>\r
+               /// <param name="arrIndex">indexlist which should be merged with the current one</param>\r
+               /// <param name="typeOfIndex">type of index to merge</param>\r
+               public void MergeIndex( ArrayList arrIndex, IndexType typeOfIndex )\r
+               {\r
+                       ArrayList _index = null;\r
+\r
+                       switch(typeOfIndex)\r
+                       {\r
+                               case IndexType.AssiciativeLinks: _index = _aLinks;break;\r
+                               case IndexType.KeywordLinks: _index = _kLinks;break;\r
+                       }\r
+                       \r
+                       foreach(IndexItem curItem in arrIndex)\r
+                       {\r
+                               //IndexItem searchItem = ContainsIndex(_index, curItem.KeyWordPath);\r
+                               int insertIndex=0;\r
+                               IndexItem searchItem = BinSearch(0, _index.Count-1, _index, curItem.KeyWordPath, false, false, ref insertIndex);\r
+\r
+                               if(searchItem != null)\r
+                               {\r
+                                       // extend the keywords topics\r
+                                       foreach(IndexTopic curEntry in curItem.Topics)\r
+                                       {\r
+                                               searchItem.Topics.Add( curEntry );\r
+                                       }\r
+                               } \r
+                               else \r
+                               {\r
+                                       // add the item to the global collection\r
+                                       //_index.Add( curItem );\r
+\r
+                                       if(insertIndex > _index.Count)\r
+                                               _index.Add(curItem);\r
+                                       else\r
+                                       _index.Insert(insertIndex, curItem);\r
+                               }\r
+                       }                       \r
+               }\r
+\r
+               /// <summary>\r
+               /// Searches an index entry using recursive binary search algo (divide and conquer).\r
+               /// </summary>\r
+               /// <param name="nStart">start index for searching</param>\r
+               /// <param name="nEnd">end index for searching</param>\r
+               /// <param name="arrIndex">arraylist containing sorted IndexItem entries</param>\r
+               /// <param name="keywordPath">keyword path to search</param>\r
+               /// <param name="searchKeyword">true if the keywordPath will only contain the keyword not the complete path</param>\r
+               /// <param name="caseInsensitive">True if case should be ignored</param>\r
+               /// <param name="insertIndex">out reference. will receive the index where the item with the\r
+               /// keywordPath should be inserted if not found (receives -1 if the item was found)</param>\r
+               /// <returns>Returns an IndexItem instance if found, otherwise null \r
+               /// (use insertIndex for inserting the new item in a sorted order)</returns>\r
+               private IndexItem BinSearch(int nStart, int nEnd, ArrayList arrIndex, string keywordPath, \r
+                       bool searchKeyword, bool caseInsensitive, ref int insertIndex)\r
+               {\r
+                       if( arrIndex.Count <= 0 )\r
+                       {\r
+                               insertIndex=0;\r
+                               return null;\r
+                       }\r
+\r
+                       if(caseInsensitive)\r
+                               keywordPath = keywordPath.ToLower();\r
+\r
+                       if( (nEnd - nStart) > 1)\r
+                       {\r
+                               int nCheck = nStart + (nEnd-nStart)/2;\r
+\r
+                               IndexItem iC = arrIndex[nCheck] as IndexItem;\r
+\r
+                               string sCompare = iC.KeyWordPath;\r
+\r
+                               if(searchKeyword)\r
+                                       sCompare = iC.KeyWord;\r
+\r
+                               if(caseInsensitive)\r
+                                       sCompare = sCompare.ToLower();\r
+\r
+                               if( sCompare == keywordPath )\r
+                               {\r
+                                       insertIndex=-1;\r
+                                       return iC;\r
+                               }\r
+\r
+                               if( keywordPath.CompareTo(sCompare) < 0 )\r
+                               {\r
+                                       return BinSearch(nStart, nCheck-1, arrIndex, keywordPath, searchKeyword, caseInsensitive, ref insertIndex);\r
+                               }\r
+\r
+                               if( keywordPath.CompareTo(sCompare) > 0 )\r
+                               {\r
+                                       return BinSearch(nCheck+1, nEnd, arrIndex, keywordPath, searchKeyword, caseInsensitive, ref insertIndex);\r
+                               }\r
+                       }\r
+                       else if(nEnd-nStart == 1)\r
+                       {\r
+                               IndexItem i1 = arrIndex[nStart] as IndexItem;\r
+                               IndexItem i2 = arrIndex[nEnd] as IndexItem;\r
+\r
+                               string sCompare1 = i1.KeyWordPath;\r
+\r
+                               if(searchKeyword)\r
+                                       sCompare1 = i1.KeyWord;\r
+\r
+                               if(caseInsensitive)\r
+                                       sCompare1 = sCompare1.ToLower();\r
+\r
+                               string sCompare2 = i2.KeyWordPath;\r
+\r
+                               if(searchKeyword)\r
+                                       sCompare2 = i2.KeyWord;\r
+\r
+                               if(caseInsensitive)\r
+                                       sCompare2 = sCompare2.ToLower();\r
+\r
+                               if( sCompare1 == keywordPath)\r
+                               {\r
+                                       insertIndex = -1;\r
+                                       return i1;\r
+                               }\r
+\r
+                               if( sCompare2 == keywordPath)\r
+                               {\r
+                                       insertIndex = -1;\r
+                                       return i2;\r
+                               }\r
+\r
+                               if( sCompare1.CompareTo(keywordPath) > 0)\r
+                               {\r
+                                       insertIndex = nStart;\r
+                                       return null;\r
+                               } \r
+                               else if( sCompare2.CompareTo(keywordPath) > 0)\r
+                               {\r
+                                       insertIndex = nEnd;\r
+                                       return null;\r
+                               } \r
+                               else \r
+                               {\r
+                                       insertIndex = nEnd+1;\r
+                               }\r
+                       }\r
+\r
+                       IndexItem itm = arrIndex[nEnd] as IndexItem;\r
+\r
+                       string sCompareI = itm.KeyWordPath;\r
+\r
+                       if(searchKeyword)\r
+                               sCompareI = itm.KeyWord;\r
+\r
+                       if(caseInsensitive)\r
+                               sCompareI = sCompareI.ToLower();\r
+\r
+                       if( sCompareI.CompareTo(keywordPath) > 0)\r
+                       {\r
+                               insertIndex = nStart;\r
+                               return null;\r
+                       } \r
+                       else if( sCompareI.CompareTo(keywordPath) < 0)\r
+                       {\r
+                               insertIndex = nEnd+1;\r
+                               return null;\r
+                       }\r
+                       else\r
+                       {\r
+                               insertIndex = -1;\r
+                               return arrIndex[nEnd] as IndexItem;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if a keyword exists in a index collection\r
+               /// </summary>\r
+               /// <param name="arrIndex">index to search (arraylist of IndexItems)</param>\r
+               /// <param name="keywordPath">keywordpath to search</param>\r
+               /// <returns>Returns the found IndexItem, otherwise null</returns>\r
+               private IndexItem ContainsIndex(ArrayList arrIndex, string keywordPath)\r
+               {\r
+                       foreach(IndexItem curItem in arrIndex)\r
+                       {\r
+                               if(curItem.KeyWordPath == keywordPath)\r
+                                       return curItem;\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Searches the alinks- or klinks-index for a specific keyword/associative\r
+               /// </summary>\r
+               /// <param name="search">keyword/associative to search</param>\r
+               /// <param name="typeOfIndex">type of index to search</param>\r
+               /// <returns>Returns an ArrayList which contains IndexTopic items or null if nothing was found</returns>\r
+               public IndexItem SearchIndex(string search, IndexType typeOfIndex)\r
+               {\r
+                       ArrayList _index = null;\r
+\r
+                       switch( typeOfIndex )\r
+                       {\r
+                               case IndexType.AssiciativeLinks: _index = _aLinks;break;\r
+                               case IndexType.KeywordLinks: _index = _kLinks;break;\r
+                       }\r
+\r
+                       int insertIdx=0;\r
+                       IndexItem foundItem = BinSearch(0, _index.Count, _index, search, true, true, ref insertIdx);\r
+\r
+                       return foundItem;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/IndexItem.cs b/irc/TechBot/CHMLibrary/IndexItem.cs
new file mode 100644 (file)
index 0000000..a214158
--- /dev/null
@@ -0,0 +1,396 @@
+using System;\r
+using System.IO;\r
+using System.Text;\r
+using System.Collections;\r
+\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>IndexItem</c> implements an help-index item\r
+       /// </summary>\r
+       public sealed class IndexItem : IComparable\r
+       {\r
+               /// <summary>\r
+               /// Internal member storing the keyword\r
+               /// </summary>\r
+               private string _keyWord = "";\r
+               /// <summary>\r
+               /// Internal member storing all associated information type strings\r
+               /// </summary>\r
+               private ArrayList _infoTypeStrings = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the flag if this is a see-also keyword\r
+               /// </summary>\r
+               private bool _isSeeAlso = false;\r
+               /// <summary>\r
+               /// Internal member storing the indent of the keyword\r
+               /// </summary>\r
+               private int _indent = 0;\r
+               /// <summary>\r
+               /// Internal member storing the last index of the keyword in the seperated list\r
+               /// </summary>\r
+               private int _charIndex = 0;\r
+               /// <summary>\r
+               /// Internal member storing the entry index\r
+               /// </summary>\r
+               private int _entryIndex = 0;\r
+               /// <summary>\r
+               /// Internal member storing an array of see-also values\r
+               /// </summary>\r
+               private string[] _seeAlso = new string[0];\r
+               /// <summary>\r
+               /// Internal member storing an array of topic offsets\r
+               /// </summary>\r
+               private int[] _nTopics = new int[0];\r
+               /// <summary>\r
+               /// Internal member storing the topics\r
+               /// </summary>\r
+               private ArrayList _Topics = null;\r
+               /// <summary>\r
+               /// Associated CHMFile instance\r
+               /// </summary>\r
+               private CHMFile _chmFile = null;\r
+               /// <summary>\r
+               /// Internal flag specifying the chm file path\r
+               /// </summary>\r
+               private string _chmFileName = "";\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="chmFile">associated CHMFile instance</param>\r
+               /// <param name="keyWord">keyword</param>\r
+               /// <param name="isSeeAlso">true if it is a see-also keyword</param>\r
+               /// <param name="indent">indent of the entry</param>\r
+               /// <param name="charIndex">char index of the last keyword in the separated list</param>\r
+               /// <param name="entryIndex">index of the entry</param>\r
+               /// <param name="seeAlsoValues">string array with see-also values</param>\r
+               /// <param name="topicOffsets">integer array with topic offsets</param>\r
+               internal IndexItem(CHMFile chmFile, string keyWord, bool isSeeAlso, int indent, int charIndex, int entryIndex, string[] seeAlsoValues, int[] topicOffsets)\r
+               {\r
+                       _chmFile = chmFile;\r
+                       _chmFileName = _chmFile.ChmFilePath;\r
+                       _keyWord = keyWord;\r
+                       _isSeeAlso = isSeeAlso;\r
+                       _indent = indent;\r
+                       _charIndex = charIndex;\r
+                       _entryIndex = entryIndex;\r
+                       _seeAlso = seeAlsoValues;\r
+                       _nTopics = topicOffsets;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               public IndexItem()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               /// <param name="writeFileName">true if the chm filename should be written</param>\r
+               internal void Dump(ref BinaryWriter writer, bool writeFileName)\r
+               {\r
+                       int i=0;\r
+\r
+                       writer.Write(_keyWord);\r
+                       writer.Write(_isSeeAlso);\r
+                       writer.Write(_indent);\r
+\r
+                       if(writeFileName)\r
+                               writer.Write(_chmFileName);\r
+\r
+                       writer.Write(_infoTypeStrings.Count);\r
+\r
+                       for(i=0; i<_infoTypeStrings.Count; i++)\r
+                               writer.Write( (_infoTypeStrings[i]).ToString() );\r
+\r
+                       writer.Write(_seeAlso.Length);\r
+\r
+                       for(i=0; i<_seeAlso.Length; i++)\r
+                       {\r
+                               if(_seeAlso[i] == null)\r
+                                       writer.Write("");\r
+                               else\r
+                                       writer.Write( _seeAlso[i] );\r
+                       }\r
+\r
+                       writer.Write(Topics.Count);\r
+\r
+                       for(i=0; i<Topics.Count; i++)\r
+                       {\r
+                               IndexTopic topic = ((IndexTopic)(Topics[i]));\r
+                               topic.Dump(ref writer);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       Dump(ref writer, false);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               /// <param name="filesList">filelist from helpsystem</param>\r
+               internal bool ReadDump(ref BinaryReader reader, ArrayList filesList)\r
+               {\r
+                       int i=0;\r
+                       _keyWord = reader.ReadString();\r
+                       _isSeeAlso = reader.ReadBoolean();\r
+                       _indent = reader.ReadInt32();\r
+                       _chmFileName = reader.ReadString();\r
+\r
+                       foreach(CHMFile curFile in filesList)\r
+                       {\r
+                               if(curFile.ChmFilePath == _chmFileName)\r
+                               {\r
+                                       _chmFile = curFile;\r
+                                       break;\r
+                               }\r
+                       }\r
+\r
+                       if(_chmFile==null)\r
+                               return false;\r
+\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               string sIT = reader.ReadString();\r
+                               _infoTypeStrings.Add(sIT);\r
+                       }\r
+\r
+                       nCnt = reader.ReadInt32();\r
+\r
+                       _seeAlso = new string[nCnt];\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               _seeAlso[i] = reader.ReadString();\r
+                       }\r
+\r
+                       nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               IndexTopic topic = new IndexTopic("","","","");\r
+                               topic.SetChmInfo( _chmFile.CompileFile, _chmFile.ChmFilePath);\r
+                               topic.AssociatedFile = _chmFile;\r
+                               topic.ReadDump(ref reader);\r
+                               \r
+                               Topics.Add(topic);\r
+                       }\r
+\r
+                       return true;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       int i=0;\r
+                       _keyWord = reader.ReadString();\r
+                       _isSeeAlso = reader.ReadBoolean();\r
+                       _indent = reader.ReadInt32();\r
+\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               string sIT = reader.ReadString();\r
+                               _infoTypeStrings.Add(sIT);\r
+                       }\r
+\r
+                       nCnt = reader.ReadInt32();\r
+\r
+                       _seeAlso = new string[nCnt];\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               _seeAlso[i] = reader.ReadString();\r
+                       }\r
+\r
+                       nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               IndexTopic topic = new IndexTopic("","","","");\r
+                               topic.AssociatedFile = _chmFile;\r
+                               topic.SetChmInfo( _chmFile.CompileFile, _chmFile.ChmFilePath);\r
+                               topic.ReadDump(ref reader);\r
+                               Topics.Add(topic);\r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Implements the compareto method which allows sorting.\r
+               /// </summary>\r
+               /// <param name="obj">object to compare to</param>\r
+               /// <returns>See <see cref="System.IComparable">IComparable.CompareTo()</see></returns>\r
+               public int CompareTo(object obj)\r
+               {\r
+                       if( obj.GetType() == this.GetType() )\r
+                       {\r
+                               IndexItem cmp = (IndexItem)obj;\r
+\r
+                               return this.KeyWordPath.CompareTo( cmp.KeyWordPath );\r
+                       }\r
+\r
+                       return 0;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the associated CHMFile instance\r
+               /// </summary>\r
+               internal CHMFile ChmFile\r
+               {\r
+                       get { return _chmFile; }\r
+                       set { _chmFile = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the ArrayList which holds all information types/categories this item is associated\r
+               /// </summary>\r
+               internal ArrayList InfoTypeStrings\r
+               {\r
+                       get { return _infoTypeStrings; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Adds a see-also string to the index item and marks it as see also item\r
+               /// </summary>\r
+               /// <param name="seeAlsoString">see also string to add</param>\r
+               internal void AddSeeAlso(string seeAlsoString)\r
+               {\r
+                       string[] seeAlso = new string[ _seeAlso.Length +1 ];\r
+                       for(int i=0; i<_seeAlso.Length; i++)\r
+                               seeAlso[i] = _seeAlso[i];\r
+\r
+                       seeAlso[_seeAlso.Length] = seeAlsoString;\r
+                       _seeAlso = seeAlso;\r
+                       _isSeeAlso = true;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the full keyword-path of this item ( ", " separated list)\r
+               /// </summary>\r
+               public string KeyWordPath\r
+               {\r
+                       get { return _keyWord; }\r
+                       set { _keyWord = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the keyword of this item\r
+               /// </summary>\r
+               public string KeyWord\r
+               {\r
+                       get \r
+                       { \r
+                               return _keyWord.Substring(_charIndex, _keyWord.Length-_charIndex); \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the keyword of this item with prefixing indent spaces\r
+               /// </summary>\r
+               public string IndentKeyWord\r
+               {\r
+                       get\r
+                       {\r
+                               string sKW = this.KeyWord;\r
+                               StringBuilder sb = new StringBuilder("",this.Indent*3 + sKW.Length);\r
+                               for(int i=0; i<this.Indent; i++)\r
+                                       sb.Append("   ");\r
+                               sb.Append(sKW);\r
+                               return sb.ToString();\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the see-also flag of this item\r
+               /// </summary>\r
+               public bool IsSeeAlso\r
+               {\r
+                       get { return _isSeeAlso; }\r
+                       set { _isSeeAlso = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the listbox indent for this item\r
+               /// </summary>\r
+               public int Indent\r
+               {\r
+                       get { return _indent; }\r
+                       set { _indent = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the character index of an indent keyword\r
+               /// </summary>\r
+               public int CharIndex\r
+               {\r
+                       get { return _charIndex; }\r
+                       set { _charIndex = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the see-also values of this item\r
+               /// </summary>\r
+               public string[] SeeAlso\r
+               {\r
+                       get { return _seeAlso; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets an array with the associated topics\r
+               /// </summary>\r
+               public ArrayList Topics\r
+               {\r
+                       get \r
+                       {\r
+                               if( _Topics == null )\r
+                               {\r
+                                       if(IsSeeAlso)\r
+                                       {\r
+                                               _Topics = new ArrayList();\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               if( (_chmFile != null) && (_chmFile.TopicsFile != null) )\r
+                                               {\r
+                                                       _Topics = new ArrayList();\r
+\r
+                                                       for(int i=0; i<_nTopics.Length; i++)\r
+                                                       {\r
+                                                               IndexTopic newTopic = IndexTopic.FromTopicEntry((TopicEntry)_chmFile.TopicsFile.TopicTable[ _nTopics[i] ]);\r
+                                                               newTopic.AssociatedFile = _chmFile;\r
+                                                               _Topics.Add( newTopic );\r
+                                                       }\r
+                                               } \r
+                                               else \r
+                                               {\r
+                                                       _Topics = new ArrayList();\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _Topics;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/IndexTopic.cs b/irc/TechBot/CHMLibrary/IndexTopic.cs
new file mode 100644 (file)
index 0000000..07c8f66
--- /dev/null
@@ -0,0 +1,216 @@
+using System;\r
+using System.IO;\r
+\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>IndexTopic</c> implements an entry for the <see cref="IndexItem">IndexItem</see> topics list.\r
+       /// </summary>\r
+       public sealed class IndexTopic\r
+       {\r
+               private DataMode _topicMode = DataMode.TextBased;\r
+               private string _title="";\r
+               private string _local="";\r
+               private string _compileFile = "";\r
+               private string _chmPath = "";\r
+               private int _topicOffset = -1;\r
+               private CHMFile _associatedFile = null;\r
+\r
+               /// <summary>\r
+               /// Creates a new instance of the class based on an existing TopicEntry\r
+               /// </summary>\r
+               /// <param name="entry"></param>\r
+               internal static IndexTopic FromTopicEntry(TopicEntry entry)\r
+               {\r
+                       return new IndexTopic(entry.EntryOffset, entry.ChmFile);\r
+                       //return new IndexTopic( entry.Title, entry.Locale, entry.ChmFile.CompileFile, entry.ChmFile.ChmFilePath);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Creates a new instance of the class (binary extraction mode)\r
+               /// </summary>\r
+               /// <param name="topicOffset">offset of the topic entry</param>\r
+               /// <param name="associatedFile">associated CHMFile instance</param>\r
+               internal IndexTopic(int topicOffset, CHMFile associatedFile)\r
+               {\r
+                       _topicMode = DataMode.Binary;\r
+                       _topicOffset = topicOffset;\r
+                       _associatedFile = associatedFile;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="Title">topic title</param>\r
+               /// <param name="local">topic local (content filename)</param>\r
+               /// <param name="compilefile">name of the chm file (location of topic)</param>\r
+               /// <param name="chmpath">path of the chm file</param>\r
+               public IndexTopic(string Title, string local, string compilefile, string chmpath)\r
+               {\r
+                       _topicMode = DataMode.TextBased;\r
+                       _title = Title;\r
+                       _local = local;\r
+                       _compileFile = compilefile;\r
+                       _chmPath = chmpath;\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write((int)_topicMode);\r
+\r
+                       if(_topicMode==DataMode.TextBased)\r
+                       {\r
+                               writer.Write(_title);\r
+                               writer.Write(_local);\r
+                       } \r
+                       else \r
+                       {\r
+                               writer.Write(_topicOffset);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       _topicMode = (DataMode)reader.ReadInt32();\r
+\r
+                       if(_topicMode==DataMode.TextBased)\r
+                       {\r
+                               _title = reader.ReadString();\r
+                               _local = reader.ReadString();\r
+                       } \r
+                       else \r
+                       {\r
+                               _topicOffset = reader.ReadInt32();\r
+                       }\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Internally used to set the chm-finos when reading from dump store\r
+               /// </summary>\r
+               /// <param name="compilefile"></param>\r
+               /// <param name="chmpath"></param>\r
+               internal void SetChmInfo(string compilefile, string chmpath)\r
+               {\r
+                       _compileFile = compilefile;\r
+                       _chmPath = chmpath;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the associated CHMFile instance\r
+               /// </summary>\r
+               internal CHMFile AssociatedFile\r
+               {\r
+                       get { return _associatedFile; }\r
+                       set { _associatedFile = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the topic title\r
+               /// </summary>\r
+               public string Title\r
+               {\r
+                       get \r
+                       { \r
+                               if((_topicMode == DataMode.Binary )&&(_associatedFile!=null))\r
+                               {\r
+                                       if( _topicOffset >= 0)\r
+                                       {\r
+                                               TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);\r
+                                               if(te != null)\r
+                                               {\r
+                                                       return te.Title;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _title; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the local (content filename)\r
+               /// </summary>\r
+               public string Local\r
+               {\r
+                       get \r
+                       {\r
+                               if((_topicMode == DataMode.Binary )&&(_associatedFile!=null))\r
+                               {\r
+                                       if( _topicOffset >= 0)\r
+                                       {\r
+                                               TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);\r
+                                               if(te != null)\r
+                                               {\r
+                                                       return te.Locale;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _local; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the compile file (location)\r
+               /// </summary>\r
+               public string CompileFile\r
+               {\r
+                       get \r
+                       { \r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.CompileFile;\r
+\r
+                               return _compileFile; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the chm file path\r
+               /// </summary>\r
+               public string ChmFilePath\r
+               {\r
+                       get \r
+                       {\r
+                               if(_associatedFile != null)\r
+                                       return _associatedFile.ChmFilePath;\r
+\r
+                               return _chmPath; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the url\r
+               /// </summary>\r
+               public string URL\r
+               {\r
+                       get\r
+                       {\r
+                               string sL = Local;\r
+\r
+                               if(sL.Length<=0)\r
+                                       return "";//"about:blank";\r
+\r
+                               if( (sL.ToLower().IndexOf("http://") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("https://") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("mailto:") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("ftp://") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("ms-its:") >= 0))\r
+                                       return sL;\r
+\r
+                               return HtmlHelpSystem.UrlPrefix + ChmFilePath + "::/" + sL;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/InformationType.cs b/irc/TechBot/CHMLibrary/InformationType.cs
new file mode 100644 (file)
index 0000000..7f23e5c
--- /dev/null
@@ -0,0 +1,146 @@
+using System;\r
+using System.IO;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// Enumeration for specifying the mode of the information type\r
+       /// </summary>\r
+       public enum InformationTypeMode\r
+       {\r
+               /// <summary>\r
+               /// Inclusive information type. The user will be allowed to select from one or more information types.\r
+               /// </summary>\r
+               Inclusive = 0,\r
+               /// <summary>\r
+               /// Exclusive information type. The user will be allowed to choose only one information type within each category\r
+               /// </summary>\r
+               Exclusive = 1,\r
+               /// <summary>\r
+               /// Hidden information type. The user cannot see this information types (only for API calls).\r
+               /// </summary>\r
+               Hidden = 2\r
+       }\r
+\r
+       /// <summary>\r
+       /// The class <c>InformationType</c> implements a methods/properties for an information type.\r
+       /// </summary>\r
+       /// <remarks>Note: Information types and categories allow users to filter help contents. \r
+       /// They are only supported if using sitemap TOC and/or sitemap Index.</remarks>\r
+       public class InformationType\r
+       {\r
+               private string _name = "";\r
+               private string _description = "";\r
+               private InformationTypeMode _typeMode = InformationTypeMode.Inclusive;\r
+               private bool _isInCategory = false;\r
+               private int _referenceCount = 1;\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               /// <remarks>the mode is set to InformationTypeMode.Inclusive by default</remarks>\r
+               public InformationType() : this("","")\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               /// <param name="name">name of the information type</param>\r
+               /// <param name="description">description</param>\r
+               /// <remarks>the mode is set to InformationTypeMode.Inclusive by default</remarks>\r
+               public InformationType(string name, string description) : this(name, description, InformationTypeMode.Inclusive)\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               /// <param name="name">name of the information type</param>\r
+               /// <param name="description">description</param>\r
+               /// <param name="mode">mode of the information type</param>\r
+               public InformationType(string name, string description, InformationTypeMode mode)\r
+               {\r
+                       _name = name;\r
+                       _description = description;\r
+                       _typeMode = mode;\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       writer.Write( (int)_typeMode );\r
+                       writer.Write( _name );\r
+                       writer.Write( _description );\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       _typeMode = (InformationTypeMode)reader.ReadInt32();\r
+                       _name = reader.ReadString();\r
+                       _description = reader.ReadString();\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Sets the flag if this information type is nested in at least one category \r
+               /// </summary>\r
+               /// <param name="newValue">true or false</param>\r
+               internal void SetCategoryFlag(bool newValue)\r
+               {\r
+                       _isInCategory = newValue;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the reference count of this information type instance\r
+               /// </summary>\r
+               internal int ReferenceCount\r
+               {\r
+                       get { return _referenceCount; }\r
+                       set { _referenceCount = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets true if this information type is nested in at least one category\r
+               /// </summary>\r
+               public bool IsInCategory\r
+               {\r
+                       get { return _isInCategory; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the name of the information type\r
+               /// </summary>\r
+               public string Name\r
+               {\r
+                       get { return _name; }\r
+                       set { _name = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the description of the information type\r
+               /// </summary>\r
+               public string Description\r
+               {\r
+                       get { return _description; }\r
+                       set { _name = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the mode of the information type\r
+               /// </summary>\r
+               public InformationTypeMode Mode\r
+               {\r
+                       get { return _typeMode; }\r
+                       set { _typeMode = value; }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/Storage/CHMStream.cs b/irc/TechBot/CHMLibrary/Storage/CHMStream.cs
new file mode 100644 (file)
index 0000000..8eab659
--- /dev/null
@@ -0,0 +1,2842 @@
+using System;\r
+using System.Diagnostics;\r
+using System.Text;\r
+using System.Data;\r
+using System.Text.RegularExpressions;\r
+using System.Collections;\r
+using System.Collections.Specialized;\r
+using System.IO;\r
+using System.Runtime.InteropServices;\r
+\r
+namespace CHMStream\r
+{\r
+       /// <summary>\r
+       /// Summary description for CHMFile.\r
+       /// </summary>\r
+       ///\r
+       public class CHMStream : IDisposable\r
+       {\r
+               public MemoryStream OpenStream(chmUnitInfo Info)\r
+               {\r
+                       if (Info==null)\r
+                               return null;\r
+\r
+                       MemoryStream st=new MemoryStream();\r
+                       this.ExtractFile(Info,st);\r
+                       return st;\r
+               }\r
+\r
+               public MemoryStream OpenStream(string FileName)\r
+               {\r
+                       chmUnitInfo info=this.GetFileInfo(FileName);\r
+                       if (info==null)\r
+                               return null;\r
+                       return OpenStream(info);\r
+               }\r
+\r
+               private string m_CHMFileName;\r
+               public string CHMFileName\r
+               {\r
+                       get\r
+                       {\r
+                               return m_CHMFileName;\r
+                       }\r
+               }\r
+\r
+               public CHMStream()\r
+               {\r
+               }\r
+\r
+               public CHMStream(string CHMFileName)\r
+               {\r
+                       OpenCHM(CHMFileName);\r
+               }\r
+\r
+               public void OpenCHM(string CHMFileName)\r
+               {                       \r
+                       m_CHMFileName=CHMFileName;\r
+                       FileInfo fi=new FileInfo(m_CHMFileName);\r
+                       Dir=fi.DirectoryName;\r
+                       m_CHMName=fi.Name;\r
+                       fi=null;                        \r
+                       chm_open(m_CHMFileName);\r
+               }\r
+\r
+               private bool m_bCHMLoaded=false;\r
+               public bool CHMLoaded\r
+               {\r
+                       get\r
+                       {\r
+                               return m_bCHMLoaded;\r
+                       }\r
+               }\r
+\r
+               private string m_CHMName="";\r
+               public string CHMName\r
+               {\r
+                       get \r
+                       {\r
+                               return m_CHMName;\r
+                       }\r
+               }\r
+\r
+               private string Dir="";\r
+               private string m_FileFind="";\r
+               private string m_FileFindLastPart="";\r
+               private chmUnitInfo m_FileInfo=null;\r
+               private int m_FileCount=0;\r
+               public void FindFile(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {\r
+                       string LocalFile=Info.path;\r
+                       LocalFile=LocalFile.Replace("/",@"\");\r
+                       if (!LocalFile.StartsWith(@"\"))\r
+                               LocalFile=@"\"+LocalFile;\r
+                       LocalFile=LocalFile.ToLower();\r
+                                                       \r
+                       if (m_FileFind.Length<=LocalFile.Length)\r
+                       {                               \r
+                               if (LocalFile.IndexOf(m_FileFind)==LocalFile.Length-m_FileFind.Length)\r
+                               {\r
+                                       Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS;\r
+                                       m_FileInfo=Info;\r
+                                       return;\r
+                               }                       \r
+                       }\r
+                       Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+               }\r
+\r
+               public void FileCount(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {\r
+                       m_FileCount++;\r
+                       Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+               }\r
+\r
+               private ArrayList m_FileList=null;\r
+               private string m_strByExt="";\r
+               public void FileList(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {                       \r
+                       m_FileList.Add(Info.path);\r
+                       Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+               }\r
+\r
+               public void FileListByExtension(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {                       \r
+                       FileInfo fi=new FileInfo(Info.path);\r
+                       if (fi.Extension.ToLower()==m_strByExt.ToLower())\r
+                               m_FileList.Add(Info.path);\r
+                       Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+               }\r
+\r
+               public void FindFileIndex(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {                       \r
+                       if (m_FileCount==m_FileFindIndex)\r
+                       {\r
+                               m_FileInfo=Info;\r
+                               Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS;\r
+                       }\r
+                       else\r
+                       {\r
+                               m_FileCount++;\r
+                               Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+                       }\r
+               }\r
+\r
+               public int GetFileCount()\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return 0;\r
+\r
+                       m_FileCount=0;\r
+                       this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileCount);                  \r
+                       this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);\r
+                       this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileCount);\r
+                       return m_FileCount;\r
+               }               \r
+\r
+               public ArrayList GetFileList()\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return null;\r
+\r
+                       m_FileList=null;\r
+                       m_FileList=new ArrayList(1000);\r
+                       this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileList);                   \r
+                       this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);\r
+                       this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileList);\r
+                       return m_FileList;\r
+               }               \r
+\r
+               public ArrayList GetFileListByExtenstion(string Ext)\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return null;\r
+\r
+                       m_FileList=null;\r
+                       m_FileList=new ArrayList(1000);\r
+                       m_strByExt=Ext;\r
+                       this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileListByExtension);                        \r
+                       this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);\r
+                       this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileListByExtension);\r
+                       return m_FileList;\r
+               }               \r
+\r
+               public chmUnitInfo GetFileInfo(string FileName)\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return null;\r
+                       \r
+                       m_FileFind=FileName.ToLower().Replace("/",@"\");        \r
+\r
+                       // Remove all leading '..\'                     \r
+                       do\r
+                       {                               \r
+                               if (m_FileFind.StartsWith(@"..\"))\r
+                                       m_FileFind=m_FileFind.Substring(3);                                     \r
+                               else \r
+                                       break;\r
+                       }\r
+                       while(true);\r
+\r
+                       if (!m_FileFind.StartsWith(@"\"))\r
+                               m_FileFind=@"\"+m_FileFind;\r
+                               \r
+                       string []parts=m_FileFind.Split('\\');\r
+                       m_FileFindLastPart=@"\"+parts[parts.GetUpperBound(0)];                  \r
+\r
+                       this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFile);\r
+                       m_FileInfo=null;\r
+                       this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);\r
+                       this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFile);\r
+                       return m_FileInfo;\r
+               }               \r
+               \r
+               private int m_FileFindIndex=0;\r
+               public chmUnitInfo GetFileInfo(int FileIndex)\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return null;\r
+\r
+                       m_FileFindIndex=FileIndex;\r
+\r
+                       this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFileIndex);\r
+                       m_FileCount=0;\r
+                       m_FileInfo=null;\r
+                       this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);\r
+                       this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFileIndex);\r
+                       return m_FileInfo;\r
+               }\r
+\r
+               public chmUnitInfo GetFileInfoByExtension(string Ext)\r
+               {\r
+                       this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFileByExtension);\r
+                       m_FileInfo=null;\r
+                       m_FileFind=Ext.ToLower();\r
+                       this.chm_enumerate(CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_ALL);\r
+                       this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFileByExtension);\r
+                       return m_FileInfo;\r
+               }\r
+\r
+               public bool ExtractFile(string FileName, System.IO.Stream st)\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return false;\r
+\r
+                       chmUnitInfo Info=GetFileInfo(FileName);\r
+                       return ExtractFile(Info,st);\r
+               }\r
+\r
+               public bool ExtractFile(chmUnitInfo Info, System.IO.Stream st)\r
+               {               \r
+                       if (!m_bCHMLoaded)\r
+                               return false;\r
+\r
+                       if (Info==null)\r
+                               return false;\r
+                       else\r
+                       {\r
+                               chm_retrieve_object(Info,st,0,Info.length);                             \r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               public string ExtractTextFile(string FileName)\r
+               {       \r
+                       if (!m_bCHMLoaded)\r
+                               return "CHM File not loaded";\r
+\r
+                       chmUnitInfo Info=GetFileInfo(FileName);\r
+                       return ExtractTextFile(Info);\r
+               }\r
+\r
+               public string ExtractTextFile(chmUnitInfo Info)\r
+               {                               \r
+                       if (!m_bCHMLoaded)\r
+                               return "CHM File not loaded";\r
+\r
+                       if (Info==null)\r
+                               return "";\r
+                       \r
+                       if (Info.path.Length>=2)\r
+                       {\r
+                               if (Info.path.Substring(0,2).CompareTo("/#")==0)\r
+                                       return "";\r
+                               if (Info.path.Substring(0,2).CompareTo("/$")==0)\r
+                                       return "";\r
+                       }\r
+\r
+                       MemoryStream st=new MemoryStream((int)Info.length);\r
+                       this.chm_retrieve_object(Info,st,0,Info.length);                        \r
+\r
+                       if (st.Length==0)\r
+                               return "";\r
+\r
+                       string Text="";                 \r
+                       \r
+                       ASCIIEncoding ascii=new ASCIIEncoding();                        \r
+                       Text=ascii.GetString(st.ToArray(),0,50);\r
+\r
+                       // UTF Decoding\r
+                       if (Text.IndexOf("UTF-8")!=-1)\r
+                       {\r
+                               UTF8Encoding utf8 = new UTF8Encoding();\r
+                               Text=utf8.GetString(st.ToArray(),0,(int)st.Length);\r
+                       }\r
+                       else\r
+                               Text=ascii.GetString(st.ToArray(),0,(int)st.Length);\r
+       \r
+                       return Text;\r
+               }       \r
+               \r
+               public void FindFileByExtension(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {                       \r
+                       if ((Info.path.StartsWith("::")) || (Info.path.StartsWith("#")) ||(Info.path.StartsWith("$")))\r
+                       {\r
+                               Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+                               return;\r
+                       }                       \r
+\r
+                       FileInfo Fi=new FileInfo(Info.path);\r
+                       if (Fi.Extension.ToLower()==m_FileFind)\r
+                       {\r
+                               Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS;\r
+                               m_FileInfo=Info;\r
+                       }\r
+                       else\r
+                               Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+               }\r
+\r
+               public bool GetCHMParts(string Url, ref string CHMFileName, ref string FileName, ref string Anchor)\r
+               {\r
+                       Regex ParseURLRegEx= new Regex( @"ms-its:(?'CHMFile'.*)::(?'Topic'.*)", RegexOptions.IgnoreCase| RegexOptions.Singleline | RegexOptions.ExplicitCapture| RegexOptions.IgnorePatternWhitespace| RegexOptions.Compiled);\r
+\r
+                       // Parse URL - Get CHM Filename & Page Name\r
+                       //      Format 'ms-its:file name.chm::/topic.htm'\r
+                       if (ParseURLRegEx.IsMatch(Url))\r
+                       {\r
+                               Match m=ParseURLRegEx.Match(Url);\r
+                               CHMFileName=m.Groups["CHMFile"].Value;\r
+                               string Topic=m.Groups["Topic"].Value;\r
+                               int idx=Topic.IndexOf("#");\r
+                               if (idx>-1)\r
+                               {\r
+                                       FileName=Topic.Substring(0,idx);\r
+                                       Anchor=Topic.Substring(idx+1);\r
+                               }\r
+                               else\r
+                                       FileName=Topic;\r
+                               return true;\r
+                       }\r
+                       return false;\r
+               }\r
+\r
+               private string m_TempDir="";            \r
+               string ReplaceFileName(Match m)\r
+               {\r
+                       string strReplace = m.ToString();\r
+\r
+                       // Process string.\r
+                       if (m.Groups["FileName"]==null)\r
+                               return strReplace;\r
+\r
+                       string FileName=m.Groups["FileName"].Value;\r
+                       string FileName2=FileName.Replace("/",@"\");\r
+                       int idx=FileName2.IndexOf("::");\r
+                       if (idx!=-1)\r
+                               FileName2=FileName2.Substring(idx+2);\r
+                       string []parts=FileName2.Split('\\');                   \r
+                       string NewName=@"file://"+m_TempDir+parts[parts.GetUpperBound(0)];\r
+\r
+                       strReplace=strReplace.Replace(FileName,NewName);        \r
+                       return strReplace;\r
+               }\r
+\r
+               public ArrayList GetFileList(ref string Text, string TempDir)\r
+               {                       \r
+                       if (!m_bCHMLoaded)\r
+                               return null;\r
+\r
+                       m_TempDir=TempDir;\r
+\r
+                       ArrayList FilesList=new ArrayList();\r
+\r
+                       // Parse HTML for CCS, ima, etc                 \r
+                       string regexContent=@"[\x2f a-zA-Z0-9\x5C\x2E\x28\x29\x23\x24\x25\x26\x27\x22\x21\x3F\x3E\x3D\x3C\x3B\x3A\x5B\x5D\x5E\x5F\x7D\x7C\x7B\x7E\x40\x2D\x2C\x2B\x2A]*\s*";\r
+                       string regexFileName=@"\s*=\s*[""|'](?'FileName'[^""^']*)[""|']\s*";\r
+\r
+                       Regex ScriptRegex = new Regex(@"<script[^>]*>.*</script>",\r
+                               RegexOptions.IgnoreCase\r
+                               | RegexOptions.Multiline\r
+                               | RegexOptions.Singleline\r
+                               | RegexOptions.IgnorePatternWhitespace\r
+                               | RegexOptions.Compiled);\r
+\r
+                       Regex XMLRegex = new Regex(@"<\?xml.*\?>",\r
+                               RegexOptions.IgnoreCase\r
+                               | RegexOptions.Multiline\r
+                               | RegexOptions.Singleline\r
+                               | RegexOptions.IgnorePatternWhitespace\r
+                               | RegexOptions.Compiled);\r
+\r
+                       Regex XMLRegex2 = new Regex(@"<xml[^>]*>.*</xml>",\r
+                               RegexOptions.IgnoreCase\r
+                               | RegexOptions.Multiline\r
+                               | RegexOptions.Singleline\r
+                               | RegexOptions.IgnorePatternWhitespace\r
+                               | RegexOptions.Compiled);\r
+\r
+                       Regex SRCRegex = new Regex( \r
+                               @"src"+regexFileName,\r
+                               RegexOptions.IgnoreCase\r
+                               | RegexOptions.Multiline\r
+                               | RegexOptions.Singleline\r
+                               | RegexOptions.IgnorePatternWhitespace\r
+                               | RegexOptions.Compiled);\r
+\r
+                       Regex StyleSheetRegex = new Regex(                              \r
+                               @"<link\s*"+regexContent+@"rel\s*=\s*[""|']stylesheet[""|']\s*"+regexContent + "href"+regexFileName,\r
+                               RegexOptions.IgnoreCase\r
+                               | RegexOptions.Multiline\r
+                               | RegexOptions.Singleline\r
+                               | RegexOptions.IgnorePatternWhitespace\r
+                               | RegexOptions.Compiled);\r
+                       \r
+                       // Remove Script Tags\r
+                       Text=ScriptRegex.Replace(Text,"");\r
+\r
+                       // Remove XML Tags\r
+                       Text=XMLRegex.Replace(Text,"");\r
+                       Text=XMLRegex2.Replace(Text,"");                        \r
+\r
+\r
+                       StringBuilder s=new StringBuilder(Text);\r
+                       \r
+                       if (StyleSheetRegex.IsMatch(Text))\r
+                       {                               \r
+                               Match m = StyleSheetRegex.Match(Text);\r
+                               while (m.Success)\r
+                               {                                                                               \r
+                                       string FileName=m.Groups["FileName"].ToString();\r
+                                       FilesList.Add(FileName);                                        \r
+                                       m=m.NextMatch();\r
+                               }\r
+                               Text=StyleSheetRegex.Replace(Text,new MatchEvaluator(ReplaceFileName));\r
+                       }\r
+\r
+                       if (SRCRegex.IsMatch(Text))\r
+                       {                                                               \r
+                               Match m = SRCRegex.Match(Text);\r
+                               while (m.Success)\r
+                               {                                       \r
+                                       string FileName=m.Groups["FileName"].ToString();\r
+                                       FilesList.Add(FileName);\r
+                                       m=m.NextMatch();\r
+                               }\r
+                               Text=SRCRegex.Replace(Text,new MatchEvaluator(ReplaceFileName));\r
+                       }\r
+                       \r
+                       return FilesList;\r
+               }\r
+\r
+               public string GetHTMLAndFiles(string TempDir, string Url)\r
+               {\r
+                       string HTMLText="";\r
+                       if (TempDir.EndsWith(@"\")) TempDir=TempDir.Substring(TempDir.Length-1);                        \r
+       \r
+                       // Delete Temp Directory\r
+                       if (Directory.Exists(TempDir))\r
+                               Directory.Delete(TempDir,true);\r
+\r
+                       // Create Temp Directory\r
+                       if (!Directory.Exists(TempDir))\r
+                               Directory.CreateDirectory(TempDir);\r
+\r
+                       if (!TempDir.EndsWith(@"\")) TempDir+=@"\";             \r
+\r
+                       string m_TopicName="";\r
+\r
+                       string m_CHMFile=CHMFileName;\r
+                       string Anchor="";\r
+                       if (!GetCHMParts(Url,ref m_CHMFile, ref m_TopicName, ref Anchor))\r
+                       {\r
+                               m_CHMFile=this.CHMFileName;\r
+                               m_TopicName=Url;\r
+                       }\r
+                               \r
+                       if (m_TopicName=="")\r
+                               return "#No TopicName defined in Url : "+ Url;          \r
+\r
+                       m_TopicName=m_TopicName.Replace("/",@"\");                      \r
+                       if (!m_CHMFile.StartsWith(@"\"))\r
+                               m_CHMFile=this.Dir+@"\"+m_CHMFile;\r
+\r
+                       // Open CHM                     \r
+                       CHMStream LocalCHM=this;\r
+                       \r
+                       if (this.CHMFileName.ToLower().CompareTo(m_CHMFile.ToLower())!=0)\r
+                               LocalCHM=new CHMStream(m_CHMFile);\r
+\r
+                       // Get HTML\r
+                       HTMLText=LocalCHM.ExtractTextFile(m_TopicName);\r
+                       if (HTMLText=="")\r
+                               return "#Failed to find Topic in CHM File : "+Url;\r
+\r
+                       HTMLText=GetFiles(TempDir, HTMLText, LocalCHM);\r
+\r
+                       return HTMLText;\r
+               }\r
+\r
+               public string GetFiles(string TempDir, string HTMLText, CHMStream chm)\r
+               {\r
+                       return GetFiles(TempDir, HTMLText, chm,0);\r
+               }\r
+\r
+               public string GetFiles(string TempDir, string HTMLText, CHMStream  chm, int Level)\r
+               {\r
+                       // Get FilesList & Extract Files to Temp Dir\r
+                       ArrayList FileList=chm.GetFileList(ref HTMLText, TempDir);\r
+                       if (FileList!=null)\r
+                       {\r
+                               foreach(object obj in FileList)\r
+                               {\r
+                                       string FileName=(string)obj;\r
+\r
+                                       string CHMFileName="";\r
+                                       string TopicName="";                                    \r
+                                       string Anchor="";\r
+                                       CHMStream NewCHM=chm;                                   \r
+                                       if (GetCHMParts(FileName,ref CHMFileName, ref TopicName, ref Anchor))\r
+                                       {       \r
+                                               NewCHM=new CHMStream(chm.Dir+@"\"+CHMFileName);\r
+                                               if (!NewCHM.CHMLoaded)\r
+                                                       NewCHM=null;\r
+                                               FileName=TopicName;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               CHMFileName=chm.CHMFileName;\r
+                                               NewCHM=chm;\r
+                                               TopicName=FileName;\r
+                                       }\r
+                                       if (NewCHM==null)\r
+                                               continue;\r
+\r
+                                       if (((FileName.ToLower().EndsWith(".htm")) || (FileName.ToLower().EndsWith(".html"))) && (Level<2))\r
+                                       {\r
+                                               string HTMLText2=NewCHM.ExtractTextFile(FileName);\r
+                                               FileInfo Fi=new FileInfo(FileName);\r
+                                               string path=TempDir+Fi.Name;\r
+                                               HTMLText2=GetFiles(TempDir,HTMLText2, chm, Level+1);\r
+\r
+                                               if (File.Exists(path))\r
+                                                       File.Delete(path);\r
+\r
+                                               StreamWriter st=new StreamWriter(path);\r
+                                               st.WriteLine(HTMLText2);\r
+                                               st.Close();\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               // Extract all other files as is                                                \r
+                                               string FileName2=FileName.Replace("/",@"\");\r
+                                               if (FileName2.Substring(0,1)==@"\")\r
+                                                       FileName2=FileName2.Substring(1);\r
+\r
+                                               string []parts=FileName2.Split('\\');\r
+                                               string path=TempDir+parts[parts.GetUpperBound(0)];\r
+                                               if (File.Exists(path))\r
+                                                       File.Delete(path);\r
+                                               System.IO.FileStream st=new FileStream(path,FileMode.CreateNew);\r
+                                               NewCHM.ExtractFile(FileName2,st);\r
+                                               st.Close();\r
+                                       }                               \r
+                               }\r
+                       }\r
+\r
+                       // return HTML string of main page\r
+                       return HTMLText;\r
+               }\r
+               \r
+               #region CHMStream Enums\r
+               // the two available spaces in a CHM file                      \r
+               // N.B.: The format supports arbitrarily many spaces, but only \r
+               //       two appear to be used at present.                     \r
+               public enum CHM_COMPRESSION { CHM_UNCOMPRESSED=0, CHM_COMPRESSED=1};\r
+\r
+               // resolve a particular object from the archive \r
+               public enum CHM_RESOLVE { CHM_RESOLVE_SUCCESS=0, CHM_RESOLVE_FAILURE=1};\r
+                                                                                                                 \r
+               // retrieve part of an object from the archive \r
+               public enum CHM_ENUMERATE\r
+               {\r
+                       None=0,\r
+                       CHM_ENUMERATE_NORMAL    =1,\r
+                       CHM_ENUMERATE_META              =2,\r
+                       CHM_ENUMERATE_SPECIAL   =4,\r
+                       CHM_ENUMERATE_FILES             =8,\r
+                       CHM_ENUMERATE_DIRS              =16,\r
+                       CHM_ENUMERATE_ALL               =31};\r
+\r
+               public enum CHM_ENUMERATOR\r
+               { \r
+                       CHM_ENUMERATOR_FAILURE  =0, \r
+                       CHM_ENUMERATOR_SUCCESS  =2, \r
+                       CHM_ENUMERATOR_CONTINUE =1\r
+               };\r
+               #endregion\r
+\r
+               #region Internal Parameters\r
+               private int ffs(int val)\r
+               {\r
+                       int bit=1;\r
+                       int idx=1;\r
+                       while (bit != 0  &&  (val & bit) == 0)\r
+                       {\r
+                               bit <<= 1;\r
+                               ++idx;\r
+                       }\r
+                       if (bit == 0)\r
+                               return 0;\r
+                       else\r
+                               return idx;\r
+               }\r
+\r
+               // names of sections essential to decompression \r
+               private const string _CHMU_RESET_TABLE = @"::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";\r
+               private const string _CHMU_LZXC_CONTROLDATA = @"::DataSpace/Storage/MSCompressed/ControlData";\r
+               private const string _CHMU_CONTENT = @"::DataSpace/Storage/MSCompressed/Content";\r
+               private const string _CHMU_SPANINFO =   @"::DataSpace/Storage/MSCompressed/SpanInfo";\r
+\r
+           private UInt64              dir_offset=0;\r
+               private UInt64              dir_len=0;    \r
+               private UInt64              data_offset=0;\r
+               private Int32               index_root=0;\r
+               private Int32               index_head=0;\r
+               private UInt32              block_len=0;                \r
+               \r
+               private chmUnitInfo                     rt_unit;\r
+               private chmUnitInfo                     cn_unit;\r
+               private chmLzxcResetTable       reset_table;\r
+               private bool                            compression_enabled=false;\r
+\r
+               // LZX control data \r
+               private int                                     window_size=0;\r
+               private UInt32              reset_interval=0;\r
+               private UInt32              reset_blkcount=0;\r
+               private BinaryReader            st=null;\r
+\r
+               // decompressor state \r
+               private lzw             lzx_state;              \r
+               private int                 lzx_last_block=0;\r
+               #endregion\r
+\r
+               #region Open CHM Stream\r
+               private bool CheckSig(string Sig1, char[] Sig2)\r
+               {\r
+                       int  i=0;\r
+                       foreach(char ch in Sig1.ToCharArray())\r
+                       {\r
+                               if(ch!=Sig2[i])\r
+                                       return false;\r
+                               i++;\r
+                       }\r
+                       return true;    \r
+               }\r
+\r
+               // open an ITS archive \r
+               private bool chm_open(string filename)\r
+               {\r
+                       chmItsfHeader        itsfHeader=new chmItsfHeader();\r
+                       chmItspHeader        itspHeader=new chmItspHeader();\r
+                       chmUnitInfo          uiSpan=new chmUnitInfo();\r
+                       chmUnitInfo          uiLzxc=new chmUnitInfo();\r
+                       chmLzxcControlData   ctlData=new chmLzxcControlData();\r
+                       \r
+                       m_bCHMLoaded=false;\r
+                       if (!File.Exists(filename))\r
+                               return false;\r
+\r
+                       st=new BinaryReader(File.OpenRead(filename));\r
+                       if (st==null)\r
+                               return false;\r
+\r
+                       // read and verify header                       \r
+                       if (itsfHeader.Read_itsf_header(st)==0)\r
+                       {\r
+                               st.Close();\r
+                               return false;\r
+                       }\r
+                       st.BaseStream.Seek((long)itsfHeader.dir_offset,SeekOrigin.Begin);                       \r
+\r
+                       // stash important values from header \r
+                       dir_offset  = itsfHeader.dir_offset;\r
+                       dir_len     = itsfHeader.dir_len;\r
+                       data_offset = itsfHeader.data_offset;\r
+\r
+                       // now, read and verify the directory header chunk \r
+                       if (itspHeader.Read_itsp_header(st)==0)\r
+                       {\r
+                               st.Close();\r
+                               return false;\r
+                       }\r
+\r
+                       // grab essential information from ITSP header \r
+                       dir_offset += (UInt64)itspHeader.header_len;\r
+                       dir_len    -= (UInt64)itspHeader.header_len;\r
+                       index_root  = itspHeader.index_root;\r
+                       index_head  = itspHeader.index_head;\r
+                       block_len   = itspHeader.block_len;\r
+\r
+                       // if the index root is -1, this means we don't have any PMGI blocks.\r
+                       // as a result, we must use the sole PMGL block as the index root\r
+            \r
+                       if (index_root == -1)\r
+                               index_root = index_head;\r
+\r
+                       compression_enabled=true;\r
+\r
+                       // prefetch most commonly needed unit infos \r
+                       // if (CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_SPANINFO, ref uiSpan) \r
+                       //  || uiSpan.space == CHM_COMPRESSION.CHM_COMPRESSED || \r
+                       if (CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_RESET_TABLE, ref rt_unit)    \r
+                               || rt_unit.space == CHM_COMPRESSION.CHM_COMPRESSED                        \r
+                               || CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_CONTENT,ref cn_unit)    \r
+                               || cn_unit.space == CHM_COMPRESSION.CHM_COMPRESSED                        \r
+                               || CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_LZXC_CONTROLDATA, ref uiLzxc)                \r
+                               || uiLzxc.space == CHM_COMPRESSION.CHM_COMPRESSED)\r
+                       {\r
+                               compression_enabled=false;\r
+                               // st.Close();                          \r
+                               // return false;\r
+                       }\r
+\r
+                       // try to read span \r
+                       // N.B.: we've already checked that uiSpan is in the uncompressed section,\r
+                       //        so this should not require attempting to decompress, which may\r
+                       //        rely on having a valid "span"\r
+                    \r
+                       if (compression_enabled)\r
+                       {               \r
+                               reset_table=new chmLzxcResetTable();\r
+                               st.BaseStream.Seek((long)((long)data_offset + (long)rt_unit.start),SeekOrigin.Begin);\r
+                               if (reset_table.Read_lzxc_reset_table(st)!=1)\r
+                               {\r
+                                       compression_enabled=false;\r
+                               }\r
+                       }\r
+\r
+                       if (compression_enabled)\r
+                       {                       \r
+                               // read control data \r
+                               ctlData=new chmLzxcControlData();\r
+                               st.BaseStream.Seek((long)((long)data_offset + (long)uiLzxc.start),SeekOrigin.Begin);\r
+                               if (ctlData.Read_lzxc_control_data(st)!=1)\r
+                               {\r
+                                       compression_enabled=false;\r
+                               }                       \r
+\r
+                               window_size = (int)ctlData.windowSize;\r
+                               reset_interval = ctlData.resetInterval;\r
+                               try\r
+                               {\r
+                                       reset_blkcount = (uint)(reset_interval    /\r
+                                               (window_size / 2) *\r
+                                               ctlData.windowsPerReset);\r
+                               }\r
+                               catch(Exception)\r
+                               {\r
+                                       reset_blkcount=0;\r
+                               }\r
+                       }\r
+\r
+                       m_bCHMLoaded=true;\r
+\r
+                       return true;\r
+               }\r
+               #endregion\r
+\r
+               #region Close CHM Stream\r
+               // close an ITS archive \r
+               private  void chm_close()\r
+               {\r
+                       if (!m_bCHMLoaded)\r
+                               return;\r
+\r
+                       st.Close();\r
+                       lzx_state.LZXteardown();\r
+                       lzx_state=null;\r
+               }\r
+               #endregion\r
+                       \r
+               #region Find File in CHM Stream\r
+               // resolve a particular object from the archive \r
+               private CHMStream.CHM_RESOLVE chm_resolve_object(string objPath, ref chmUnitInfo ui)\r
+               {                       \r
+                       Int32 curPage;\r
+       \r
+                       // starting page \r
+                       curPage = index_root;\r
+\r
+                       // until we have either returned or given up \r
+                       while (curPage != -1)\r
+                       {\r
+                               st.BaseStream.Seek((long)((long)dir_offset + (long)(curPage*block_len)),SeekOrigin.Begin);\r
+                               \r
+                               char[] sig=st.ReadChars(4);\r
+                               st.BaseStream.Seek(-4,SeekOrigin.Current);\r
+                               if (CheckSig("PMGL",sig))\r
+                               {\r
+                                       chmPmglHeader PmglHeader=new chmPmglHeader();\r
+                                       if (PmglHeader.Read_pmgl_header(st)==1)\r
+                                       {\r
+                                               // scan block \r
+                                               ui=PmglHeader.FindObject(st,block_len,objPath);\r
+                                               if (ui== null) \r
+                                                       return CHMStream.CHM_RESOLVE.CHM_RESOLVE_FAILURE;\r
+\r
+                                               // parse entry and return \r
+                                               return CHMStream.CHM_RESOLVE.CHM_RESOLVE_SUCCESS;\r
+                                       }\r
+                               }       \r
+                               else if (CheckSig("PMGI",sig))\r
+                               {                       \r
+                                       chmPmgiHeader pmgiHeader=new chmPmgiHeader();\r
+                                       pmgiHeader.Read_pmgi_header(st);\r
+                                       curPage = pmgiHeader._chm_find_in_PMGI(st, block_len, objPath);\r
+                               }                               \r
+                               else\r
+                                       // else, we are confused.  give up. \r
+                                       return CHMStream.CHM_RESOLVE.CHM_RESOLVE_FAILURE;\r
+                       }\r
+\r
+                       // didn't find anything.  fail. \r
+                       return CHMStream.CHM_RESOLVE.CHM_RESOLVE_FAILURE;\r
+               }\r
+               #endregion\r
+\r
+               #region Extract File from CHM Stream\r
+\r
+               //      * utility methods for dealing with compressed data               \r
+               // get the bounds of a compressed block.  return 0 on failure                   \r
+               private int _chm_get_cmpblock_bounds(System.IO.BinaryReader st, UInt64 block, ref UInt64 start, ref UInt64 len)\r
+               {\r
+                       // for all but the last block, use the reset table \r
+                       if (block < reset_table.block_count-1)\r
+                       {\r
+                               // unpack the start address                             \r
+                               st.BaseStream.Seek((long)data_offset + (long)rt_unit.start + (long)reset_table.table_offset + (long)(block*8),SeekOrigin.Begin);\r
+                               start=st.ReadUInt64();\r
+                               len=st.ReadUInt64();\r
+                       }\r
+\r
+                       // for the last block, use the span in addition to the reset table \r
+                       else\r
+                       {                               \r
+                               // unpack the start address \r
+                               st.BaseStream.Seek((long)data_offset + (long)rt_unit.start + (long)reset_table.table_offset + (long)(block*8),SeekOrigin.Begin);\r
+                               start=st.ReadUInt64();\r
+                               len = reset_table.compressed_len;\r
+                       }\r
+\r
+                       // compute the length and absolute start address \r
+                       len -= start;\r
+                       start += data_offset + cn_unit.start;\r
+\r
+                       return 1;\r
+               }\r
+\r
+               // decompress the block.  must have lzx_mutex. \r
+               private ulong _chm_decompress_block(UInt64 block, System.IO.Stream OutBuffer)\r
+               {\r
+                       // byte []cbuffer = new byte(reset_table.block_len + 6144);\r
+                       ulong cmpStart=0;                                    // compressed start  \r
+                       ulong cmpLen=0;                                       // compressed len    \r
+                       UInt32 blockAlign = (UInt32)(block % reset_blkcount); // reset intvl. aln.                      \r
+                       \r
+                       // check if we need previous blocks \r
+                       if (blockAlign != 0)                    \r
+                       {\r
+                               /* fetch all required previous blocks since last reset */\r
+                               for (UInt32 i = blockAlign; i > 0; i--)\r
+                               {\r
+                                       UInt32 curBlockIdx = (UInt32)(block-i);\r
+                                       \r
+                                       /* check if we most recently decompressed the previous block */\r
+                                       if ((ulong)lzx_last_block != curBlockIdx)\r
+                                       {\r
+                                               if ((curBlockIdx % reset_blkcount)==0)\r
+                                               {\r
+                                                       lzx_state.LZXreset();\r
+                                               }\r
+                                               \r
+                                               _chm_get_cmpblock_bounds(st,curBlockIdx, ref cmpStart, ref cmpLen);\r
+                                               st.BaseStream.Seek((long)cmpStart,SeekOrigin.Begin);                                            \r
+                                               if (lzx_state.LZXdecompress(st,OutBuffer, ref cmpLen, ref reset_table.block_len) != lzw.DECR_OK)\r
+                                                       return (Int64)0;\r
+                                       }\r
+                                       lzx_last_block = (int)(curBlockIdx);\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if ((block % reset_blkcount)==0)\r
+                               {\r
+                                       lzx_state.LZXreset();\r
+                               }               \r
+                       }\r
+\r
+                       // decompress the block we actually want \r
+                       if (_chm_get_cmpblock_bounds(st, block, ref cmpStart, ref cmpLen)==0)\r
+                               return 0;\r
+\r
+                       st.BaseStream.Seek((long)cmpStart,SeekOrigin.Begin);\r
+                                                       \r
+                       if (lzx_state.LZXdecompress(st, OutBuffer, ref cmpLen,ref reset_table.block_len) != lzw.DECR_OK)\r
+                               return (Int64)0;\r
+\r
+                       lzx_last_block = (int)block;\r
+\r
+                       // XXX: modify LZX routines to return the length of the data they\r
+                       // * decompressed and return that instead, for an extra sanity check.\r
+                    return reset_table.block_len;\r
+               }\r
+\r
+               // grab a region from a compressed block \r
+               private ulong _chm_decompress_region(Stream buf, ulong  start, ulong len)\r
+               {\r
+                       ulong nBlock, nOffset;\r
+                       ulong nLen;\r
+                       ulong gotLen;\r
+                       // byte [] ubuffer=null;\r
+\r
+                       if (len <= 0)\r
+                               return (Int64)0;\r
+\r
+                       // figure out what we need to read \r
+                       nBlock = start / reset_table.block_len;\r
+                       nOffset = start % reset_table.block_len;\r
+                       nLen = len;\r
+                       if (nLen > (reset_table.block_len - nOffset))\r
+                               nLen = reset_table.block_len - nOffset;\r
+\r
+                       // data request not satisfied, so... start up the decompressor machine                  \r
+                       if (lzx_state==null)\r
+                       {\r
+                               int window_size = ffs(this.window_size) - 1;\r
+                               lzx_last_block = -1;\r
+\r
+                               lzx_state=new lzw();\r
+                               lzx_state.LZXinit(window_size);\r
+                       }\r
+\r
+                       // decompress some data \r
+                       MemoryStream ms=new MemoryStream((int)reset_table.block_len+6144);                      \r
+                       gotLen = _chm_decompress_block(nBlock, ms);\r
+                       if (gotLen < nLen)\r
+                               nLen = gotLen;  \r
+                       \r
+                       // memcpy(buf, ubuffer+nOffset, (unsigned int)nLen);\r
+                       ms.Position=(long)nOffset;\r
+                       for(ulong i=0;i<nLen;i++)\r
+                               buf.WriteByte((byte)ms.ReadByte());\r
+                       buf.Flush();\r
+                       return nLen;\r
+               }\r
+\r
+               // retrieve (part of) an object \r
+               private ulong chm_retrieve_object(chmUnitInfo ui, Stream buf, ulong addr, ulong len)\r
+               {\r
+                       // starting address must be in correct range \r
+                       if (addr < 0  ||  addr >= (ulong)ui.length)\r
+                               return (Int64)0;\r
+\r
+                       // clip length \r
+                       if (addr + (ulong)len > (ulong)ui.length)\r
+                               len = (ulong)ui.length - (ulong)addr;\r
+\r
+                       // if the file is uncompressed, it's simple \r
+                       if (ui.space == CHMStream.CHM_COMPRESSION.CHM_UNCOMPRESSED)\r
+                       {\r
+                               // read data \r
+                               long FilePos=st.BaseStream.Position;                            \r
+                               st.BaseStream.Seek((long)((long)data_offset + (long)ui.start + (long)addr),SeekOrigin.Begin);\r
+                               // byte [] buffer=st.ReadBytes((int)len);\r
+                               buf.Write(st.ReadBytes((int)len),0,(int) len);\r
+                               st.BaseStream.Seek(FilePos,SeekOrigin.Begin);\r
+                               return (ulong)len;\r
+                       }\r
+\r
+                       // else if the file is compressed, it's a little trickier \r
+                       else // ui->space == CHM_COMPRESSED \r
+                       {                       \r
+                               if (lzx_state!=null)\r
+                               {\r
+                                       lzx_state.LZXteardown();\r
+                                       lzx_state=null;\r
+                               }\r
+                               ulong swath=0, total=0;\r
+                               do \r
+                               {\r
+                                       if (!compression_enabled)\r
+                                               return total;\r
+\r
+                                       // swill another mouthful \r
+                                       swath = _chm_decompress_region(buf, ui.start + addr, len);\r
+\r
+                                       // if we didn't get any... \r
+                                       if (swath == 0)\r
+                                       {\r
+                                               Trace.Assert((total!=ui.length),"De-compress failed","Length Required = "+ui.length.ToString()+" Length returned = "+total.ToString());\r
+                                               return total;\r
+                                       }\r
+\r
+                                       // update stats \r
+                                       total += swath;\r
+                                       len -= swath;\r
+                                       addr += swath;\r
+                               } while (len != 0);             \r
+                               lzx_state=null;\r
+\r
+                               Trace.Assert((len!=ui.length),"De-compress failed","Length Required = "+ui.length.ToString()+" Length returned = "+len.ToString());\r
+                               return len;\r
+                       }\r
+               }\r
+               #endregion\r
+       \r
+               #region Enumerate functions                             \r
+               // Enumerate the objects in the .chm archive \r
+               // Use delegate to handle callback\r
+\r
+               public delegate void CHMFileFound(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status);\r
+               public event CHMFileFound CHMFileFoundEvent;\r
+\r
+               public void OnFileFound(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)\r
+               {\r
+                       if (CHMFileFoundEvent!=null)\r
+                               CHMFileFoundEvent(Info,ref Status);\r
+               }\r
+               private int chm_enumerate(CHM_ENUMERATE what)\r
+               {\r
+                       Int32 curPage;\r
+\r
+                       // buffer to hold whatever page we're looking at                        \r
+                       chmPmglHeader header;\r
+                       uint end=0;\r
+                       uint cur=0;                     \r
+\r
+                       // the current ui \r
+                       chmUnitInfo ui= new chmUnitInfo();\r
+                       CHMStream.CHM_ENUMERATE flag=CHMStream.CHM_ENUMERATE.None;\r
+\r
+                       // starting page \r
+                       curPage = index_head;\r
+\r
+                       // until we have either returned or given up \r
+                       while (curPage != -1)\r
+                       {\r
+                               st.BaseStream.Seek((long)((long)dir_offset + (long)(curPage*block_len)),SeekOrigin.Begin);\r
+\r
+                               // figure out start and end for this page \r
+                               cur = (uint)st.BaseStream.Position;\r
+                               \r
+                               header=new chmPmglHeader();\r
+                               if (header.Read_pmgl_header(st)==0)                             \r
+                                       return 0;\r
+\r
+                               end = (uint)(st.BaseStream.Position + block_len - (header.free_space)- chmPmglHeader._CHM_PMGL_LEN);\r
+\r
+                               // loop over this page \r
+                               while (st.BaseStream.Position < end)\r
+                               {\r
+                                       if (header._chm_parse_PMGL_entry(st,ref ui)==0)\r
+                                               return 0;\r
+\r
+                                       // check for DIRS \r
+                                       if (ui.length == 0  &&  ((what & CHM_ENUMERATE.CHM_ENUMERATE_DIRS)==0))\r
+                                               continue;\r
+\r
+                                       // check for FILES \r
+                                       if (ui.length != 0  &&  ((what & CHM_ENUMERATE.CHM_ENUMERATE_FILES)==0))\r
+                                               continue;\r
+\r
+                                       // check for NORMAL vs. META \r
+                                       if (ui.path[0] == '/')\r
+                                       {\r
+                                               // check for NORMAL vs. SPECIAL \r
+                                               if (ui.path.Length>2)\r
+                                               {\r
+                                                       if (ui.path[1] == '#'  ||  ui.path[1] == '$')\r
+                                                               flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_SPECIAL;\r
+                                                       else\r
+                                                               flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_NORMAL;\r
+                                               }                                       \r
+                                               else\r
+                                                       flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_META;\r
+                                               if ((what & flag)==0)\r
+                                                       continue;\r
+                                       }\r
+\r
+                                       // call the enumerator \r
+                                       {\r
+                                               CHMStream.CHM_ENUMERATOR status = CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;\r
+                                               OnFileFound(ui,ref status);\r
+                                                                                                       \r
+                                               switch (status)\r
+                                               {\r
+                                                       case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_FAILURE:  \r
+                                                               return 0;\r
+\r
+                                                       case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE: \r
+                                                               break;\r
+\r
+                                                       case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS:  \r
+                                                               return 1;\r
+\r
+                                                       default:\r
+                                                               break;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               // advance to next page \r
+                               curPage = header.block_next;\r
+                       }\r
+\r
+                       return 1;\r
+               }\r
+               #endregion\r
+\r
+               #region IDisposable Members\r
+\r
+               private bool disposed=false;\r
+               public void Dispose()\r
+               {\r
+                       Dispose(true);\r
+                       // This object will be cleaned up by the Dispose method.\r
+                       // Therefore, you should call GC.SupressFinalize to\r
+                       // take this object off the finalization queue \r
+                       // and prevent finalization code for this object\r
+                       // from executing a second time.\r
+                       GC.SuppressFinalize(this);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dispose(bool disposing) executes in two distinct scenarios. \r
+               /// If disposing equals true, the method has been called directly \r
+               /// or indirectly by a user's code. Managed and unmanaged resources \r
+               /// can be disposed. \r
+               /// If disposing equals false, the method has been called by the \r
+               /// runtime from inside the finalizer and you should not reference  \r
+               /// other objects. Only unmanaged resources can be disposed.\r
+               /// </summary>\r
+               /// <param name="disposing">disposing flag</param>\r
+               private void Dispose(bool disposing)\r
+               {\r
+                       // Check to see if Dispose has already been called.\r
+                       if(!this.disposed)\r
+                       {\r
+                               // If disposing equals true, dispose all managed \r
+                               // and unmanaged resources.\r
+                               if(disposing)\r
+                               {\r
+                                       // Dispose managed resources.                                   \r
+                               }\r
+                       }\r
+                       disposed = true;         \r
+               }\r
+\r
+               #endregion\r
+       }\r
+\r
+       #region Structures used by CHM Storage\r
+       public class BaseStructure\r
+       {\r
+               public bool CheckSig(string Sig1, char[] Sig2)\r
+               {\r
+                       int i=0;\r
+                       foreach(char ch in Sig1.ToCharArray())\r
+                       {\r
+                               if (ch!=Sig2[i])\r
+                                       return false;\r
+                               i++;\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               // skip a compressed dword \r
+               public void skip_cword(BinaryReader st)\r
+               {               \r
+                       byte b=0;\r
+                       while ((b=st.ReadByte())>= 0x80);                       \r
+               }\r
+\r
+               // skip the data from a PMGL entry \r
+               public void _chm_skip_PMGL_entry_data(BinaryReader st)\r
+               {\r
+                       skip_cword(st);\r
+                       skip_cword(st);\r
+                       skip_cword(st);\r
+               }\r
+\r
+               // parse a compressed dword \r
+               public UInt64 _chm_parse_cword(BinaryReader st)\r
+               {\r
+                       UInt64 accum = 0;\r
+                       byte temp=0;                    \r
+                       while ((temp=st.ReadByte()) >= 0x80)\r
+                       {\r
+                               accum <<= 7;\r
+                               accum += (ulong)(temp & 0x7f);\r
+                       }\r
+\r
+                       return (accum << 7) + temp;\r
+               }\r
+\r
+               // parse a utf-8 string into an ASCII char buffer \r
+               public int _chm_parse_UTF8(BinaryReader st, UInt64 count, ref string path)\r
+               {\r
+                       UTF8Encoding utf8=new UTF8Encoding();\r
+                       path=utf8.GetString(st.ReadBytes((int)count),0,(int)count);                                                     \r
+                       return 1;\r
+               }\r
+       }\r
+\r
+       public class chmUnitInfo\r
+       {\r
+               public UInt64           start=0;\r
+               public UInt64           length=0;\r
+               public CHMStream.CHM_COMPRESSION space=CHMStream.CHM_COMPRESSION.CHM_UNCOMPRESSED;\r
+               public string           path="";\r
+       }\r
+\r
+       // structure of ITSF headers                                            \r
+       public class chmItsfHeader : BaseStructure\r
+       {\r
+               public const int _CHM_ITSF_V2_LEN=0x58;\r
+               public const int _CHM_ITSF_V3_LEN=0x60;\r
+\r
+               public char[]   signature=null;                         //  0 (ITSF) \r
+               public Int32    version=0;                                      //  4 \r
+               public Int32    header_len=0;                           //  8 \r
+               public Int32    unknown_000c=0;                         //  c \r
+               public UInt32   last_modified=0;                        // 10 \r
+               public UInt32   lang_id=0;                                      // 14                   \r
+               public Guid             dir_uuid;                                       // 18 \r
+               public Guid             stream_uuid;                            // 28 \r
+               public UInt64   unknown_offset=0;                       // 38 \r
+               public UInt64   unknown_len=0;                          // 40 \r
+               public UInt64   dir_offset=0;                           // 48 \r
+               public UInt64   dir_len=0;                                      // 50 \r
+               public UInt64   data_offset=0;                          // 58 (Not present before V3) \r
+\r
+               public int Read_itsf_header(BinaryReader st)\r
+               {                                                       \r
+                       signature=st.ReadChars(4);\r
+                       if (CheckSig("ITSF",signature)==false)\r
+                               return 0;\r
+                       \r
+                       version=st.ReadInt32();\r
+                       header_len=st.ReadInt32();\r
+                       unknown_000c=st.ReadInt32();\r
+                       last_modified=st.ReadUInt32();\r
+                       lang_id=st.ReadUInt32();\r
+                       dir_uuid=new Guid(st.ReadBytes(16));\r
+                       stream_uuid=new Guid(st.ReadBytes(16));                 \r
+                       unknown_offset=st.ReadUInt64();\r
+                       unknown_len=st.ReadUInt64();\r
+                       dir_offset=st.ReadUInt64();\r
+                       dir_len=st.ReadUInt64();\r
+\r
+                       if (version==2)\r
+                       {\r
+                               if (header_len != chmItsfHeader._CHM_ITSF_V2_LEN)\r
+                                       return 0;\r
+                       }\r
+                       else if (version==3)\r
+                       {\r
+                               if (header_len != chmItsfHeader._CHM_ITSF_V3_LEN)\r
+                                       return 0;\r
+                       }\r
+                       else return 0;\r
+\r
+                       if (version==3)\r
+                               data_offset=st.ReadUInt64();\r
+                       else\r
+                               data_offset = dir_offset + dir_len;\r
+\r
+                       return 1;\r
+               }\r
+       }\r
+\r
+       // structure of ITSP headers \r
+       public class chmItspHeader : BaseStructure\r
+       {\r
+               const int CHM_ITSP_V1_LEN=0x54;\r
+       \r
+               public char[]   signature=null;         //  0 (ITSP) \r
+               public Int32    version=0;         \r
+               public Int32    header_len=0;      \r
+               public Int32    unknown_000c=0;    \r
+               public UInt32   block_len=0;       \r
+               public Int32    blockidx_intvl=0;  \r
+               public Int32    index_depth=0;     \r
+               public Int32    index_root=0;                   \r
+               public Int32    index_head=0;                   \r
+               public Int32    unknown_0024=0;    \r
+               public Int32    num_blocks=0;    \r
+               public Int32    unknown_002c=0;    \r
+               public UInt32   lang_id=0;         \r
+               public Guid             system_uuid;\r
+               public Guid             unknown_0044;\r
+\r
+               public int Read_itsp_header(BinaryReader st)\r
+               {                                                       \r
+                       signature=st.ReadChars(4);              //  0 (ITSP)                    \r
+                       if (CheckSig("ITSP",signature)==false)\r
+                               return 0;\r
+\r
+                       version=st.ReadInt32();\r
+                       header_len=st.ReadInt32();\r
+               \r
+                       if (header_len!=CHM_ITSP_V1_LEN)\r
+                               return 0;\r
+\r
+                       unknown_000c=st.ReadInt32();\r
+                       block_len=st.ReadUInt32();\r
+                       blockidx_intvl=st.ReadInt32();\r
+                       index_depth=st.ReadInt32();\r
+                       index_root=st.ReadInt32();                      \r
+                       index_head=st.ReadInt32();                              \r
+                       unknown_0024=st.ReadInt32();\r
+                       num_blocks=st.ReadInt32();\r
+                       unknown_002c=st.ReadInt32();\r
+                       lang_id=st.ReadUInt32();\r
+                       system_uuid=new Guid(st.ReadBytes(16));\r
+                       unknown_0044=new Guid(st.ReadBytes(16));\r
+\r
+                       return 1;\r
+               }\r
+       }\r
+\r
+       public class chmPmglHeader : BaseStructure\r
+       {\r
+               public const int _CHM_PMGL_LEN=0x14;\r
+               public char[]   signature=null;           //  0 (PMGL) \r
+               public UInt32   free_space=0;             //  4 \r
+               public UInt32   unknown_0008=0;           //  8 \r
+               public Int32    block_prev=0;             //  c \r
+               public Int32    block_next=0;             // 10 \r
+\r
+               public int Read_pmgl_header(BinaryReader st)\r
+               {\r
+                       signature=st.ReadChars(4);\r
+                       if (CheckSig("PMGL",signature)==false)\r
+                               return 0;\r
+\r
+                       free_space=st.ReadUInt32();\r
+                       unknown_0008=st.ReadUInt32();\r
+                       block_prev=st.ReadInt32();\r
+                       block_next=st.ReadInt32();\r
+                       return 1;\r
+               }\r
+\r
+               // parse a PMGL entry into a chmUnitInfo struct; return 1 on success. \r
+               public int _chm_parse_PMGL_entry(BinaryReader st, ref chmUnitInfo ui)\r
+               {\r
+                       UInt64 strLen;\r
+\r
+                       // parse str len \r
+                       strLen = _chm_parse_cword(st);                  \r
+\r
+                       // parse path                   \r
+                       if (_chm_parse_UTF8(st, strLen, ref ui.path)==0)\r
+                               return 0;\r
+\r
+                       // parse info \r
+                       ui.space  = (CHMStream.CHM_COMPRESSION)_chm_parse_cword(st);\r
+                       ui.start  = _chm_parse_cword(st);\r
+                       ui.length = _chm_parse_cword(st);\r
+                       return 1;\r
+               }\r
+\r
+               public chmUnitInfo FindObject(BinaryReader st, UInt32 block_len, string objPath)\r
+               {       \r
+                       UInt32 end = (UInt32)st.BaseStream.Position+ block_len - free_space - _CHM_PMGL_LEN;                    \r
+\r
+                       // now, scan progressively \r
+                       chmUnitInfo FoundObject=new chmUnitInfo();\r
+\r
+                       while (st.BaseStream.Position < end)\r
+                       {                               \r
+                               _chm_parse_PMGL_entry(st,ref FoundObject);\r
+                               if (FoundObject.path.ToLower().CompareTo(objPath.ToLower())==0)\r
+                                       return FoundObject;\r
+                       }\r
+                       FoundObject=null;\r
+\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       public class chmPmgiHeader : BaseStructure\r
+       {\r
+               public const int _CHM_PMGI_LEN=0x8;\r
+\r
+               public char[]   signature=null;           //  0 (PMGL) \r
+               public UInt32   free_space=0;             //  4                         \r
+\r
+               public int Read_pmgi_header(BinaryReader st)\r
+               {\r
+                       signature=st.ReadChars(4);\r
+               \r
+                       if ((signature[0]!='P') || (signature[1]!='M') || (signature[2]!='G') || (signature[3]!='I'))\r
+                               return 0;\r
+\r
+                       free_space=st.ReadUInt32();                             \r
+                       return 1;\r
+               }\r
+\r
+               public Int32 _chm_find_in_PMGI(BinaryReader st, UInt32 block_len, string objPath)\r
+               {                                       \r
+                       int page=-1;\r
+                       UInt64 strLen;\r
+                       string buffer="";\r
+                       uint end = (uint)st.BaseStream.Position + block_len - free_space - _CHM_PMGI_LEN;\r
+\r
+                       // now, scan progressively \r
+                       while (st.BaseStream.Position < end)\r
+                       {\r
+                               // grab the name \r
+                               strLen = _chm_parse_cword(st);\r
+                               buffer="";\r
+                               if (_chm_parse_UTF8(st, strLen, ref buffer)==0)\r
+                                       return -1;\r
+\r
+                               // check if it is the right name \r
+                               if (buffer.ToLower().CompareTo(objPath.ToLower())>0)\r
+                                       return page;\r
+\r
+                               // load next value for path \r
+                               page = (int)_chm_parse_cword(st);\r
+                       }\r
+                       return page;\r
+               }\r
+       }\r
+\r
+       public class chmLzxcResetTable:BaseStructure\r
+       {               \r
+               public UInt32      version=0;\r
+               public UInt32      block_count=0;\r
+               public UInt32      unknown=0;\r
+               public UInt32      table_offset=0;\r
+               public UInt64      uncompressed_len=0;\r
+               public UInt64      compressed_len=0;\r
+               public UInt64      block_len=0;     \r
+               \r
+               public int Read_lzxc_reset_table(BinaryReader st)\r
+               {\r
+                       version=st.ReadUInt32();  \r
+                       block_count=st.ReadUInt32();  \r
+                       unknown=st.ReadUInt32();  \r
+                       table_offset=st.ReadUInt32();  \r
+                       uncompressed_len=st.ReadUInt64();  \r
+                       compressed_len=st.ReadUInt64();  \r
+                       block_len=st.ReadUInt64();  \r
+                               \r
+                       // check structure \r
+                       if (version != 2)\r
+                               return 0;\r
+                       else\r
+                               return 1;\r
+               }\r
+       }\r
+       \r
+       // structure of LZXC control data block \r
+       public class chmLzxcControlData:BaseStructure\r
+       {\r
+               public const int _CHM_LZXC_MIN_LEN=0x18;\r
+               public const int _CHM_LZXC_V2_LEN=0x1c; \r
+\r
+               public UInt32   size=0;                   //  0        \r
+               public char[]   signature=null;                   //  4 (LZXC) \r
+               public UInt32   version=0;                //  8        \r
+               public UInt32   resetInterval=0;          //  c        \r
+               public UInt32   windowSize=0;             // 10        \r
+               public UInt32   windowsPerReset=0;        // 14        \r
+               public UInt32   unknown_18=0;             // 18        \r
+       \r
+               public int Read_lzxc_control_data(BinaryReader st)\r
+               {\r
+                       size=st.ReadUInt32();\r
+                       signature=st.ReadChars(4);\r
+\r
+                       if (CheckSig("LZXC",signature)==false)\r
+                               return 0;\r
+\r
+                       version=st.ReadUInt32();\r
+                       resetInterval=st.ReadUInt32();\r
+                       windowSize=st.ReadUInt32();\r
+                       windowsPerReset=st.ReadUInt32();                        \r
+\r
+                       if (size>=_CHM_LZXC_V2_LEN)\r
+                               unknown_18=st.ReadUInt32();\r
+                       else\r
+                               unknown_18 = 0;\r
+\r
+                       if (version == 2)\r
+                       {\r
+                               resetInterval *= 0x8000;\r
+                               windowSize *= 0x8000;                           \r
+                       }\r
+                       if (windowSize == 0  ||  resetInterval == 0)\r
+                               return 0;\r
+\r
+                       // for now, only support resetInterval a multiple of windowSize/2 \r
+                       if (windowSize == 1)\r
+                               return 0;\r
+                       if ((resetInterval % (windowSize/2)) != 0)\r
+                               return 0;\r
+       \r
+                       return 1;\r
+               }\r
+       }\r
+       #endregion\r
+\r
+       #region LZW Decoder\r
+\r
+       internal class lzx_bits \r
+       {\r
+               public UInt32 bb=0;\r
+               public int bl=0;                        \r
+       }\r
+\r
+       internal class lzw\r
+       {\r
+               public lzw()\r
+               {               \r
+               }\r
+\r
+               /* $Id: lzx.c,v 1.5 2002/10/09 01:16:33 jedwin Exp $ */\r
+               /***************************************************************************\r
+               *                        lzx.c - LZX decompression routines               *\r
+               *                           -------------------                           *\r
+               *                                                                         *\r
+               *  maintainer: Jed Wing <jedwin@ugcs.caltech.edu>                         *\r
+               *  source:     modified lzx.c from cabextract v0.5                        *\r
+               *  notes:      This file was taken from cabextract v0.5, which was,       *\r
+               *              itself, a modified version of the lzx decompression code   *\r
+               *              from unlzx.                                                *\r
+               *                                                                         *\r
+               *  platforms:  In its current incarnation, this file has been tested on   *\r
+               *              two different Linux platforms (one, redhat-based, with a   *\r
+               *              2.1.2 glibc and gcc 2.95.x, and the other, Debian, with    *\r
+               *              2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2).  Both were *\r
+               *              Intel x86 compatible machines.                             *\r
+               ***************************************************************************/\r
+\r
+               /***************************************************************************\r
+               *                                                                         *\r
+               *   This program is free software; you can redistribute it and/or modify  *\r
+               *   it under the terms of the GNU General Public License as published by  *\r
+               *   the Free Software Foundation; either version 2 of the License, or     *\r
+               *   (at your option) any later version.  Note that an exemption to this   *\r
+               *   license has been granted by Stuart Caie for the purposes of           *\r
+               *   distribution with CHMFile.  This does not, to the best of my           *\r
+               *   knowledge, constitute a change in the license of this (the LZX) code  *\r
+               *   in general.                                                           *\r
+               *                                                                         *\r
+               ***************************************************************************/\r
+\r
+               /* some constants defined by the LZX specification */\r
+               private  const int  LZX_MIN_MATCH                = 2;\r
+               private  const int  LZX_MAX_MATCH                = 257;\r
+               private  const int  LZX_NUM_CHARS                = 256;\r
+               private  const int  LZX_BLOCKTYPE_INVALID        = 0;   /* also blocktypes 4-7 invalid */\r
+               private  const int  LZX_BLOCKTYPE_VERBATIM       = 1;\r
+               private  const int  LZX_BLOCKTYPE_ALIGNED        = 2;\r
+               private  const int  LZX_BLOCKTYPE_UNCOMPRESSED   = 3;\r
+               private  const int  LZX_PRETREE_NUM_ELEMENTS     = 20;\r
+               private  const int  LZX_ALIGNED_NUM_ELEMENTS     = 8;   /* aligned offset tree #elements */\r
+               private  const int  LZX_NUM_PRIMARY_LENGTHS      = 7;   /* this one missing from spec! */\r
+               private  const int  LZX_NUM_SECONDARY_LENGTHS    = 249; /* length tree #elements */\r
+\r
+               /* LZX huffman defines: tweak tablebits as desired */\r
+               private  const int  LZX_PRETREE_MAXSYMBOLS  = LZX_PRETREE_NUM_ELEMENTS;\r
+               private  const int  LZX_PRETREE_TABLEBITS   = 6;\r
+               private  const int  LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 50*8;\r
+               private  const int  LZX_MAINTREE_TABLEBITS  = 12;\r
+               private  const int  LZX_LENGTH_MAXSYMBOLS   = LZX_NUM_SECONDARY_LENGTHS+1;\r
+               private  const int  LZX_LENGTH_TABLEBITS    = 12;\r
+               private  const int  LZX_ALIGNED_MAXSYMBOLS  = LZX_ALIGNED_NUM_ELEMENTS;\r
+               private  const int  LZX_ALIGNED_TABLEBITS   = 7;\r
+               private  const int  LZX_LENTABLE_SAFETY   = 64; /* we allow length table decoding overruns */\r
+\r
+               public   const int  DECR_OK                             = 0;\r
+               public   const int  DECR_DATAFORMAT             = 1;\r
+               public   const int  DECR_ILLEGALDATA    = 2;\r
+               public   const int  DECR_NOMEMORY               = 3;\r
+\r
+               private  byte[] window;         /* the actual decoding window              */\r
+               private  ulong window_size;     /* window size (32Kb through 2Mb)          */\r
+               private  ulong actual_size;     /* window size when it was first allocated */\r
+               private  ulong window_posn;     /* current offset within the window        */\r
+               private  ulong R0, R1, R2;      /* for the LRU offset system               */\r
+               private  UInt32 main_elements;   /* number of main tree elements            */\r
+               private  int   header_read;     /* have we started decoding at all yet?    */\r
+               private  UInt32 block_type;      /* type of this block                      */\r
+               private  ulong block_length;    /* uncompressed length of this block       */\r
+               private  ulong block_remaining; /* uncompressed bytes still left to decode */\r
+               private  ulong frames_read;     /* the number of CFDATA blocks processed   */\r
+               private  long  intel_filesize;  /* magic header value used for transform   */\r
+               private  long  intel_curpos;    /* current offset in transform space       */\r
+               private  int   intel_started;   /* have we seen any translatable data yet? */\r
+\r
+\r
+               private  uint [] PRETREE_table = new uint[(1<<(6)) + (((20))<<1)]; \r
+               private  byte [] PRETREE_len = new byte [((20)) + (64)];\r
+\r
+               private  uint [] MAINTREE_table= new uint[(1<<(12)) + (((256) + 50*8)<<1)]; \r
+               private  byte [] MAINTREE_len = new byte [((256) + 50*8) + (64)];\r
+\r
+               private  uint [] LENGTH_table= new uint[(1<<(12)) + (((249)+1)<<1)]; \r
+               private  byte [] LENGTH_len = new byte [((249)+1) + (64)];\r
+\r
+               private  uint [] ALIGNED_table= new uint[(1<<(7)) + (((8))<<1)]; \r
+               private  byte [] ALIGNED_len = new byte [((8)) + (64)];         \r
+               private System.IO.BinaryReader BitSource=null;\r
+               private System.IO.Stream OutputStream=null;\r
+\r
+               /* LZX decruncher */\r
+\r
+               /* Microsoft's LZX document and their implementation of the\r
+               * com.ms.util.cab Java package do not concur.\r
+               *\r
+               * In the LZX document, there is a table showing the correlation between\r
+               * window size and the number of position slots. It states that the 1MB\r
+               * window = 40 slots and the 2MB window = 42 slots. In the implementation,\r
+               * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the\r
+               * first slot whose position base is equal to or more than the required\r
+               * window size'. This would explain why other tables in the document refer\r
+               * to 50 slots rather than 42.\r
+               *\r
+               * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode\r
+               * is not defined in the specification.\r
+               *\r
+               * The LZX document does not state the uncompressed block has an\r
+               * uncompressed length field. Where does this length field come from, so\r
+               * we can know how large the block is? The implementation has it as the 24\r
+               * bits following after the 3 blocktype bits, before the alignment\r
+               * padding.\r
+               *\r
+               * The LZX document states that aligned offset blocks have their aligned\r
+               * offset huffman tree AFTER the main and length trees. The implementation\r
+               * suggests that the aligned offset tree is BEFORE the main and length\r
+               * trees.\r
+               *\r
+               * The LZX document decoding algorithm states that, in an aligned offset\r
+               * block, if an extra_bits value is 1, 2 or 3, then that number of bits\r
+               * should be read and the result added to the match offset. This is\r
+               * correct for 1 and 2, but not 3, where just a huffman symbol (using the\r
+               * aligned tree) should be read.\r
+               *\r
+               * Regarding the E8 preprocessing, the LZX document states 'No translation\r
+               * may be performed on the last 6 bytes of the input block'. This is\r
+               * correct.  However, the pseudocode provided checks for the *E8 leader*\r
+               * up to the last 6 bytes. If the leader appears between -10 and -7 bytes\r
+               * from the end, this would cause the next four bytes to be modified, at\r
+               * least one of which would be in the last 6 bytes, which is not allowed\r
+               * according to the spec.\r
+               *\r
+               * The specification states that the huffman trees must always contain at\r
+               * least one element. However, many CAB files contain blocks where the\r
+               * length tree is completely empty (because there are no matches), and\r
+               * this is expected to succeed.\r
+               */\r
+\r
+               /* LZX uses what it calls 'position slots' to represent match offsets.\r
+               * What this means is that a small 'position slot' number and a small\r
+               * offset from that slot are encoded instead of one large offset for\r
+               * every match.\r
+               * - position_base is an index to the position slot bases\r
+               * - extra_bits states how many bits of offset-from-base data is needed.\r
+               */\r
+               private byte [] extra_bits = {\r
+                                                                                0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,\r
+                                                                                7,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,\r
+                                                                                15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\r
+                                                                                17, 17, 17\r
+                                                                        };\r
+\r
+               private ulong [] position_base = {\r
+                                                                                        0,       1,       2,      3,      4,      6,      8,     12,     16,     24,     32,       48,      64,      96,     128,     192,\r
+                                                                                        256,     384,     512,    768,   1024,   1536,   2048,   3072,   4096,   6144,   8192,    12288,   16384,   24576,   32768,   49152,\r
+                                                                                        65536,   98304,  131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,\r
+                                                                                        1835008, 1966080, 2097152\r
+                                                                                };\r
+               \r
+               private UInt32 ReadUInt16()\r
+               {\r
+                       UInt32 rc=0;\r
+                       UInt32 Byte1=0;\r
+                       UInt32 Byte2=0;\r
+                       try\r
+                       {                               \r
+                               Byte1=BitSource.ReadByte();\r
+                               Byte2=BitSource.ReadByte();\r
+                       }\r
+                       catch(Exception)\r
+                       {\r
+                       }                       \r
+                       rc=(Byte2<<8)+Byte1;\r
+                       return rc;\r
+               }\r
+\r
+               public bool LZXinit(int WindowSize)\r
+               {                       \r
+                       ulong wndsize = (ulong)(1 << WindowSize);\r
+                       int i, posn_slots;\r
+\r
+                       /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */\r
+                       /* if a previously allocated window is big enough, keep it     */\r
+                       if (WindowSize< 15 || WindowSize> 21) return false;\r
+\r
+                       /* allocate state and associated window */              \r
+                       window = new byte[wndsize];\r
+                       if (window==null)\r
+                       {                                       \r
+                               return false;\r
+                       }\r
+\r
+                       actual_size = wndsize;\r
+                       window_size = wndsize;\r
+\r
+                       /* calculate required position slots */\r
+                       if (WindowSize == 20) posn_slots = 42;\r
+                       else if (WindowSize== 21) posn_slots = 50;\r
+                       else posn_slots = WindowSize << 1;\r
+\r
+                       /** alternatively **/\r
+                       /* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */\r
+\r
+                       /* initialize other state */\r
+                       R0  =  R1  = R2 = 1;\r
+                       main_elements   = (uint)(LZX_NUM_CHARS + (posn_slots << 3));\r
+                       header_read     = 0;\r
+                       frames_read     = 0;\r
+                       block_remaining = 0;\r
+                       block_type      = LZX_BLOCKTYPE_INVALID;\r
+                       intel_curpos    = 0;\r
+                       intel_started   = 0;\r
+                       window_posn     = 0;\r
+\r
+                       /* initialise tables to 0 (because deltas will be applied to them) */\r
+                       for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) MAINTREE_len[i] = 0;\r
+                       for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   LENGTH_len[i]   = 0;                      \r
+\r
+                       return true;\r
+               }\r
+\r
+               public void LZXteardown()\r
+               {\r
+                       window=null;                    \r
+               }\r
+\r
+               public int LZXreset()\r
+               {\r
+                       R0  =  R1  = R2 = 1;\r
+                       header_read     = 0;\r
+                       frames_read     = 0;\r
+                       block_remaining = 0;\r
+                       block_type      = LZX_BLOCKTYPE_INVALID;\r
+                       intel_curpos    = 0;\r
+                       intel_started   = 0;\r
+                       window_posn     = 0;\r
+\r
+                       for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) MAINTREE_len[i] = 0;\r
+                       for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++)   LENGTH_len[i]   = 0;\r
+\r
+                       return DECR_OK;\r
+               }\r
+\r
+\r
+               /* Bitstream reading macros:\r
+               *\r
+               * INIT_BITSTREAM    should be used first to set up the system\r
+               * READ_BITS(var,n)  takes N bits from the buffer and puts them in var\r
+               *\r
+               * ENSURE_BITS(n)    ensures there are at least N bits in the bit buffer\r
+               * PEEK_BITS(n)      extracts (without removing) N bits from the bit buffer\r
+               * REMOVE_BITS(n)    removes N bits from the bit buffer\r
+               *\r
+               * These bit access routines work by using the area beyond the MSB and the\r
+               * LSB as a free source of zeroes. This avoids having to mask any bits.\r
+               * So we have to know the bit width of the bitbuffer variable. This is\r
+               * sizeof(ulong) * 8, also defined as ULONG_BITS\r
+               */\r
+\r
+               /* number of bits in ulong. Note: This must be at multiple of 16, and at\r
+               * least 32 for the bitbuffer code to work (ie, it must be able to ensure\r
+               * up to 17 bits - that's adding 16 bits when there's one bit left, or\r
+               * adding 32 bits when there are no bits left. The code should work fine\r
+               * for machines where ulong >= 32 bits.\r
+               */\r
+               private int ULONG_BITS()\r
+               {\r
+                       int rc=(System.Runtime.InteropServices.Marshal.SizeOf(typeof(System.UInt32))<<3);\r
+                       return rc;\r
+               }\r
+\r
+               /* make_decode_table(nsyms, nbits, length[], table[])\r
+               *\r
+               * This function was coded by David Tritscher. It builds a fast huffman\r
+               * decoding table out of just a canonical huffman code lengths table.\r
+               *\r
+               * nsyms  = total number of symbols in this huffman tree.\r
+               * nbits  = any symbols with a code length of nbits or less can be decoded\r
+               *          in one lookup of the table.\r
+               * length = A table to get code lengths from [0 to syms-1]\r
+               * table  = The table to fill up with decoded symbols and pointers.\r
+               *\r
+               * Returns 0 for OK or 1 for error\r
+               */\r
+\r
+               private int make_decode_table(ulong nsyms, byte nbits, ref byte [] length, ref UInt32[] table) \r
+               {\r
+                       ulong sym;\r
+                       ulong leaf;\r
+                       byte bit_num = 1;\r
+                       ulong fill;\r
+                       ulong pos         = 0; /* the current position in the decode table */\r
+                       ulong table_mask  = (ulong)(1 << nbits);\r
+                       ulong bit_mask    = table_mask >> 1; /* don't do 0 length codes */\r
+                       ulong next_symbol = bit_mask; /* base of allocation for long codes */\r
+\r
+                       /* fill entries for codes short enough for a direct mapping */\r
+                       while (bit_num <= nbits) \r
+                       {\r
+                               for (sym = 0; sym < nsyms; sym++) \r
+                               {\r
+                                       if (length[sym] == bit_num) \r
+                                       {\r
+                                               leaf = pos;\r
+\r
+                                               if((pos += bit_mask) > table_mask) return 1; /* table overrun */\r
+\r
+                                               /* fill all possible lookups of this symbol with the symbol itself */\r
+                                               fill = bit_mask;\r
+                                               while (fill-- > 0) table[leaf++] = (uint)sym;\r
+                                       }\r
+                               }\r
+                               bit_mask >>= 1;\r
+                               bit_num++;\r
+                       }\r
+\r
+                       /* if there are any codes longer than nbits */\r
+                       if (pos != table_mask) \r
+                       {\r
+                               /* clear the remainder of the table */\r
+                               for (sym = pos; sym < table_mask; sym++) table[sym] = 0;\r
+\r
+                               /* give ourselves room for codes to grow by up to 16 more bits */\r
+                               pos <<= 16;\r
+                               table_mask <<= 16;\r
+                               bit_mask = 1 << 15;\r
+\r
+                               while (bit_num <= 16) \r
+                               {\r
+                                       for (sym = 0; sym < nsyms; sym++) \r
+                                       {\r
+                                               if (length[sym] == bit_num) \r
+                                               {\r
+                                                       leaf = pos >> 16;\r
+                                                       for (fill = 0; fill < (ulong)(bit_num - nbits); fill++) \r
+                                                       {\r
+                                                               /* if this path hasn't been taken yet, 'allocate' two entries */\r
+                                                               if (table[leaf] == 0) \r
+                                                               {\r
+                                                                       table[(next_symbol << 1)] = 0;\r
+                                                                       table[(next_symbol << 1) + 1] = 0;\r
+                                                                       table[leaf] = (uint)next_symbol++;\r
+                                                               }\r
+                                                               /* follow the path and select either left or right for next bit */\r
+                                                               leaf = table[leaf] << 1;\r
+                                                               if (((pos >> (byte)(15-fill)) & 1)==1) \r
+                                                                       leaf++;\r
+                                                       }\r
+                                                       table[leaf] = (uint)sym;\r
+\r
+                                                       if ((pos += bit_mask) > table_mask) \r
+                                                               return 1; /* table overflow */\r
+                                               }\r
+                                       }\r
+                                       bit_mask >>= 1;\r
+                                       bit_num++;\r
+                               }\r
+                       }\r
+\r
+                       /* full table? */\r
+                       if (pos == table_mask) \r
+                               return 0;\r
+\r
+                       /* either erroneous table, or all elements are 0 - let's find out. */\r
+                       for (sym = 0; sym < nsyms; sym++) if (length[(uint)sym]!=0) \r
+                                                                                                 return 1;\r
+\r
+                       return 0;\r
+               }               \r
+\r
+               private int lzx_read_lens(byte []lens, ulong first, ulong last, ref lzx_bits lb) \r
+               {\r
+                       ulong i,j, x,y;\r
+                       int z;\r
+                       \r
+                       UInt32 bitbuf = lb.bb;\r
+                       int bitsleft = lb.bl;                   \r
+\r
+                       UInt32 [] hufftbl=null;                 \r
+\r
+                       for (x = 0; x < 20; x++) \r
+                       {\r
+                               do\r
+                               {\r
+                               while (bitsleft < (4)) \r
+                               { \r
+                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                       bitsleft += 16; \r
+                               } \r
+                                       y = (bitbuf >> (ULONG_BITS()- (4))); \r
+                                       bitbuf <<= 4; \r
+                                       bitsleft -= 4; \r
+                               }\r
+                               while (false);\r
+                               PRETREE_len[x] = (byte)y;\r
+                       }       \r
+                       if (make_decode_table( 20, 6, ref PRETREE_len, ref PRETREE_table)!=0)\r
+                               return 2;                       \r
+\r
+                       for (x = first; x < last; ) \r
+                       {\r
+                               do\r
+                               {\r
+                               while (bitsleft < 16) \r
+                               { \r
+                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                       bitsleft += 16; \r
+                               } \r
+                                       hufftbl = PRETREE_table; \r
+                                       if ((i = hufftbl[((ulong)bitbuf >> (ULONG_BITS()- 6))]) >= 20) \r
+                                       { \r
+                                               j = (ulong)(1 << (byte)(ULONG_BITS()- ((6)))); \r
+                                               do \r
+                                               {\r
+                                                       j >>= 1; \r
+                                                       i <<= 1; \r
+                                                       if ((bitbuf & j)!=0)\r
+                                                               i|=1;\r
+                                                       else\r
+                                                               i|=0;\r
+\r
+                                                       if (j==0) \r
+                                                       {\r
+                                                               return (2); \r
+                                                       } \r
+                                               } \r
+                                               while ((i = hufftbl[i]) >= 20); \r
+                                       } \r
+                                       z = (int)i;\r
+                                       j = PRETREE_len[z]; \r
+                                       bitbuf <<= (byte)j;\r
+                                       bitsleft -= (int)j; \r
+                               }\r
+                               while (false);\r
+\r
+                               if (z == 17) \r
+                               {\r
+                                       do\r
+                                       {\r
+                                       while (bitsleft < (4)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16; \r
+                                       } \r
+                                               y = (bitbuf >> (ULONG_BITS()- (4))); \r
+                                               bitbuf <<= 4;\r
+                                               bitsleft -= 4; \r
+                                       }\r
+                                       while(false);\r
+                                       y += 4;\r
+                               \r
+                                       while ((y--)!=0) \r
+                                               lens[x++] = 0;\r
+                               }\r
+                               else if (z == 18) \r
+                               {                                       \r
+                                       do\r
+                                       {\r
+                                       while (bitsleft < (5)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16;\r
+                                       } \r
+                                               (y) = (bitbuf >> (ULONG_BITS()- (5))); \r
+                                               bitbuf <<= 5;\r
+                                               bitsleft -= 5; \r
+                                       }\r
+                                       while (false);\r
+                               \r
+                                       y += 20;\r
+\r
+                                       while ((y--)!=0) \r
+                                               lens[x++] = 0;\r
+                               }\r
+                               else if (z == 19) \r
+                               {\r
+                                       do\r
+                                       {\r
+                                       while (bitsleft < (1)) \r
+                                       {\r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16; \r
+                                       } \r
+                                               y = (bitbuf >> (ULONG_BITS()- (1))); \r
+                                               bitbuf <<= 1;\r
+                                               bitsleft -= 1; \r
+                                       }\r
+                                       while(false);\r
+                                       y += 4;\r
+                                       do\r
+                                       {\r
+                                       while (bitsleft < (16)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16;                                                         \r
+                                       } \r
+                                               hufftbl = (PRETREE_table); \r
+                                               if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 6))]) >= 20) \r
+                                               { \r
+                                                       j = (ulong)1 << (byte)(ULONG_BITS()- 6); \r
+                                                       do \r
+                                                       { \r
+                                                               j >>= 1; \r
+                                                               i <<= 1; \r
+                                                               if ((bitbuf & j)==0)\r
+                                                                       i|=0;\r
+                                                               else\r
+                                                                       i|=1;\r
+                                                               if (j==0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                       } \r
+                                                       while ((i = hufftbl[i]) >= 20); \r
+                                               } \r
+                                               z = (int)i;\r
+                                               j = PRETREE_len[z];                                     \r
+\r
+                                               bitbuf <<= (byte)j;\r
+                                               bitsleft -= (int)j; \r
+                                       }\r
+                                       while(false);\r
+                                       z = lens[x] - z; \r
+                                       if (z < 0) \r
+                                               z += 17;\r
+\r
+                                       while ((y--)!=0) \r
+                                               lens[x++] = (byte)z;\r
+                               }\r
+                               else \r
+                               {\r
+                                       z = lens[x] - z; \r
+                                       if (z < 0) \r
+                                               z += 17;\r
+                                       lens[x++] = (byte)z;\r
+                               }\r
+                       }\r
+                       lb.bb = bitbuf;\r
+                       lb.bl = bitsleft;\r
+                       return 0;\r
+               }\r
+\r
+               public int LZXdecompress(System.IO.BinaryReader inpos, System.IO.Stream outpos, ref ulong inlen, ref ulong outlen) \r
+               {\r
+                       BitSource=inpos;\r
+                       OutputStream=outpos;\r
+                       \r
+                       long endinp = BitSource.BaseStream.Position+(long)inlen;                                                \r
+                       ulong runsrc, rundest;\r
+                       UInt32 [] hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */\r
+\r
+                       UInt32 bitbuf;\r
+                       int bitsleft;\r
+                       ulong match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */\r
+                       lzx_bits lb; /* used in READ_LENGTHS macro */\r
+                       lb=new lzx_bits();                      \r
+\r
+                       int togo = (int)outlen, this_run, main_element, aligned_bits;\r
+                       int match_length, length_footer, extra, verbatim_bits;\r
+\r
+                       bitsleft = 0; \r
+                       bitbuf = 0; \r
+\r
+                       /* read header if necessary */\r
+                       if (header_read==0) \r
+                       {\r
+                               i = j = 0;      \r
+                               do\r
+                               {\r
+                               while (bitsleft < (1)) \r
+                               { \r
+                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                       bitsleft += 16; \r
+                               } \r
+                                       k = (bitbuf >> (ULONG_BITS()- (1))); \r
+                                       bitbuf <<= 1;\r
+                                       bitsleft -= 1;\r
+                               }\r
+                               while(false);\r
+\r
+                               if (k!=0)       \r
+                               {\r
+                                       do\r
+                                       {\r
+                                       while (bitsleft < (16)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() -16 -  bitsleft); \r
+                                               bitsleft += 16; \r
+                                       } \r
+                                               i = (bitbuf >> (ULONG_BITS()- (16))); \r
+                                               bitbuf <<= 16;\r
+                                               bitsleft -= 1;\r
+                                       }\r
+                                       while(false);\r
+\r
+                                       do\r
+                                       {\r
+                                       while (bitsleft < (16)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16;                                                         \r
+                                       } \r
+                                               j = (bitbuf >> (ULONG_BITS()- (16))); \r
+                                               bitbuf <<= 16;\r
+                                               bitsleft -= 16;                                 \r
+                                       }\r
+                                       while(false);\r
+                               }\r
+                               intel_filesize = (long)((i << 16) | j); \r
+                               header_read = 1;\r
+                       }\r
+\r
+                       /* main decoding loop */\r
+                       while (togo > 0) \r
+                       {\r
+                               if (block_remaining == 0) \r
+                               {\r
+                                       if (block_type == (3)) \r
+                                       {\r
+                                               if ((block_length & 1)!=0) \r
+                                                       BitSource.ReadByte();\r
+                                               bitsleft = 0; \r
+                                               bitbuf = 0; \r
+                                       }\r
+\r
+                                       do \r
+                                       { \r
+                                       while (bitsleft < (3)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16; \r
+                                       } \r
+                                               (block_type) = (uint)(bitbuf >> (ULONG_BITS()- (3))); \r
+                                               bitbuf <<= 3; \r
+                                               bitsleft -= 3; \r
+                                       } \r
+                                       while (false);\r
+\r
+                                       do \r
+                                       { \r
+                                       while (bitsleft < (16)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16; \r
+                                       } \r
+                                               (i) = (bitbuf >> (ULONG_BITS()- (16))); \r
+                                               bitbuf <<= 16;  \r
+                                               bitsleft -= 16; \r
+                                       } \r
+                                       while (false);\r
+\r
+                                       do \r
+                                       { \r
+                                       while (bitsleft < (8)) \r
+                                       { \r
+                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                               bitsleft += 16; \r
+                                       } \r
+                                               (j) = (bitbuf >> (ULONG_BITS()- (8))); \r
+                                               bitbuf <<= 8; \r
+                                               bitsleft -= 8; \r
+                                       } \r
+                                       while (false);\r
+                                       block_remaining = block_length = (i << 8) | j;\r
+\r
+                                       switch (block_type) \r
+                                       {\r
+                                               case (LZX_BLOCKTYPE_ALIGNED):\r
+                                                       for (i = 0; i < 8; i++) \r
+                                                       { \r
+                                                               do \r
+                                                               { \r
+                                                               while (bitsleft < (3)) \r
+                                                               { \r
+                                                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                       bitsleft += 16; \r
+                                                               } \r
+                                                                       (j) = (bitbuf >> (ULONG_BITS()- (3))); \r
+                                                                       bitbuf <<= 3; \r
+                                                                       bitsleft -= 3; \r
+                                                               } \r
+                                                               while (false); \r
+                                                               (ALIGNED_len)[i] = (byte)j; \r
+                                                       }\r
+                                                       if (make_decode_table( 8, 7, ref ALIGNED_len, ref ALIGNED_table)!=0) \r
+                                                       { \r
+                                                               return (2); \r
+                                                       }\r
+\r
+                                                       do \r
+                                                       { \r
+                                                               lb.bb = bitbuf; \r
+                                                               lb.bl = bitsleft;                                                               \r
+                                                               if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                               bitbuf = lb.bb; \r
+                                                               bitsleft = lb.bl;                                                               \r
+                                                       } \r
+                                                       while (false);\r
+                                                       do \r
+                                                       { \r
+                                                               lb.bb = bitbuf; \r
+                                                               lb.bl = bitsleft; \r
+                                                               if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                               bitbuf = lb.bb; \r
+                                                               bitsleft = lb.bl; \r
+                                                       } \r
+                                                       while (false);\r
+\r
+                                                       if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0) \r
+                                                       { \r
+                                                               return (2); \r
+                                                       }\r
+                                                       if (MAINTREE_len[0xE8] != 0) intel_started = 1;\r
+\r
+                                                       do \r
+                                                       { \r
+                                                               lb.bb = bitbuf; \r
+                                                               lb.bl = bitsleft; \r
+                                                               if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                               bitbuf = lb.bb; \r
+                                                               bitsleft = lb.bl; \r
+                                                       } \r
+                                                       while (false);\r
+                                                       if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0) \r
+                                                       { \r
+                                                               return (2); \r
+                                                       }\r
+                                                       break;                                                  \r
+                                   \r
+                                               case (LZX_BLOCKTYPE_VERBATIM):\r
+                                                       do \r
+                                                       { \r
+                                                               lb.bb = bitbuf; \r
+                                                               lb.bl = bitsleft;                                                               \r
+                                                               if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                               bitbuf = lb.bb; \r
+                                                               bitsleft = lb.bl;                                                               \r
+                                                       } \r
+                                                       while (false);\r
+                                                       do \r
+                                                       { \r
+                                                               lb.bb = bitbuf; \r
+                                                               lb.bl = bitsleft;                                                               \r
+                                                               if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                               bitbuf = lb.bb; \r
+                                                               bitsleft = lb.bl; \r
+                                                       } \r
+                                                       while (false);\r
+\r
+                                                       if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0) \r
+                                                       { \r
+                                                               return (2); \r
+                                                       }\r
+                                                       if (MAINTREE_len[0xE8] != 0) intel_started = 1;\r
+\r
+                                                       do \r
+                                                       { \r
+                                                               lb.bb = bitbuf; \r
+                                                               lb.bl = bitsleft; \r
+                                                               if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0) \r
+                                                               { \r
+                                                                       return (2); \r
+                                                               } \r
+                                                               bitbuf = lb.bb; \r
+                                                               bitsleft = lb.bl; \r
+                                                       } \r
+                                                       while (false);\r
+                                                       if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0) \r
+                                                       { \r
+                                                               return (2); \r
+                                                       }\r
+                                                       break;\r
+\r
+                                               case (LZX_BLOCKTYPE_UNCOMPRESSED):\r
+                                                       intel_started = 1; \r
+                                                       while (bitsleft < (16)) \r
+                                                       { \r
+                                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - bitsleft); \r
+                                                               bitsleft += 16;\r
+                                                       } \r
+                                                       if (bitsleft > 16)\r
+                                                       {\r
+                                                               BitSource.BaseStream.Seek(-2,System.IO.SeekOrigin.Current);                                                             \r
+                                                       }\r
+                                                       R0 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));\r
+                                                       R1 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));\r
+                                                       R2 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));\r
+                                                       break;\r
+\r
+                                               default:\r
+                                                       return (DECR_ILLEGALDATA);\r
+                                       }\r
+                               }\r
+\r
+                               /* buffer exhaustion check */\r
+                               if (BitSource.BaseStream.Position > (long) endinp) \r
+                               {\r
+                                       /* it's possible to have a file where the next run is less than\r
+                                       * 16 bits in size. In this case, the READ_HUFFSYM() macro used\r
+                                       * in building the tables will exhaust the buffer, so we should\r
+                                       * allow for this, but not allow those accidentally read bits to\r
+                                       * be used (so we check that there are at least 16 bits\r
+                                       * remaining - in this boundary case they aren't really part of\r
+                                       * the compressed data)\r
+                                       */\r
+                                       if (BitSource.BaseStream.Position> (long)(endinp+2) || bitsleft < 16) \r
+                                               return DECR_ILLEGALDATA;\r
+                               }\r
+\r
+                               while ((this_run = (int)block_remaining) > 0 && togo > 0) \r
+                               {\r
+                                       if (this_run > togo)\r
+                                               this_run = togo;\r
+\r
+                                       togo -= this_run;\r
+                                       block_remaining -= (ulong)this_run;\r
+\r
+                                       /* apply 2^x-1 mask */\r
+                                       window_posn &= window_size - 1;\r
+\r
+                                       /* runs can't straddle the window wraparound */\r
+                                       if ((window_posn + (ulong)this_run) > window_size)\r
+                                               return DECR_DATAFORMAT;\r
+\r
+                                       switch (block_type) \r
+                                       {\r
+                                               case LZX_BLOCKTYPE_VERBATIM:\r
+                                                       while (this_run > 0) \r
+                                                       {\r
+                                                               do \r
+                                                               { \r
+                                                               while (bitsleft < (16)) \r
+                                                               { \r
+                                                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                       bitsleft += 16; \r
+                                                               } \r
+                                                                       hufftbl = MAINTREE_table; \r
+                                                                       if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= 256 + 50*8) \r
+                                                                       { \r
+                                                                               j = (ulong)(1 << (ULONG_BITS()- 12)); \r
+                                                                               do \r
+                                                                               { \r
+                                                                                       j >>= 1; \r
+                                                                                       i <<= 1; \r
+                                                                                       if ((bitbuf & j)!=0) \r
+                                                                                               i|=1; \r
+                                                                                       else \r
+                                                                                               i|=0; \r
+                                                                                       if (j==0) \r
+                                                                                       { \r
+                                                                                               return (2); \r
+                                                                                       } \r
+                                                                               } \r
+                                                                               while ((i = hufftbl[i]) >= (((256) + 50*8))); \r
+                                                                       } \r
+                                                                       j = MAINTREE_len[main_element = (int)i]; \r
+                                                                       bitbuf <<= (byte)j; \r
+                                                                       bitsleft -= (byte)j; \r
+                                                               } \r
+                                                               while (false);\r
+\r
+                                                               if (main_element < (256)) \r
+                                                               {                                   \r
+                                                                       window[window_posn++] = (byte)main_element;\r
+                                                                       this_run--;\r
+                                                               }\r
+                                                               else \r
+                                                               {                                   \r
+                                                                       main_element -= (256);\r
+\r
+                                                                       match_length = main_element & (7);\r
+                                                                       if (match_length == (7)) \r
+                                                                       {\r
+                                                                               do \r
+                                                                               { \r
+                                                                               while (bitsleft < (16)) \r
+                                                                               { \r
+                                                                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                       bitsleft += 16; \r
+                                                                               } \r
+                                                                                       hufftbl = (LENGTH_table); \r
+                                                                                       if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((249)+1))) \r
+                                                                                       { \r
+                                                                                               j = (ulong)(1 << (ULONG_BITS()- ((12)))); \r
+                                                                                               do \r
+                                                                                               { \r
+                                                                                                       j >>= 1; \r
+                                                                                                       i <<= 1; \r
+                                                                                                       if ((bitbuf & j)!=0) \r
+                                                                                                               i|=1; \r
+                                                                                                       else \r
+                                                                                                               i|=0; \r
+                                                                                                       if (j==0) \r
+                                                                                                       { \r
+                                                                                                               return (2); \r
+                                                                                                       } \r
+                                                                                               } \r
+                                                                                               while ((i = hufftbl[i]) >= (((249)+1))); \r
+                                                                                       } \r
+                                                                                       j = LENGTH_len[(length_footer) = (int)i]; \r
+                                                                                       bitbuf <<= (byte)j; \r
+                                                                                       bitsleft -= (byte)j; \r
+                                                                               } \r
+                                                                               while (false);\r
+\r
+                                                                               match_length += length_footer;\r
+                                                                       }\r
+                                                                       match_length += (2);\r
+\r
+                                                                       match_offset = (ulong)(main_element >> 3);\r
+\r
+                                                                       if (match_offset > 2) \r
+                                                                       {                                       \r
+                                                                               if (match_offset != 3) \r
+                                                                               {\r
+                                                                                       extra = extra_bits[match_offset];\r
+                                                                                       do \r
+                                                                                       { \r
+                                                                                       while (bitsleft < (extra)) \r
+                                                                                       { \r
+                                                                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                               bitsleft += 16; \r
+                                                                                       } \r
+                                                                                               verbatim_bits = (int)(bitbuf >> (ULONG_BITS()- (extra))); \r
+                                                                                               bitbuf <<= extra; \r
+                                                                                               bitsleft -= extra; \r
+                                                                                       } \r
+                                                                                       while (false);\r
+                                                                                       match_offset = position_base[match_offset] - 2 + (ulong)verbatim_bits;\r
+                                                                               }\r
+                                                                               else \r
+                                                                               {\r
+                                                                                       match_offset = 1;\r
+                                                                               }\r
+                                   \r
+                                                                               R2 = R1; R1 = R0; R0 = match_offset;\r
+                                                                       }\r
+                                                                       else if (match_offset == 0) \r
+                                                                       {\r
+                                                                               match_offset = R0;\r
+                                                                       }\r
+                                                                       else if (match_offset == 1) \r
+                                                                       {\r
+                                                                               match_offset = R1;\r
+                                                                               R1 = R0; R0 = match_offset;\r
+                                                                       }\r
+                                                                       else  \r
+                                                                       {\r
+                                                                               match_offset = R2;\r
+                                                                               R2 = R0; R0 = match_offset;\r
+                                                                       }\r
+\r
+                                                                       rundest = window_posn;\r
+                                                                       // rundest= window+window_posn\r
+                                                                       runsrc  = rundest - match_offset;                                                               \r
+                                                                       window_posn += (ulong)match_length;\r
+                                                                       this_run -= match_length;\r
+                               \r
+                                                                       // runsrc < window\r
+                                                                       while ((runsrc<0) && (match_length-- > 0))\r
+                                                                       {\r
+                                                                               window[rundest++]=window[runsrc+window_size];\r
+                                                                               // *rundest++ = *(runsrc + window_size); \r
+                                                                               runsrc++;\r
+                                                                       }\r
+                               \r
+                                                                       while (match_length-- > 0) \r
+                                                                       {\r
+                                                                               window[rundest++]=window[runsrc++];\r
+                                                                               // *rundest++ = *runsrc++;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       break;\r
+\r
+                                               case LZX_BLOCKTYPE_ALIGNED:\r
+                                                       while (this_run > 0) \r
+                                                       {\r
+                                                               do \r
+                                                               { \r
+                                                               while (bitsleft < (16)) \r
+                                                               { \r
+                                                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                       bitsleft += 16;                                                                         \r
+                                                               } \r
+                                                                       hufftbl = MAINTREE_table; \r
+                                                                       if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((256) + 50*8))) \r
+                                                                       { \r
+                                                                               j = (ulong)1 << (ULONG_BITS()- ((12))); \r
+                                                                               do \r
+                                                                               { \r
+                                                                                       j >>= 1; \r
+                                                                                       i <<= 1; \r
+                                                                                       if ((bitbuf & j)!=0) \r
+                                                                                               i|=1; \r
+                                                                                       else \r
+                                                                                               i|=0; \r
+                                                                                       if (j==0) \r
+                                                                                       { \r
+                                                                                               return (2); \r
+                                                                                       } \r
+                                                                               } \r
+                                                                               while ((i = hufftbl[i]) >= (((256) + 50*8))); \r
+                                                                       } \r
+                                                                       j = MAINTREE_len[(main_element) = (int)i]; \r
+                                                                       bitbuf <<= (int)j; \r
+                                                                       bitsleft -= (int)j; \r
+                                                               } \r
+                                                               while (false);\r
+\r
+                                                               if (main_element < (256)) \r
+                                                               {                            \r
+                                                                       window[window_posn++] = (byte)main_element;\r
+                                                                       this_run--;\r
+                                                               }\r
+                                                               else \r
+                                                               {                            \r
+                                                                       main_element -= (256);\r
+                                                                       match_length = main_element & (7);\r
+                                                                       if (match_length == (7)) \r
+                                                                       {\r
+                                                                               do \r
+                                                                               { \r
+                                                                               while (bitsleft < (16)) \r
+                                                                               { \r
+                                                                                       bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                       bitsleft += 16; \r
+                                                                               } \r
+                                                                                       hufftbl = LENGTH_table; \r
+                                                                                       if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((249)+1))) \r
+                                                                                       { \r
+                                                                                               j = (ulong) 1 << (ULONG_BITS()- ((12))); \r
+                                                                                               do \r
+                                                                                               { \r
+                                                                                                       j >>= 1; \r
+                                                                                                       i <<= 1; \r
+                                                                                                       if ((bitbuf & j)!=0)\r
+                                                                                                               i|=1;\r
+                                                                                                       else\r
+                                                                                                               i|=0;\r
+\r
+                                                                                                       if (j==0) \r
+                                                                                                       { \r
+                                                                                                               return (2); \r
+                                                                                                       } \r
+                                                                                               } \r
+                                                                                               while ((i = hufftbl[i]) >= (((249)+1))); \r
+                                                                                       } \r
+                                                                                       j = LENGTH_len[length_footer = (int)i]; \r
+                                                                                       bitbuf <<= (int)j; \r
+                                                                                       bitsleft -= (int)j; \r
+                                                                               } \r
+                                                                               while (false);\r
+                                                                               match_length += length_footer;\r
+                                                                       }\r
+                                                                       match_length += (2);\r
+\r
+                                                                       match_offset = (ulong)(main_element >> 3);\r
+\r
+                                                                       if (match_offset > 2) \r
+                                                                       {                                \r
+                                                                               extra = extra_bits[match_offset];\r
+                                                                               match_offset = position_base[match_offset] - 2;\r
+                                                                               if (extra > 3) \r
+                                                                               {                                    \r
+                                                                                       extra -= 3;\r
+                                                                                       do \r
+                                                                                       { \r
+                                                                                       while (bitsleft < (extra)) \r
+                                                                                       {       \r
+                                                                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                               bitsleft += 16; \r
+                                                                                       } \r
+                                                                                               verbatim_bits = (int)(bitbuf >> (ULONG_BITS()- (extra))); \r
+                                                                                               bitbuf <<= extra; \r
+                                                                                               bitsleft -= extra; \r
+                                                                                       } \r
+                                                                                       while (false);\r
+                                                                                       match_offset += (ulong)(verbatim_bits << 3);\r
+                                                                                       do \r
+                                                                                       { \r
+                                                                                       while (bitsleft < (16)) \r
+                                                                                       { \r
+                                                                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                               bitsleft += 16; \r
+                                                                                       } \r
+                                                                                               hufftbl = (ALIGNED_table); \r
+                                                                                               if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 7))]) >= 8) \r
+                                                                                               { \r
+                                                                                                       j = (ulong)1 << (ULONG_BITS()- ((7))); \r
+                                                                                                       do \r
+                                                                                                       { \r
+                                                                                                               j >>= 1; \r
+                                                                                                               i <<= 1; \r
+                                                                                                               if ((bitbuf & j)!=0)\r
+                                                                                                                       i|=1;\r
+                                                                                                               else\r
+                                                                                                                       i|=0;\r
+                                                                                                               if (j==0) \r
+                                                                                                               { \r
+                                                                                                                       return (2); \r
+                                                                                                               } \r
+                                                                                                       } \r
+                                                                                                       while ((i = hufftbl[i]) >= (((8)))); \r
+                                                                                               } \r
+\r
+                                                                                               j = (ALIGNED_len)[(aligned_bits) = (int)i]; \r
+                                                                                               bitbuf <<= (int)j; \r
+                                                                                               bitsleft -= (int)j; \r
+                                                                                       } \r
+                                                                                       while (false);\r
+                                                                                       match_offset += (ulong)aligned_bits;\r
+                                                                               }\r
+                                                                               else if (extra == 3)    \r
+                                                                               {                                    \r
+                                                                                       do \r
+                                                                                       { \r
+                                                                                       while (bitsleft < (16)) \r
+                                                                                       { \r
+                                                                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                               bitsleft += 16; \r
+                                                                                       } \r
+                                                                                               hufftbl = (ALIGNED_table); \r
+                                                                                               if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 7))]) >= 8) \r
+                                                                                               { \r
+                                                                                                       j = (ulong)1 << (ULONG_BITS()- ((7))); \r
+                                                                                                       do \r
+                                                                                                       { \r
+                                                                                                               j >>= 1; \r
+                                                                                                               i <<= 1; \r
+                                                                                                               if ((bitbuf & j)!=0)\r
+                                                                                                                       i|=1;\r
+                                                                                                               else\r
+                                                                                                                       i|=0;\r
+                                                                                                               if (j!=0) \r
+                                                                                                               { \r
+                                                                                                                       return (2); \r
+                                                                                                               } \r
+                                                                                                       } \r
+                                                                                                       while ((i = hufftbl[i]) >= 8); \r
+                                                                                               } \r
+                                                                                               j = (ALIGNED_len)[(aligned_bits) = (int)i]; \r
+                                                                                               bitbuf <<= (int)j; \r
+                                                                                               bitsleft -= (int)j; \r
+                                                                                       } \r
+                                                                                       while (false);\r
+                                                                                       match_offset += (ulong)aligned_bits;\r
+                                                                               }\r
+                                                                               else if (extra > 0) \r
+                                                                               {                                     \r
+                                                                                       do \r
+                                                                                       { \r
+                                                                                       while (bitsleft < (extra)) \r
+                                                                                       { \r
+                                                                                               bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); \r
+                                                                                               bitsleft += 16; \r
+                                                                                       } \r
+                                                                                               (verbatim_bits) = (int)(bitbuf >> (int)(ULONG_BITS()- (extra))); \r
+                                                                                               bitbuf <<= extra;\r
+                                                                                               bitsleft -= extra; \r
+                                                                                       } \r
+                                                                                       while (false);\r
+                                                                                       match_offset += (ulong)verbatim_bits;\r
+                                                                               }\r
+                                                                               else  \r
+                                                                               {                                    \r
+                                                                                       match_offset = 1;\r
+                                                                               }\r
+                                \r
+                                                                               R2 = R1; R1 = R0; R0 = match_offset;\r
+                                                                       }\r
+                                                                       else if (match_offset == 0) \r
+                                                                       {\r
+                                                                               match_offset = R0;\r
+                                                                       }\r
+                                                                       else if (match_offset == 1) \r
+                                                                       {\r
+                                                                               match_offset = R1;\r
+                                                                               R1 = R0; R0 = match_offset;\r
+                                                                       }\r
+                                                                       else  \r
+                                                                       {\r
+                                                                               match_offset = R2;\r
+                                                                               R2 = R0; R0 = match_offset;\r
+                                                                       }\r
+\r
+                                                                       rundest = window_posn;\r
+                                                                       runsrc  = rundest - match_offset;\r
+                                                                       window_posn += (ulong)match_length;\r
+                                                                       this_run -= match_length;\r
+                                   \r
+                                                                       while ((runsrc<0) && (match_length-- > 0))\r
+                                                                       {\r
+                                                                               // *rundest++ = *(runsrc + window_size); runsrc++;\r
+                                                                               window[rundest++]=window[runsrc + window_size];\r
+                                                                               runsrc++;\r
+                                                                       }\r
+                                   \r
+                                                                       while (match_length-- > 0) \r
+                                                                       {\r
+                                                                               // *rundest++ = *runsrc++;\r
+                                                                               window[rundest++]=window[runsrc++];\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       break;\r
+\r
+                                               case LZX_BLOCKTYPE_UNCOMPRESSED:\r
+                                                       if ((BitSource.BaseStream.Position + (long)this_run) > (long)endinp) \r
+                                                               return (2);\r
+\r
+                                                       // memcpy(window + window_posn, inposCount, this_run);\r
+                                                       for(i=0; i<(ulong)this_run;i++)\r
+                                                       {\r
+                                                               window[window_posn+i]=BitSource.ReadByte();\r
+                                                       }\r
+                                                       window_posn += (ulong)this_run;\r
+                                                       break;\r
+\r
+                                               default:\r
+                                                       return DECR_ILLEGALDATA; /* might as well */\r
+                                       }\r
+\r
+                               }\r
+                       }\r
+\r
+                       if (togo != 0) return DECR_ILLEGALDATA;\r
+\r
+                       // memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t) outlen);\r
+                       ulong start=0;\r
+                       if (window_posn==0)\r
+                               start=(ulong)window_size;\r
+                       else\r
+                               start=(ulong)window_posn;\r
+                       \r
+                       start-=(ulong)outlen;\r
+\r
+                       long Pos=OutputStream.Position;\r
+                       for(i=0;i<(ulong)outlen;i++)\r
+                       {       \r
+                               OutputStream.WriteByte(window[start+i]);\r
+                       }\r
+                       OutputStream.Seek(Pos,System.IO.SeekOrigin.Begin);\r
+                       \r
+                       /* intel E8 decoding */\r
+                       if ((frames_read++ < 32768) && intel_filesize != 0) \r
+                       {\r
+                               if (outlen <= 6 || (intel_started==0)) \r
+                               {\r
+                                       intel_curpos += (long)outlen;\r
+                               }\r
+                               else \r
+                               {\r
+                                       // UBYTE *data    = outpos;\r
+                                       long dataend = OutputStream.Position + (int)outlen - 10;\r
+                                       long curpos    = intel_curpos;\r
+                                       long filesize  = intel_filesize;\r
+                                       long abs_off, rel_off;\r
+\r
+                                       intel_curpos = (long)curpos + (long)outlen;\r
+\r
+                                       while (OutputStream.Position < dataend) \r
+                                       {\r
+                                               if (OutputStream.ReadByte() != 0xE8) \r
+                                               { \r
+                                                       curpos++; \r
+                                                       continue; \r
+                                               }\r
+\r
+                                               abs_off = (long)(OutputStream.ReadByte() | (OutputStream.ReadByte() <<8) | (OutputStream.ReadByte() <<16) | (OutputStream.ReadByte() <<24));\r
+                                               if (abs_off < filesize)\r
+                                               {\r
+                                                       if (abs_off >= 0)\r
+                                                               rel_off = (long)(abs_off - curpos);\r
+                                                       else \r
+                                                               rel_off = (long)abs_off + filesize;\r
+                                                       OutputStream.WriteByte((byte)(rel_off  & 0x000000ff));\r
+                                                       OutputStream.WriteByte((byte)((rel_off & 0x0000ff00)>>8));\r
+                                                       OutputStream.WriteByte((byte)((rel_off & 0x00ff0000)>>16));\r
+                                                       OutputStream.WriteByte((byte)((rel_off & 0xff000000)>>24));                                                     \r
+                                               }\r
+                                               curpos += 5;\r
+                                       }\r
+                               }\r
+                       }                       \r
+                       \r
+                       return DECR_OK;\r
+               }               \r
+       }\r
+       #endregion\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/TOCItem.cs b/irc/TechBot/CHMLibrary/TOCItem.cs
new file mode 100644 (file)
index 0000000..8f6b7f9
--- /dev/null
@@ -0,0 +1,494 @@
+using System;\r
+using System.IO;\r
+using System.Collections;\r
+using System.Windows.Forms;\r
+\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>TOCItem</c> implements a toc-entry item\r
+       /// </summary>\r
+       public sealed class TOCItem\r
+       {\r
+               /// <summary>\r
+               /// Constant for standard folder (closed) image index (HH2 image list)\r
+               /// </summary>\r
+               public const int STD_FOLDER_HH2 = 4;\r
+               /// <summary>\r
+               /// Constant for standard folder (opened) image index (HH2 image list)\r
+               /// </summary>\r
+               public const int STD_FOLDER_OPEN_HH2 = 6;\r
+               /// <summary>\r
+               /// Constant for standard file image index (HH2 image list)\r
+               /// </summary>\r
+               public const int STD_FILE_HH2 = 16;\r
+\r
+               /// <summary>\r
+               /// Constant for standard folder (closed) image index (HH1 image list)\r
+               /// </summary>\r
+               public const int STD_FOLDER_HH1 = 0;\r
+               /// <summary>\r
+               /// Constant for standard folder (opened) image index (HH1 image list)\r
+               /// </summary>\r
+               public const int STD_FOLDER_OPEN_HH1 = 1;\r
+               /// <summary>\r
+               /// Constant for standard file image index (HH1 image list)\r
+               /// </summary>\r
+               public const int STD_FILE_HH1 = 10;\r
+\r
+               /// <summary>\r
+               /// Internal flag specifying the data extraction mode used for this item\r
+               /// </summary>\r
+               private DataMode _tocMode = DataMode.TextBased;\r
+               /// <summary>\r
+               /// Internal member storing the offset (only used in binary tocs)\r
+               /// </summary>\r
+               private int _offset = 0;\r
+               /// <summary>\r
+               /// Internal member storing the offset of the next item(only used in binary tocs)\r
+               /// </summary>\r
+               private int _offsetNext = 0;\r
+               /// <summary>\r
+               /// Internal member storing a merge link. \r
+               /// If the target file is in the merged files list of the CHM,\r
+               /// this item will be replaced with the target TOC or Topic, if not it will \r
+               /// be removed from TOC.\r
+               /// </summary>\r
+               private string _mergeLink = "";\r
+               /// <summary>\r
+               /// Internal member storing the toc name\r
+               /// </summary>\r
+               private string _name = "";\r
+               /// <summary>\r
+               /// Internal member storing the toc loca (content file)\r
+               /// </summary>\r
+               private string _local = "";\r
+               /// <summary>\r
+               /// Internal member storing all associated information type strings\r
+               /// </summary>\r
+               private ArrayList _infoTypeStrings = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the associated chm file\r
+               /// </summary>\r
+               private string _chmFile = "";\r
+               /// <summary>\r
+               /// Internal member storing the image index\r
+               /// </summary>\r
+               private int _imageIndex = -1;\r
+               /// <summary>\r
+               /// Internal member storing the offset of the associated topic entry (for binary tocs)\r
+               /// </summary>\r
+               private int _topicOffset = -1;\r
+               /// <summary>\r
+               /// Internal member storing the toc children\r
+               /// </summary>\r
+               private ArrayList _children = new ArrayList();\r
+               /// <summary>\r
+               /// Internal member storing the parameter collection\r
+               /// </summary>\r
+               private Hashtable _otherParams = new Hashtable();\r
+               /// <summary>\r
+               /// Internal member storing the associated chmfile object\r
+               /// </summary>\r
+               private CHMFile _associatedFile = null;\r
+               /// <summary>\r
+               /// Parent item\r
+               /// </summary>\r
+               private TOCItem _parent=null;\r
+\r
+               /// <summary>\r
+               /// Holds a pointer to the next item in the TOC\r
+               /// </summary>\r
+               public TOCItem Next=null;\r
+\r
+               /// <summary>\r
+               /// Holds a pointer to the previous item in the TOC\r
+               /// </summary>\r
+               public TOCItem Prev=null;\r
+\r
+               /// <summary>\r
+               /// Holds a pointer to the TreeNode where this TOC Item is used\r
+               /// </summary>\r
+               public System.Windows.Forms.TreeNode treeNode=null;\r
+\r
+               /// <summary>\r
+               /// Constructor of the class used during text-based data extraction\r
+               /// </summary>\r
+               /// <param name="name">name of the item</param>\r
+               /// <param name="local">local content file</param>\r
+               /// <param name="ImageIndex">image index</param>\r
+               /// <param name="chmFile">associated chm file</param>\r
+               public TOCItem(string name, string local, int ImageIndex, string chmFile)\r
+               {\r
+                       _tocMode = DataMode.TextBased;\r
+                       _name = name;\r
+                       _local = local;\r
+                       _imageIndex = ImageIndex;\r
+                       _chmFile = chmFile;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the class used during binary data extraction\r
+               /// </summary>\r
+               /// <param name="topicOffset">offset of the associated topic entry</param>\r
+               /// <param name="ImageIndex">image index to use</param>\r
+               /// <param name="associatedFile">associated chm file</param>\r
+               public TOCItem(int topicOffset, int ImageIndex, CHMFile associatedFile)\r
+               {\r
+                       _tocMode = DataMode.Binary;\r
+                       _associatedFile = associatedFile;\r
+                       _chmFile = associatedFile.ChmFilePath;\r
+                       _topicOffset = topicOffset;\r
+                       _imageIndex = ImageIndex;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               public TOCItem()\r
+               {\r
+               }\r
+\r
+               #region Data dumping\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               /// <param name="writeFilename">true if the chmfile name should be written</param>\r
+               internal void Dump(ref BinaryWriter writer, bool writeFilename)\r
+               {\r
+                       writer.Write((int)_tocMode);\r
+                       writer.Write(_topicOffset);\r
+                       writer.Write(_name);\r
+\r
+                       if((_tocMode == DataMode.TextBased)||(_topicOffset<0))\r
+                       {\r
+                               writer.Write(_local);\r
+                       }\r
+\r
+                       writer.Write(_imageIndex);\r
+\r
+                       writer.Write(_mergeLink);\r
+\r
+                       if(writeFilename)\r
+                               writer.Write(_chmFile);\r
+\r
+                       writer.Write(_infoTypeStrings.Count);\r
+\r
+                       for(int i=0; i<_infoTypeStrings.Count; i++)\r
+                               writer.Write( (_infoTypeStrings[i]).ToString() );\r
+\r
+                       writer.Write(_children.Count);\r
+\r
+                       for(int i=0; i<_children.Count; i++)\r
+                       {\r
+                               TOCItem child = ((TOCItem)(_children[i]));\r
+                               child.Dump(ref writer, writeFilename);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Dump the class data to a binary writer\r
+               /// </summary>\r
+               /// <param name="writer">writer to write the data</param>\r
+               internal void Dump(ref BinaryWriter writer)\r
+               {\r
+                       Dump(ref writer, false);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               /// <param name="readFilename">true if the chmfile name should be read</param>\r
+               internal void ReadDump(ref BinaryReader reader, bool readFilename)\r
+               {\r
+                       int i=0;\r
+                       _tocMode = (DataMode)reader.ReadInt32();\r
+                       _topicOffset = reader.ReadInt32();\r
+                       _name = reader.ReadString();\r
+\r
+                       if((_tocMode == DataMode.TextBased)||(_topicOffset<0))\r
+                       {\r
+                               _local = reader.ReadString();\r
+                       }\r
+                       \r
+                       _imageIndex = reader.ReadInt32();\r
+\r
+                       _mergeLink = reader.ReadString();\r
+\r
+                       if(readFilename)\r
+                               _chmFile = reader.ReadString();\r
+\r
+                       int nCnt = reader.ReadInt32();\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               string sIT = reader.ReadString();\r
+                               _infoTypeStrings.Add(sIT);\r
+                       }\r
+\r
+                       nCnt = reader.ReadInt32();\r
+\r
+                       if(_associatedFile != null)\r
+                               _chmFile = _associatedFile.ChmFilePath;\r
+\r
+                       for(i=0; i<nCnt; i++)\r
+                       {\r
+                               TOCItem child = new TOCItem();\r
+                               child.AssociatedFile = _associatedFile;\r
+\r
+                               child.ReadDump(ref reader, readFilename);\r
+\r
+                               if(_associatedFile != null)\r
+                                       child.ChmFile = _associatedFile.ChmFilePath;\r
+                               else if(!readFilename)\r
+                                       child.ChmFile = _chmFile;\r
+                               child.Parent = this;\r
+\r
+                               _children.Add(child);\r
+\r
+                               if(child.MergeLink.Length > 0)\r
+                                       _associatedFile.MergLinks.Add(child);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Reads the object data from a dump store\r
+               /// </summary>\r
+               /// <param name="reader">reader to read the data</param>\r
+               internal void ReadDump(ref BinaryReader reader)\r
+               {\r
+                       ReadDump(ref reader, false);\r
+               }\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the data extraction mode with which this item was created.\r
+               /// </summary>\r
+               internal DataMode TocMode\r
+               {\r
+                       get { return _tocMode; }\r
+                       set { _tocMode = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the offset of the associated topic entry\r
+               /// </summary>\r
+               internal int TopicOffset\r
+               {\r
+                       get { return _topicOffset; }\r
+                       set { _topicOffset = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the associated CHMFile instance\r
+               /// </summary>\r
+               internal CHMFile AssociatedFile\r
+               {\r
+                       get { return _associatedFile; }\r
+                       set \r
+                       { \r
+                               _associatedFile = value; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the offset of the item.\r
+               /// </summary>\r
+               /// <remarks>Only used in binary tocs</remarks>\r
+               internal int Offset\r
+               {\r
+                       get     { return _offset; }\r
+                       set     { _offset = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the offset of the next item.\r
+               /// </summary>\r
+               /// <remarks>Only used in binary tocs</remarks>\r
+               internal int OffsetNext\r
+               {\r
+                       get     { return _offsetNext; }\r
+                       set     { _offsetNext = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the ArrayList which holds all information types/categories this item is associated\r
+               /// </summary>\r
+               internal ArrayList InfoTypeStrings\r
+               {\r
+                       get { return _infoTypeStrings; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the parent of this item\r
+               /// </summary>\r
+               public TOCItem Parent\r
+               {\r
+                       get { return _parent; }\r
+                       set { _parent = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the mergelink for this item. \r
+               /// <b>You should not set the mergedlink by your own !</b>\r
+               /// This is only for loading merged CHMs.\r
+               /// </summary>\r
+               public string MergeLink\r
+               {\r
+                       get { return _mergeLink; }\r
+                       set { _mergeLink = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the name of the item\r
+               /// </summary>\r
+               public string Name\r
+               {\r
+                       get     \r
+                       { \r
+                               if(_mergeLink.Length > 0)\r
+                                       return "";\r
+\r
+                               if(_name.Length <= 0)\r
+                               {\r
+                                       if((_tocMode == DataMode.Binary )&&(_associatedFile!=null))\r
+                                       {\r
+                                               if( _topicOffset >= 0)\r
+                                               {\r
+                                                       TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);\r
+                                                       if(te != null)\r
+                                                       {\r
+                                                               return te.Title;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _name; \r
+                       }\r
+                       set     \r
+                       { \r
+                               _name = value; \r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the local of the item\r
+               /// </summary>\r
+               public string Local\r
+               {\r
+                       get     \r
+                       { \r
+                               if(_mergeLink.Length > 0)\r
+                                       return "";\r
+\r
+                               if(_local.Length <= 0)\r
+                               {\r
+                                       if((_tocMode == DataMode.Binary )&&(_associatedFile!=null))\r
+                                       {\r
+                                               if( _topicOffset >= 0)\r
+                                               {\r
+                                                       TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);\r
+                                                       if(te != null)\r
+                                                       {\r
+                                                               return te.Locale;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               return _local; \r
+                       }\r
+                       set     { _local = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the chm file\r
+               /// </summary>\r
+               public string ChmFile\r
+               {\r
+                       get     \r
+                       { \r
+                               if(_associatedFile!=null)\r
+                                       return _associatedFile.ChmFilePath;\r
+\r
+                               return _chmFile; \r
+                       }\r
+                       set     { _chmFile = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the url for the webbrowser for this file\r
+               /// </summary>\r
+               public string Url\r
+               {\r
+                       get\r
+                       {\r
+                               string sL = Local;\r
+\r
+                               if( (sL.ToLower().IndexOf("http://") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("https://") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("mailto:") >= 0) ||\r
+                                       (sL.ToLower().IndexOf("ftp://") >= 0) || \r
+                                       (sL.ToLower().IndexOf("ms-its:") >= 0))\r
+                                       return sL;\r
+\r
+                               return HtmlHelpSystem.UrlPrefix + ChmFile + "::/" + sL;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the image index of the item\r
+               /// </summary>\r
+               /// <remarks>Set this to -1 for a default icon</remarks>\r
+               public int ImageIndex\r
+               {\r
+                       get     \r
+                       { \r
+                               if( _imageIndex == -1)\r
+                               {\r
+                                       int nFolderAdd = 0;\r
+\r
+                                       if((_associatedFile != null) && (_associatedFile.ImageTypeFolder))\r
+                                       {\r
+                                               // get the value which should be added, to display folders instead of books\r
+                                               if(HtmlHelpSystem.UseHH2TreePics) \r
+                                                       nFolderAdd = 8;\r
+                                               else\r
+                                                       nFolderAdd = 4;\r
+                                       }\r
+                                       \r
+\r
+                                       if( _children.Count > 0)\r
+                                               return (HtmlHelpSystem.UseHH2TreePics ? (STD_FOLDER_HH2+nFolderAdd) : (STD_FOLDER_HH1+nFolderAdd));\r
+\r
+                                       return (HtmlHelpSystem.UseHH2TreePics ? STD_FILE_HH2 : STD_FILE_HH1);\r
+                               }\r
+                               return _imageIndex; \r
+                       }\r
+                       set     { _imageIndex = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets/Sets the children of this item.\r
+               /// </summary>\r
+               /// <remarks>Each entry in the ArrayList is of type TOCItem</remarks>\r
+               public ArrayList Children\r
+               {\r
+                       get { return _children; }\r
+                       set { _children = value; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal hashtable storing all params\r
+               /// </summary>\r
+               public Hashtable Params\r
+               {\r
+                       get { return _otherParams; }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/CHMLibrary/TableOfContents.cs b/irc/TechBot/CHMLibrary/TableOfContents.cs
new file mode 100644 (file)
index 0000000..68dfb05
--- /dev/null
@@ -0,0 +1,198 @@
+using System;\r
+using System.Diagnostics;\r
+using System.Collections;\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace HtmlHelp\r
+{\r
+       /// <summary>\r
+       /// The class <c>TableOfContents</c> holds the TOC of the htmlhelp system class.\r
+       /// </summary>\r
+       public class TableOfContents\r
+       {\r
+               private ArrayList _toc = new ArrayList();\r
+\r
+               /// <summary>\r
+               /// Standard constructor\r
+               /// </summary>\r
+               public TableOfContents()\r
+               {\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor of the class\r
+               /// </summary>\r
+               /// <param name="toc"></param>\r
+               public TableOfContents(ArrayList toc)\r
+               {\r
+                       _toc = toc;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the internal stored table of contents\r
+               /// </summary>\r
+               public ArrayList TOC\r
+               {\r
+                       get { return _toc; }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Clears the current toc\r
+               /// </summary>\r
+               public void Clear()\r
+               {\r
+                       if(_toc!=null)\r
+                               _toc.Clear();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Gets the number of topics in the toc\r
+               /// </summary>\r
+               /// <returns>Returns the number of topics in the toc</returns>\r
+               public int Count()\r
+               {\r
+                       if(_toc!=null)\r
+                               return _toc.Count;\r
+                       else\r
+                               return 0;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges the <c>arrToC</c> list to the one in this instance\r
+               /// </summary>\r
+               /// <param name="arrToC">the toc list which should be merged with the current one</param>\r
+               internal void MergeToC( ArrayList arrToC )\r
+               {\r
+                       if(_toc==null)\r
+                               _toc = new ArrayList();\r
+\r
+                       MergeToC(_toc, arrToC, null);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Merges the <c>arrToC</c> list to the one in this instance (called if merged files\r
+               /// were found in a CHM)\r
+               /// </summary>\r
+               /// <param name="arrToC">the toc list which should be merged with the current one</param>\r
+               /// <param name="openFiles">An arraylist of CHMFile instances.</param>\r
+               internal void MergeToC( ArrayList arrToC, ArrayList openFiles )\r
+               {\r
+                       if(_toc==null)\r
+                               _toc = new ArrayList();\r
+                       MergeToC(_toc, arrToC, openFiles);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal method for recursive toc merging\r
+               /// </summary>\r
+               /// <param name="globalLevel">level of global toc</param>\r
+               /// <param name="localLevel">level of local toc</param>\r
+               /// <param name="openFiles">An arraylist of CHMFile instances.</param>\r
+               private void MergeToC( ArrayList globalLevel, ArrayList localLevel, ArrayList openFiles )\r
+               {\r
+                       foreach( TOCItem curItem in localLevel)\r
+                       {\r
+                               // if it is a part of the merged-links, we have to do nothing, \r
+                               // because the method HtmlHelpSystem.RecalculateMergeLinks() has already\r
+                               // placed this item at its correct position.\r
+                               if(!IsMergedItem(curItem.Name, curItem.Local, openFiles))\r
+                               {\r
+                                       TOCItem globalItem = ContainsToC(globalLevel, curItem.Name);\r
+                                       if(globalItem == null)\r
+                                       {\r
+                                               // the global toc doesn't have a topic with this name\r
+                                               // so we need to add the complete toc node to the global toc\r
+\r
+                                               globalLevel.Add( curItem );\r
+                                       } \r
+                                       else \r
+                                       {\r
+                                               // the global toc contains the current topic\r
+                                               // advance to the next level\r
+\r
+                                               if( (globalItem.Local.Length <= 0) && (curItem.Local.Length > 0) )\r
+                                               {\r
+                                                       // set the associated url\r
+                                                       globalItem.Local = curItem.Local;\r
+                                                       globalItem.ChmFile = curItem.ChmFile;\r
+                                               }\r
+\r
+                                               MergeToC(globalItem.Children, curItem.Children);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if the item is part of the merged-links\r
+               /// </summary>\r
+               /// <param name="name">name of the topic</param>\r
+               /// <param name="local">local of the topic</param>\r
+               /// <param name="openFiles">An arraylist of CHMFile instances.</param>\r
+               /// <returns>Returns true if this item is part of the merged-links</returns>\r
+               private bool IsMergedItem(string name, string local, ArrayList openFiles)\r
+               {\r
+                       if(openFiles==null)\r
+                               return false;\r
+\r
+                       foreach(CHMFile curFile in openFiles)\r
+                       {\r
+                               foreach(TOCItem curItem in curFile.MergLinks)\r
+                                       if( (curItem.Name == name) && (curItem.Local == local) )\r
+                                               return true;\r
+                       }\r
+                       return false;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Checks if a topicname exists in a SINGLE toc level \r
+               /// </summary>\r
+               /// <param name="arrToC">toc list</param>\r
+               /// <param name="Topic">topic to search</param>\r
+               /// <returns>Returns the topic item if found, otherwise null</returns>\r
+               private TOCItem ContainsToC(ArrayList arrToC, string Topic)\r
+               {\r
+                       foreach(TOCItem curItem in arrToC)\r
+                       {\r
+                               if(curItem.Name == Topic)\r
+                                       return curItem;\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Searches the table of contents for a special topic\r
+               /// </summary>\r
+               /// <param name="topic">topic to search</param>\r
+               /// <returns>Returns an instance of TOCItem if found, otherwise null</returns>\r
+               public TOCItem SearchTopic(string topic)\r
+               {\r
+                       return SearchTopic(topic, _toc);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Internal recursive tree search\r
+               /// </summary>\r
+               /// <param name="topic">topic to search</param>\r
+               /// <param name="searchIn">tree level list to look in</param>\r
+               /// <returns>Returns an instance of TOCItem if found, otherwise null</returns>\r
+               private TOCItem SearchTopic(string topic, ArrayList searchIn)\r
+               {\r
+                       foreach(TOCItem curItem in searchIn)\r
+                       {\r
+                               if(curItem.Name.ToLower() == topic.ToLower() )\r
+                                       return curItem;\r
+\r
+                               if(curItem.Children.Count>0)\r
+                               {\r
+                                       TOCItem nf = SearchTopic(topic, curItem.Children);\r
+                                       if(nf != null)\r
+                                               return nf;\r
+                               }\r
+                       }\r
+\r
+                       return null;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/AssemblyInfo.cs b/irc/TechBot/Compression/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..dbacda1
--- /dev/null
@@ -0,0 +1,32 @@
+using System.Reflection;\r
+using System.Runtime.CompilerServices;\r
+\r
+// Information about this assembly is defined by the following\r
+// attributes.\r
+//\r
+// change them to the information which is associated with the assembly\r
+// you compile.\r
+\r
+[assembly: AssemblyTitle("")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("")]\r
+[assembly: AssemblyProduct("")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+\r
+// The assembly version has following format :\r
+//\r
+// Major.Minor.Build.Revision\r
+//\r
+// You can specify all values by your own or you can build default build and revision\r
+// numbers with the '*' character (the default):\r
+\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+// The following attributes specify the key for the sign of your assembly. See the\r
+// .NET Framework documentation for more information about signing.\r
+// This is not required, if you don't want signing let these attributes like they're.\r
+[assembly: AssemblyDelaySign(false)]\r
+[assembly: AssemblyKeyFile("")]\r
diff --git a/irc/TechBot/Compression/Checksums/Adler32.cs b/irc/TechBot/Compression/Checksums/Adler32.cs
new file mode 100644 (file)
index 0000000..220dcba
--- /dev/null
@@ -0,0 +1,200 @@
+// Adler32.cs - Computes Adler32 data checksum of a data stream\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Checksums \r
+{\r
+       \r
+       /// <summary>\r
+       /// Computes Adler32 checksum for a stream of data. An Adler32\r
+       /// checksum is not as reliable as a CRC32 checksum, but a lot faster to\r
+       /// compute.\r
+       /// \r
+       /// The specification for Adler32 may be found in RFC 1950.\r
+       /// ZLIB Compressed Data Format Specification version 3.3)\r
+       /// \r
+       /// \r
+       /// From that document:\r
+       /// \r
+       ///      "ADLER32 (Adler-32 checksum)\r
+       ///       This contains a checksum value of the uncompressed data\r
+       ///       (excluding any dictionary data) computed according to Adler-32\r
+       ///       algorithm. This algorithm is a 32-bit extension and improvement\r
+       ///       of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073\r
+       ///       standard.\r
+       /// \r
+       ///       Adler-32 is composed of two sums accumulated per byte: s1 is\r
+       ///       the sum of all bytes, s2 is the sum of all s1 values. Both sums\r
+       ///       are done modulo 65521. s1 is initialized to 1, s2 to zero.  The\r
+       ///       Adler-32 checksum is stored as s2*65536 + s1 in most-\r
+       ///       significant-byte first (network) order."\r
+       /// \r
+       ///  "8.2. The Adler-32 algorithm\r
+       /// \r
+       ///    The Adler-32 algorithm is much faster than the CRC32 algorithm yet\r
+       ///    still provides an extremely low probability of undetected errors.\r
+       /// \r
+       ///    The modulo on unsigned long accumulators can be delayed for 5552\r
+       ///    bytes, so the modulo operation time is negligible.  If the bytes\r
+       ///    are a, b, c, the second sum is 3a + 2b + c + 3, and so is position\r
+       ///    and order sensitive, unlike the first sum, which is just a\r
+       ///    checksum.  That 65521 is prime is important to avoid a possible\r
+       ///    large class of two-byte errors that leave the check unchanged.\r
+       ///    (The Fletcher checksum uses 255, which is not prime and which also\r
+       ///    makes the Fletcher check insensitive to single byte changes 0 -\r
+       ///    255.)\r
+       /// \r
+       ///    The sum s1 is initialized to 1 instead of zero to make the length\r
+       ///    of the sequence part of s2, so that the length does not have to be\r
+       ///    checked separately. (Any sequence of zeroes has a Fletcher\r
+       ///    checksum of zero.)"\r
+       /// </summary>\r
+       /// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream"/>\r
+       /// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream"/>\r
+       public sealed class Adler32 : IChecksum\r
+       {\r
+               /// <summary>\r
+               /// largest prime smaller than 65536\r
+               /// </summary>\r
+               readonly static uint BASE = 65521;\r
+               \r
+               uint checksum;\r
+               \r
+               /// <summary>\r
+               /// Returns the Adler32 data checksum computed so far.\r
+               /// </summary>\r
+               public long Value {\r
+                       get {\r
+                               return checksum;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new instance of the <code>Adler32</code> class.\r
+               /// The checksum starts off with a value of 1.\r
+               /// </summary>\r
+               public Adler32()\r
+               {\r
+                       Reset();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Resets the Adler32 checksum to the initial value.\r
+               /// </summary>\r
+               public void Reset()\r
+               {\r
+                       checksum = 1; //Initialize to 1\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Updates the checksum with the byte b.\r
+               /// </summary>\r
+               /// <param name="bval">\r
+               /// the data value to add. The high byte of the int is ignored.\r
+               /// </param>\r
+               public void Update(int bval)\r
+               {\r
+                       //We could make a length 1 byte array and call update again, but I\r
+                       //would rather not have that overhead\r
+                       uint s1 = checksum & 0xFFFF;\r
+                       uint s2 = checksum >> 16;\r
+                       \r
+                       s1 = (s1 + ((uint)bval & 0xFF)) % BASE;\r
+                       s2 = (s1 + s2) % BASE;\r
+                       \r
+                       checksum = (s2 << 16) + s1;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Updates the checksum with the bytes taken from the array.\r
+               /// </summary>\r
+               /// <param name="buffer">\r
+               /// buffer an array of bytes\r
+               /// </param>\r
+               public void Update(byte[] buffer)\r
+               {\r
+                       Update(buffer, 0, buffer.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Updates the checksum with the bytes taken from the array.\r
+               /// </summary>\r
+               /// <param name="buf">\r
+               /// an array of bytes\r
+               /// </param>\r
+               /// <param name="off">\r
+               /// the start of the data used for this update\r
+               /// </param>\r
+               /// <param name="len">\r
+               /// the number of bytes to use for this update\r
+               /// </param>\r
+               public void Update(byte[] buf, int off, int len)\r
+               {\r
+                       if (buf == null) {\r
+                               throw new ArgumentNullException("buf");\r
+                       }\r
+                       \r
+                       if (off < 0 || len < 0 || off + len > buf.Length) {\r
+                               throw new ArgumentOutOfRangeException();\r
+                       }\r
+                       \r
+                       //(By Per Bothner)\r
+                       uint s1 = checksum & 0xFFFF;\r
+                       uint s2 = checksum >> 16;\r
+                       \r
+                       while (len > 0) {\r
+                               // We can defer the modulo operation:\r
+                               // s1 maximally grows from 65521 to 65521 + 255 * 3800\r
+                               // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31\r
+                               int n = 3800;\r
+                               if (n > len) {\r
+                                       n = len;\r
+                               }\r
+                               len -= n;\r
+                               while (--n >= 0) {\r
+                                       s1 = s1 + (uint)(buf[off++] & 0xFF);\r
+                                       s2 = s2 + s1;\r
+                               }\r
+                               s1 %= BASE;\r
+                               s2 %= BASE;\r
+                       }\r
+                       \r
+                       checksum = (s2 << 16) | s1;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Checksums/CRC32.cs b/irc/TechBot/Compression/Checksums/CRC32.cs
new file mode 100644 (file)
index 0000000..a78ad4c
--- /dev/null
@@ -0,0 +1,211 @@
+// CRC32.cs - Computes CRC32 data checksum of a data stream\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Checksums \r
+{\r
+       \r
+       /// <summary>\r
+       /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:\r
+       /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.\r
+       ///\r
+       /// Polynomials over GF(2) are represented in binary, one bit per coefficient,\r
+       /// with the lowest powers in the most significant bit.  Then adding polynomials\r
+       /// is just exclusive-or, and multiplying a polynomial by x is a right shift by\r
+       /// one.  If we call the above polynomial p, and represent a byte as the\r
+       /// polynomial q, also with the lowest power in the most significant bit (so the\r
+       /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,\r
+       /// where a mod b means the remainder after dividing a by b.\r
+       ///\r
+       /// This calculation is done using the shift-register method of multiplying and\r
+       /// taking the remainder.  The register is initialized to zero, and for each\r
+       /// incoming bit, x^32 is added mod p to the register if the bit is a one (where\r
+       /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by\r
+       /// x (which is shifting right by one and adding x^32 mod p if the bit shifted\r
+       /// out is a one).  We start with the highest power (least significant bit) of\r
+       /// q and repeat for all eight bits of q.\r
+       ///\r
+       /// The table is simply the CRC of all possible eight bit values.  This is all\r
+       /// the information needed to generate CRC's on data a byte at a time for all\r
+       /// combinations of CRC register values and incoming bytes.\r
+       /// </summary>\r
+       public sealed class Crc32 : IChecksum\r
+       {\r
+               readonly static uint CrcSeed = 0xFFFFFFFF;\r
+               \r
+               readonly static uint[] CrcTable = new uint[] {\r
+                       0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,\r
+                       0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,\r
+                       0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,\r
+                       0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,\r
+                       0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,\r
+                       0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,\r
+                       0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,\r
+                       0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,\r
+                       0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,\r
+                       0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,\r
+                       0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,\r
+                       0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,\r
+                       0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,\r
+                       0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,\r
+                       0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,\r
+                       0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,\r
+                       0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,\r
+                       0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,\r
+                       0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,\r
+                       0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,\r
+                       0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,\r
+                       0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,\r
+                       0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,\r
+                       0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,\r
+                       0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,\r
+                       0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,\r
+                       0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,\r
+                       0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,\r
+                       0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,\r
+                       0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,\r
+                       0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,\r
+                       0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,\r
+                       0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,\r
+                       0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,\r
+                       0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,\r
+                       0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,\r
+                       0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,\r
+                       0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,\r
+                       0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,\r
+                       0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,\r
+                       0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,\r
+                       0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,\r
+                       0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,\r
+                       0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,\r
+                       0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,\r
+                       0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,\r
+                       0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,\r
+                       0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\r
+                       0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,\r
+                       0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,\r
+                       0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,\r
+                       0x2D02EF8D\r
+               };\r
+               \r
+               internal static uint ComputeCrc32(uint oldCrc, byte bval)\r
+               {\r
+                       return (uint)(Crc32.CrcTable[(oldCrc ^ bval) & 0xFF] ^ (oldCrc >> 8));\r
+               }\r
+               \r
+               /// <summary>\r
+               /// The crc data checksum so far.\r
+               /// </summary>\r
+               uint crc = 0;\r
+               \r
+               /// <summary>\r
+               /// Returns the CRC32 data checksum computed so far.\r
+               /// </summary>\r
+               public long Value {\r
+                       get {\r
+                               return (long)crc;\r
+                       }\r
+                       set {\r
+                               crc = (uint)value;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Resets the CRC32 data checksum as if no update was ever called.\r
+               /// </summary>\r
+               public void Reset() \r
+               { \r
+                       crc = 0; \r
+               }\r
+               \r
+               /// <summary>\r
+               /// Updates the checksum with the int bval.\r
+               /// </summary>\r
+               /// <param name = "bval">\r
+               /// the byte is taken as the lower 8 bits of bval\r
+               /// </param>\r
+               public void Update(int bval)\r
+               {\r
+                       crc ^= CrcSeed;\r
+                       crc  = CrcTable[(crc ^ bval) & 0xFF] ^ (crc >> 8);\r
+                       crc ^= CrcSeed;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Updates the checksum with the bytes taken from the array.\r
+               /// </summary>\r
+               /// <param name="buffer">\r
+               /// buffer an array of bytes\r
+               /// </param>\r
+               public void Update(byte[] buffer)\r
+               {\r
+                       Update(buffer, 0, buffer.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Adds the byte array to the data checksum.\r
+               /// </summary>\r
+               /// <param name = "buf">\r
+               /// the buffer which contains the data\r
+               /// </param>\r
+               /// <param name = "off">\r
+               /// the offset in the buffer where the data starts\r
+               /// </param>\r
+               /// <param name = "len">\r
+               /// the length of the data\r
+               /// </param>\r
+               public void Update(byte[] buf, int off, int len)\r
+               {\r
+                       if (buf == null) {\r
+                               throw new ArgumentNullException("buf");\r
+                       }\r
+                       \r
+                       if (off < 0 || len < 0 || off + len > buf.Length) {\r
+                               throw new ArgumentOutOfRangeException();\r
+                       }\r
+                       \r
+                       crc ^= CrcSeed;\r
+                       \r
+                       while (--len >= 0) {\r
+                               crc = CrcTable[(crc ^ buf[off++]) & 0xFF] ^ (crc >> 8);\r
+                       }\r
+                       \r
+                       crc ^= CrcSeed;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Checksums/IChecksum.cs b/irc/TechBot/Compression/Checksums/IChecksum.cs
new file mode 100644 (file)
index 0000000..98385b3
--- /dev/null
@@ -0,0 +1,93 @@
+// IChecksum.cs - Interface to compute a data checksum\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+namespace ICSharpCode.SharpZipLib.Checksums \r
+{\r
+       \r
+       /// <summary>\r
+       /// Interface to compute a data checksum used by checked input/output streams.\r
+       /// A data checksum can be updated by one byte or with a byte array. After each\r
+       /// update the value of the current checksum can be returned by calling\r
+       /// <code>getValue</code>. The complete checksum object can also be reset\r
+       /// so it can be used again with new data.\r
+       /// </summary>\r
+       public interface IChecksum\r
+       {\r
+               /// <summary>\r
+               /// Returns the data checksum computed so far.\r
+               /// </summary>\r
+               long Value \r
+               {\r
+                       get;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Resets the data checksum as if no update was ever called.\r
+               /// </summary>\r
+               void Reset();\r
+               \r
+               /// <summary>\r
+               /// Adds one byte to the data checksum.\r
+               /// </summary>\r
+               /// <param name = "bval">\r
+               /// the data value to add. The high byte of the int is ignored.\r
+               /// </param>\r
+               void Update(int bval);\r
+               \r
+               /// <summary>\r
+               /// Updates the data checksum with the bytes taken from the array.\r
+               /// </summary>\r
+               /// <param name="buffer">\r
+               /// buffer an array of bytes\r
+               /// </param>\r
+               void Update(byte[] buffer);\r
+               \r
+               /// <summary>\r
+               /// Adds the byte array to the data checksum.\r
+               /// </summary>\r
+               /// <param name = "buf">\r
+               /// the buffer which contains the data\r
+               /// </param>\r
+               /// <param name = "off">\r
+               /// the offset in the buffer where the data starts\r
+               /// </param>\r
+               /// <param name = "len">\r
+               /// the length of the data\r
+               /// </param>\r
+               void Update(byte[] buf, int off, int len);\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Checksums/StrangeCRC.cs b/irc/TechBot/Compression/Checksums/StrangeCRC.cs
new file mode 100644 (file)
index 0000000..a2dc10f
--- /dev/null
@@ -0,0 +1,159 @@
+// StrangeCRC.cs - computes a crc used in the bziplib ... I don't think that\r
+//                 this is the 'standard' crc, please correct me, if I'm wrong\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Checksums \r
+{\r
+       \r
+       public class StrangeCRC : IChecksum\r
+       {\r
+               readonly static uint[] crc32Table = {\r
+                       0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,\r
+                       0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,\r
+                       0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,\r
+                       0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,\r
+                       0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,\r
+                       0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,\r
+                       0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,\r
+                       0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,\r
+                       0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,\r
+                       0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,\r
+                       0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,\r
+                       0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,\r
+                       0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,\r
+                       0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,\r
+                       0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,\r
+                       0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,\r
+                       0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,\r
+                       0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,\r
+                       0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,\r
+                       0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,\r
+                       0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,\r
+                       0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,\r
+                       0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,\r
+                       0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,\r
+                       0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,\r
+                       0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,\r
+                       0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,\r
+                       0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,\r
+                       0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,\r
+                       0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,\r
+                       0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,\r
+                       0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,\r
+                       0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,\r
+                       0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,\r
+                       0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,\r
+                       0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,\r
+                       0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,\r
+                       0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,\r
+                       0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,\r
+                       0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,\r
+                       0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,\r
+                       0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,\r
+                       0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,\r
+                       0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,\r
+                       0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,\r
+                       0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,\r
+                       0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,\r
+                       0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,\r
+                       0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,\r
+                       0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,\r
+                       0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,\r
+                       0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,\r
+                       0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,\r
+                       0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,\r
+                       0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,\r
+                       0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,\r
+                       0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,\r
+                       0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,\r
+                       0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,\r
+                       0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,\r
+                       0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,\r
+                       0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,\r
+                       0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,\r
+                       0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4\r
+               };\r
+               int globalCrc;\r
+       \r
+               public StrangeCRC() \r
+               {\r
+                       Reset();\r
+               }\r
+       \r
+               public void Reset()\r
+               {\r
+                       globalCrc = -1;\r
+               }\r
+       \r
+               public long Value {\r
+                       get {\r
+                               return ~globalCrc;\r
+                       }\r
+               }\r
+               \r
+               public void Update(int inCh)\r
+               {\r
+                       int temp = (globalCrc >> 24) ^ inCh;\r
+                       if (temp < 0) {\r
+                               temp = 256 + temp;\r
+                       }\r
+                       globalCrc = (int)((globalCrc << 8) ^ crc32Table[temp]);\r
+               }\r
+               \r
+               public void Update(byte[] buf)\r
+               {\r
+                       Update(buf, 0, buf.Length);\r
+               }\r
+               \r
+               public void Update(byte[] buf, int off, int len)\r
+               {\r
+                       if (buf == null) {\r
+                               throw new ArgumentNullException("buf");\r
+                       }\r
+                       \r
+                       if (off < 0 || len < 0 || off + len > buf.Length) {\r
+                               throw new ArgumentOutOfRangeException();\r
+                       }\r
+                       \r
+                       for (int i = 0; i < len; ++i) {\r
+                               Update(buf[off++]);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Compression.cmbx b/irc/TechBot/Compression/Compression.cmbx
new file mode 100644 (file)
index 0000000..28d8e0c
--- /dev/null
@@ -0,0 +1,16 @@
+<Combine fileversion="1.0" name="Compression" description="">\r
+  <StartMode startupentry="Compression" single="True">\r
+    <Execute entry="Compression" type="None" />\r
+  </StartMode>\r
+  <Entries>\r
+    <Entry filename=".\.\Compression.prjx" />\r
+  </Entries>\r
+  <Configurations active="Debug">\r
+    <Configuration name="Release">\r
+      <Entry name="Compression" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+    <Configuration name="Debug">\r
+      <Entry name="Compression" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/Compression/Compression.prjx b/irc/TechBot/Compression/Compression.prjx
new file mode 100644 (file)
index 0000000..95316d1
--- /dev/null
@@ -0,0 +1,45 @@
+<Project name="Compression" standardNamespace="Compression" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">\r
+  <Contents>\r
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\ZipException.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Deflater.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\DeflaterConstants.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\DeflaterEngine.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\DeflaterHuffman.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\DeflaterPending.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Inflater.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\InflaterDynHeader.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\InflaterHuffmanTree.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\PendingBuffer.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Checksums" subtype="Directory" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Checksums\StrangeCRC.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Checksums\Adler32.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Checksums\CRC32.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Checksums\IChecksum.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Streams" subtype="Directory" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Streams\StreamManipulator.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Streams\DeflaterOutputStream.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Streams\InflaterInputStream.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Streams\OutputWindow.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+  </Contents>\r
+  <References />\r
+  <DeploymentInformation target="" script="" strategy="File" />\r
+  <Configuration runwithwarnings="True" name="Debug">\r
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+    <Execution commandlineparameters="" consolepause="False" />\r
+    <Output directory="..\bin\Debug" assembly="Compression" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+  </Configuration>\r
+  <Configurations active="Debug">\r
+    <Configuration runwithwarnings="True" name="Debug">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Debug" assembly="Compression" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+    <Configuration runwithwarnings="True" name="Release">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Release" assembly="Compression" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Project>
\ No newline at end of file
diff --git a/irc/TechBot/Compression/Default.build b/irc/TechBot/Compression/Default.build
new file mode 100644 (file)
index 0000000..5a229e2
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>\r
+<project name="Compression" default="build">\r
+\r
+       <property name="output.dir" value="..\bin" />\r
+\r
+       <target name="build" description="Build component">\r
+               <mkdir dir="${output.dir}" />\r
+               <csc target="library"\r
+                       output="${output.dir}\Compression.dll"\r
+                       optimize="true"\r
+                       debug="true"\r
+                       doc="${output.dir}\Compression.xml"\r
+                       warninglevel="0">\r
+                       <sources>\r
+                               <include name="**/*.cs" />\r
+                       </sources>\r
+               </csc>\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/Compression/Deflater.cs b/irc/TechBot/Compression/Deflater.cs
new file mode 100644 (file)
index 0000000..bb8d4cc
--- /dev/null
@@ -0,0 +1,542 @@
+// Deflater.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       /// <summary>\r
+       /// This is the Deflater class.  The deflater class compresses input\r
+       /// with the deflate algorithm described in RFC 1951.  It has several\r
+       /// compression levels and three different strategies described below.\r
+       ///\r
+       /// This class is <i>not</i> thread safe.  This is inherent in the API, due\r
+       /// to the split of deflate and setInput.\r
+       /// \r
+       /// author of the original java version : Jochen Hoenicke\r
+       /// </summary>\r
+       public class Deflater\r
+       {\r
+               /// <summary>\r
+               /// The best and slowest compression level.  This tries to find very\r
+               /// long and distant string repetitions.\r
+               /// </summary>\r
+               public static  int BEST_COMPRESSION = 9;\r
+               \r
+               /// <summary>\r
+               /// The worst but fastest compression level.\r
+               /// </summary>\r
+               public static  int BEST_SPEED = 1;\r
+               \r
+               /// <summary>\r
+               /// The default compression level.\r
+               /// </summary>\r
+               public static  int DEFAULT_COMPRESSION = -1;\r
+               \r
+               /// <summary>\r
+               /// This level won't compress at all but output uncompressed blocks.\r
+               /// </summary>\r
+               public static  int NO_COMPRESSION = 0;\r
+                               \r
+               /// <summary>\r
+               /// The compression method.  This is the only method supported so far.\r
+               /// There is no need to use this constant at all.\r
+               /// </summary>\r
+               public static  int DEFLATED = 8;\r
+               \r
+               /*\r
+               * The Deflater can do the following state transitions:\r
+                       *\r
+                       * (1) -> INIT_STATE   ----> INIT_FINISHING_STATE ---.\r
+                       *        /  | (2)      (5)                         |\r
+                       *       /   v          (5)                         |\r
+                       *   (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)\r
+                       *       \   | (3)                 |        ,-------'\r
+                       *        |  |                     | (3)   /\r
+                       *        v  v          (5)        v      v\r
+                       * (1) -> BUSY_STATE   ----> FINISHING_STATE\r
+                       *                                | (6)\r
+                       *                                v\r
+                       *                           FINISHED_STATE\r
+                       *    \_____________________________________/\r
+                       *          | (7)\r
+                       *          v\r
+                       *        CLOSED_STATE\r
+                       *\r
+                       * (1) If we should produce a header we start in INIT_STATE, otherwise\r
+                       *     we start in BUSY_STATE.\r
+                       * (2) A dictionary may be set only when we are in INIT_STATE, then\r
+                       *     we change the state as indicated.\r
+                       * (3) Whether a dictionary is set or not, on the first call of deflate\r
+                       *     we change to BUSY_STATE.\r
+                       * (4) -- intentionally left blank -- :)\r
+                       * (5) FINISHING_STATE is entered, when flush() is called to indicate that\r
+                       *     there is no more INPUT.  There are also states indicating, that\r
+                       *     the header wasn't written yet.\r
+                       * (6) FINISHED_STATE is entered, when everything has been flushed to the\r
+                       *     internal pending output buffer.\r
+                       * (7) At any time (7)\r
+                       *\r
+                       */\r
+                       \r
+               private static  int IS_SETDICT              = 0x01;\r
+               private static  int IS_FLUSHING             = 0x04;\r
+               private static  int IS_FINISHING            = 0x08;\r
+               \r
+               private static  int INIT_STATE              = 0x00;\r
+               private static  int SETDICT_STATE           = 0x01;\r
+               //              private static  int INIT_FINISHING_STATE    = 0x08;\r
+               //              private static  int SETDICT_FINISHING_STATE = 0x09;\r
+               private static  int BUSY_STATE              = 0x10;\r
+               private static  int FLUSHING_STATE          = 0x14;\r
+               private static  int FINISHING_STATE         = 0x1c;\r
+               private static  int FINISHED_STATE          = 0x1e;\r
+               private static  int CLOSED_STATE            = 0x7f;\r
+               \r
+               /// <summary>\r
+               /// Compression level.\r
+               /// </summary>\r
+               private int level;\r
+               \r
+               /// <summary>\r
+               /// should we include a header.\r
+               /// </summary>\r
+               private bool noHeader;\r
+               \r
+               //              /// <summary>\r
+               //              /// Compression strategy.\r
+               //              /// </summary>\r
+               //              private int strategy;\r
+               \r
+               /// <summary>\r
+               /// The current state.\r
+               /// </summary>\r
+               private int state;\r
+               \r
+               /// <summary>\r
+               /// The total bytes of output written.\r
+               /// </summary>\r
+               private int totalOut;\r
+               \r
+               /// <summary>\r
+               /// The pending output.\r
+               /// </summary>\r
+               private DeflaterPending pending;\r
+               \r
+               /// <summary>\r
+               /// The deflater engine.\r
+               /// </summary>\r
+               private DeflaterEngine engine;\r
+               \r
+               /// <summary>\r
+               /// Creates a new deflater with default compression level.\r
+               /// </summary>\r
+               public Deflater() : this(DEFAULT_COMPRESSION, false)\r
+               {\r
+                       \r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new deflater with given compression level.\r
+               /// </summary>\r
+               /// <param name="lvl">\r
+               /// the compression level, a value between NO_COMPRESSION\r
+               /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION.\r
+               /// </param>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>\r
+               public Deflater(int lvl) : this(lvl, false)\r
+               {\r
+                       \r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new deflater with given compression level.\r
+               /// </summary>\r
+               /// <param name="lvl">\r
+               /// the compression level, a value between NO_COMPRESSION\r
+               /// and BEST_COMPRESSION.\r
+               /// </param>\r
+               /// <param name="nowrap">\r
+               /// true, if we should suppress the deflate header at the\r
+               /// beginning and the adler checksum at the end of the output.  This is\r
+               /// useful for the GZIP format.\r
+               /// </param>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>\r
+               public Deflater(int lvl, bool nowrap)\r
+               {\r
+                       if (lvl == DEFAULT_COMPRESSION) {\r
+                               lvl = 6;\r
+                       } else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) {\r
+                               throw new ArgumentOutOfRangeException("lvl");\r
+                       }\r
+                       \r
+                       pending = new DeflaterPending();\r
+                       engine = new DeflaterEngine(pending);\r
+                       this.noHeader = nowrap;\r
+                       SetStrategy(DeflateStrategy.Default);\r
+                       SetLevel(lvl);\r
+                       Reset();\r
+               }\r
+               \r
+               \r
+               /// <summary>\r
+               /// Resets the deflater.  The deflater acts afterwards as if it was\r
+               /// just created with the same compression level and strategy as it\r
+               /// had before.\r
+               /// </summary>\r
+               public void Reset()\r
+               {\r
+                       state = (noHeader ? BUSY_STATE : INIT_STATE);\r
+                       totalOut = 0;\r
+                       pending.Reset();\r
+                       engine.Reset();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the current adler checksum of the data that was processed so far.\r
+               /// </summary>\r
+               public int Adler {\r
+                       get {\r
+                               return engine.Adler;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the number of input bytes processed so far.\r
+               /// </summary>\r
+               public int TotalIn {\r
+                       get {\r
+                               return engine.TotalIn;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the number of output bytes so far.\r
+               /// </summary>\r
+               public int TotalOut {\r
+                       get {\r
+                               return totalOut;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Flushes the current input block.  Further calls to deflate() will\r
+               /// produce enough output to inflate everything in the current input\r
+               /// block.  This is not part of Sun's JDK so I have made it package\r
+               /// private.  It is used by DeflaterOutputStream to implement\r
+               /// flush().\r
+               /// </summary>\r
+               public void Flush() \r
+               {\r
+                       state |= IS_FLUSHING;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Finishes the deflater with the current input block.  It is an error\r
+               /// to give more input after this method was called.  This method must\r
+               /// be called to force all bytes to be flushed.\r
+               /// </summary>\r
+               public void Finish() \r
+               {\r
+                       state |= IS_FLUSHING | IS_FINISHING;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Returns true if the stream was finished and no more output bytes\r
+               /// are available.\r
+               /// </summary>\r
+               public bool IsFinished {\r
+                       get {\r
+                               return state == FINISHED_STATE && pending.IsFlushed;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Returns true, if the input buffer is empty.\r
+               /// You should then call setInput(). \r
+               /// NOTE: This method can also return true when the stream\r
+               /// was finished.\r
+               /// </summary>\r
+               public bool IsNeedingInput {\r
+                       get {\r
+                               return engine.NeedsInput();\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the data which should be compressed next.  This should be only\r
+               /// called when needsInput indicates that more input is needed.\r
+               /// If you call setInput when needsInput() returns false, the\r
+               /// previous input that is still pending will be thrown away.\r
+               /// The given byte array should not be changed, before needsInput() returns\r
+               /// true again.\r
+               /// This call is equivalent to <code>setInput(input, 0, input.length)</code>.\r
+               /// </summary>\r
+               /// <param name="input">\r
+               /// the buffer containing the input data.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if the buffer was finished() or ended().\r
+               /// </exception>\r
+               public void SetInput(byte[] input)\r
+               {\r
+                       SetInput(input, 0, input.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the data which should be compressed next.  This should be\r
+               /// only called when needsInput indicates that more input is needed.\r
+               /// The given byte array should not be changed, before needsInput() returns\r
+               /// true again.\r
+               /// </summary>\r
+               /// <param name="input">\r
+               /// the buffer containing the input data.\r
+               /// </param>\r
+               /// <param name="off">\r
+               /// the start of the data.\r
+               /// </param>\r
+               /// <param name="len">\r
+               /// the length of the data.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if the buffer was finished() or ended() or if previous input is still pending.\r
+               /// </exception>\r
+               public void SetInput(byte[] input, int off, int len)\r
+               {\r
+                       if ((state & IS_FINISHING) != 0) {\r
+                               throw new InvalidOperationException("finish()/end() already called");\r
+                       }\r
+                       engine.SetInput(input, off, len);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the compression level.  There is no guarantee of the exact\r
+               /// position of the change, but if you call this when needsInput is\r
+               /// true the change of compression level will occur somewhere near\r
+               /// before the end of the so far given input.\r
+               /// </summary>\r
+               /// <param name="lvl">\r
+               /// the new compression level.\r
+               /// </param>\r
+               public void SetLevel(int lvl)\r
+               {\r
+                       if (lvl == DEFAULT_COMPRESSION) {\r
+                               lvl = 6;\r
+                       } else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) {\r
+                               throw new ArgumentOutOfRangeException("lvl");\r
+                       }\r
+                       \r
+                       if (level != lvl) {\r
+                               level = lvl;\r
+                               engine.SetLevel(lvl);\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the compression strategy. Strategy is one of\r
+               /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED.  For the exact\r
+               /// position where the strategy is changed, the same as for\r
+               /// setLevel() applies.\r
+               /// </summary>\r
+               /// <param name="stgy">\r
+               /// the new compression strategy.\r
+               /// </param>\r
+               public void SetStrategy(DeflateStrategy stgy)\r
+               {\r
+                       engine.Strategy = stgy;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Deflates the current input block to the given array.  It returns\r
+               /// the number of bytes compressed, or 0 if either\r
+               /// needsInput() or finished() returns true or length is zero.\r
+               /// </summary>\r
+               /// <param name="output">\r
+               /// the buffer where to write the compressed data.\r
+               /// </param>\r
+               public int Deflate(byte[] output)\r
+               {\r
+                       return Deflate(output, 0, output.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Deflates the current input block to the given array.  It returns\r
+               /// the number of bytes compressed, or 0 if either\r
+               /// needsInput() or finished() returns true or length is zero.\r
+               /// </summary>\r
+               /// <param name="output">\r
+               /// the buffer where to write the compressed data.\r
+               /// </param>\r
+               /// <param name="offset">\r
+               /// the offset into the output array.\r
+               /// </param>\r
+               /// <param name="length">\r
+               /// the maximum number of bytes that may be written.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if end() was called.\r
+               /// </exception>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">\r
+               /// if offset and/or length don't match the array length.\r
+               /// </exception>\r
+               public int Deflate(byte[] output, int offset, int length)\r
+               {\r
+                       int origLength = length;\r
+                       \r
+                       if (state == CLOSED_STATE) {\r
+                               throw new InvalidOperationException("Deflater closed");\r
+                       }\r
+                       \r
+                       if (state < BUSY_STATE) {\r
+                               /* output header */\r
+                               int header = (DEFLATED +\r
+                                       ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;\r
+                               int level_flags = (level - 1) >> 1;\r
+                               if (level_flags < 0 || level_flags > 3) {\r
+                                       level_flags = 3;\r
+                               }\r
+                               header |= level_flags << 6;\r
+                               if ((state & IS_SETDICT) != 0) {\r
+                                       /* Dictionary was set */\r
+                                       header |= DeflaterConstants.PRESET_DICT;\r
+                               }\r
+                               header += 31 - (header % 31);\r
+                               \r
+                               \r
+                               pending.WriteShortMSB(header);\r
+                               if ((state & IS_SETDICT) != 0) {\r
+                                       int chksum = engine.Adler;\r
+                                       engine.ResetAdler();\r
+                                       pending.WriteShortMSB(chksum >> 16);\r
+                                       pending.WriteShortMSB(chksum & 0xffff);\r
+                               }\r
+                               \r
+                               state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));\r
+                       }\r
+                       \r
+                       for (;;) {\r
+                               int count = pending.Flush(output, offset, length);\r
+                               offset   += count;\r
+                               totalOut += count;\r
+                               length   -= count;\r
+                               \r
+                               if (length == 0 || state == FINISHED_STATE) {\r
+                                       break;\r
+                               }\r
+                               \r
+                               if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) {\r
+                                       if (state == BUSY_STATE) {\r
+                                               /* We need more input now */\r
+                                               return origLength - length;\r
+                                       } else if (state == FLUSHING_STATE) {\r
+                                               if (level != NO_COMPRESSION) {\r
+                                                       /* We have to supply some lookahead.  8 bit lookahead\r
+                                                        * are needed by the zlib inflater, and we must fill\r
+                                                        * the next byte, so that all bits are flushed.\r
+                                                        */\r
+                                                       int neededbits = 8 + ((-pending.BitCount) & 7);\r
+                                                       while (neededbits > 0) {\r
+                                                               /* write a static tree block consisting solely of\r
+                                                                * an EOF:\r
+                                                                */\r
+                                                               pending.WriteBits(2, 10);\r
+                                                               neededbits -= 10;\r
+                                                       }\r
+                                               }\r
+                                               state = BUSY_STATE;\r
+                                       } else if (state == FINISHING_STATE) {\r
+                                               pending.AlignToByte();\r
+                                               /* We have completed the stream */\r
+                                               if (!noHeader) {\r
+                                                       int adler = engine.Adler;\r
+                                                       pending.WriteShortMSB(adler >> 16);\r
+                                                       pending.WriteShortMSB(adler & 0xffff);\r
+                                               }\r
+                                               state = FINISHED_STATE;\r
+                                       }\r
+                               }\r
+                       }\r
+                       return origLength - length;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the dictionary which should be used in the deflate process.\r
+               /// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>.\r
+               /// </summary>\r
+               /// <param name="dict">\r
+               /// the dictionary.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if setInput () or deflate () were already called or another dictionary was already set.\r
+               /// </exception>\r
+               public void SetDictionary(byte[] dict)\r
+               {\r
+                       SetDictionary(dict, 0, dict.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the dictionary which should be used in the deflate process.\r
+               /// The dictionary should be a byte array containing strings that are\r
+               /// likely to occur in the data which should be compressed.  The\r
+               /// dictionary is not stored in the compressed output, only a\r
+               /// checksum.  To decompress the output you need to supply the same\r
+               /// dictionary again.\r
+               /// </summary>\r
+               /// <param name="dict">\r
+               /// the dictionary.\r
+               /// </param>\r
+               /// <param name="offset">\r
+               /// an offset into the dictionary.\r
+               /// </param>\r
+               /// <param name="length">\r
+               /// the length of the dictionary.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if setInput () or deflate () were already called or another dictionary was already set.\r
+               /// </exception>\r
+               public void SetDictionary(byte[] dict, int offset, int length)\r
+               {\r
+                       if (state != INIT_STATE) {\r
+                               throw new InvalidOperationException();\r
+                       }\r
+                       \r
+                       state = SETDICT_STATE;\r
+                       engine.SetDictionary(dict, offset, length);\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/DeflaterConstants.cs b/irc/TechBot/Compression/DeflaterConstants.cs
new file mode 100644 (file)
index 0000000..8e2be6e
--- /dev/null
@@ -0,0 +1,85 @@
+// DeflaterConstants.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       /// <summary>\r
+       /// This class contains constants used for the deflater.\r
+       /// </summary>\r
+       public class DeflaterConstants \r
+       {\r
+               public const bool DEBUGGING = false;\r
+               \r
+               public const int STORED_BLOCK = 0;\r
+               public const int STATIC_TREES = 1;\r
+               public const int DYN_TREES    = 2;\r
+               public const int PRESET_DICT  = 0x20;\r
+               \r
+               public const int DEFAULT_MEM_LEVEL = 8;\r
+               \r
+               public const int MAX_MATCH = 258;\r
+               public const int MIN_MATCH = 3;\r
+               \r
+               public const int MAX_WBITS = 15;\r
+               public const int WSIZE = 1 << MAX_WBITS;\r
+               public const int WMASK = WSIZE - 1;\r
+               \r
+               public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7;\r
+               public const int HASH_SIZE = 1 << HASH_BITS;\r
+               public const int HASH_MASK = HASH_SIZE - 1;\r
+               public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;\r
+               \r
+               public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;\r
+               public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD;\r
+               \r
+               public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);\r
+               public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE-5);\r
+               \r
+               public const int DEFLATE_STORED = 0;\r
+               public const int DEFLATE_FAST   = 1;\r
+               public const int DEFLATE_SLOW   = 2;\r
+               \r
+               public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8,  8,  8,  32,  32 };\r
+               public static int[] MAX_LAZY    = { 0, 4, 5, 6, 4,16, 16, 32, 128, 258 };\r
+               public static int[] NICE_LENGTH = { 0, 8,16,32,16,32,128,128, 258, 258 };\r
+               public static int[] MAX_CHAIN   = { 0, 4, 8,32,16,32,128,256,1024,4096 };\r
+               public static int[] COMPR_FUNC  = { 0, 1, 1, 1, 1, 2,  2,  2,   2,   2 };\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/DeflaterEngine.cs b/irc/TechBot/Compression/DeflaterEngine.cs
new file mode 100644 (file)
index 0000000..c543f85
--- /dev/null
@@ -0,0 +1,653 @@
+// DeflaterEngine.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+using ICSharpCode.SharpZipLib.Checksums;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       public enum DeflateStrategy \r
+       {\r
+               // The default strategy.\r
+               Default  = 0,\r
+               \r
+               // This strategy will only allow longer string repetitions.  It is\r
+               // useful for random data with a small character set.\r
+               Filtered = 1,\r
+               \r
+               // This strategy will not look for string repetitions at all.  It\r
+               // only encodes with Huffman trees (which means, that more common\r
+               // characters get a smaller encoding.\r
+               HuffmanOnly = 2\r
+       }\r
+       \r
+       public class DeflaterEngine : DeflaterConstants \r
+       {\r
+               static int TOO_FAR = 4096;\r
+               \r
+               int ins_h;\r
+               //              private byte[] buffer;\r
+               short[] head;\r
+               short[] prev;\r
+               \r
+               int    matchStart, matchLen;\r
+               bool   prevAvailable;\r
+               int    blockStart;\r
+               int    strstart, lookahead;\r
+               byte[] window;\r
+               \r
+               DeflateStrategy strategy;\r
+               int max_chain, max_lazy, niceLength, goodLength;\r
+               \r
+               /// <summary>\r
+               /// The current compression function.\r
+               /// </summary>\r
+               int comprFunc;\r
+               \r
+               /// <summary>\r
+               /// The input data for compression.\r
+               /// </summary>\r
+               byte[] inputBuf;\r
+               \r
+               /// <summary>\r
+               /// The total bytes of input read.\r
+               /// </summary>\r
+               int totalIn;\r
+               \r
+               /// <summary>\r
+               /// The offset into inputBuf, where input data starts.\r
+               /// </summary>\r
+               int inputOff;\r
+               \r
+               /// <summary>\r
+               /// The end offset of the input data.\r
+               /// </summary>\r
+               int inputEnd;\r
+               \r
+               DeflaterPending pending;\r
+               DeflaterHuffman huffman;\r
+               \r
+               /// <summary>\r
+               /// The adler checksum\r
+               /// </summary>\r
+               Adler32 adler;\r
+               \r
+               public DeflaterEngine(DeflaterPending pending) \r
+               {\r
+                       this.pending = pending;\r
+                       huffman = new DeflaterHuffman(pending);\r
+                       adler = new Adler32();\r
+                       \r
+                       window = new byte[2 * WSIZE];\r
+                       head   = new short[HASH_SIZE];\r
+                       prev   = new short[WSIZE];\r
+                       \r
+                       /* We start at index 1, to avoid a implementation deficiency, that\r
+                       * we cannot build a repeat pattern at index 0.\r
+                       */\r
+                       blockStart = strstart = 1;\r
+               }\r
+               \r
+               public void Reset()\r
+               {\r
+                       huffman.Reset();\r
+                       adler.Reset();\r
+                       blockStart = strstart = 1;\r
+                       lookahead = 0;\r
+                       totalIn   = 0;\r
+                       prevAvailable = false;\r
+                       matchLen = MIN_MATCH - 1;\r
+                       \r
+                       for (int i = 0; i < HASH_SIZE; i++) {\r
+                               head[i] = 0;\r
+                       }\r
+                       \r
+                       for (int i = 0; i < WSIZE; i++) {\r
+                               prev[i] = 0;\r
+                       }\r
+               }\r
+               \r
+               public void ResetAdler()\r
+               {\r
+                       adler.Reset();\r
+               }\r
+               \r
+               public int Adler {\r
+                       get {\r
+                               return (int)adler.Value;\r
+                       }\r
+               }\r
+               \r
+               public int TotalIn {\r
+                       get {\r
+                               return totalIn;\r
+                       }\r
+               }\r
+               \r
+               public DeflateStrategy Strategy {\r
+                       get {\r
+                               return strategy;\r
+                       }\r
+                       set {\r
+                               strategy = value;\r
+                       }\r
+               }\r
+               \r
+               public void SetLevel(int lvl)\r
+               {\r
+                       goodLength = DeflaterConstants.GOOD_LENGTH[lvl];\r
+                       max_lazy   = DeflaterConstants.MAX_LAZY[lvl];\r
+                       niceLength = DeflaterConstants.NICE_LENGTH[lvl];\r
+                       max_chain  = DeflaterConstants.MAX_CHAIN[lvl];\r
+                       \r
+                       if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) {\r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("Change from "+comprFunc +" to "\r
+                               //                                                        + DeflaterConstants.COMPR_FUNC[lvl]);\r
+                               //                              }\r
+                               switch (comprFunc) {\r
+                                       case DEFLATE_STORED:\r
+                                               if (strstart > blockStart) {\r
+                                                       huffman.FlushStoredBlock(window, blockStart,\r
+                                                               strstart - blockStart, false);\r
+                                                       blockStart = strstart;\r
+                                               }\r
+                                               UpdateHash();\r
+                                               break;\r
+                                       case DEFLATE_FAST:\r
+                                               if (strstart > blockStart) {\r
+                                                       huffman.FlushBlock(window, blockStart, strstart - blockStart,\r
+                                                               false);\r
+                                                       blockStart = strstart;\r
+                                               }\r
+                                               break;\r
+                                       case DEFLATE_SLOW:\r
+                                               if (prevAvailable) {\r
+                                                       huffman.TallyLit(window[strstart-1] & 0xff);\r
+                                               }\r
+                                               if (strstart > blockStart) {\r
+                                                       huffman.FlushBlock(window, blockStart, strstart - blockStart, false);\r
+                                                       blockStart = strstart;\r
+                                               }\r
+                                               prevAvailable = false;\r
+                                               matchLen = MIN_MATCH - 1;\r
+                                               break;\r
+                               }\r
+                               comprFunc = COMPR_FUNC[lvl];\r
+                       }\r
+               }\r
+               \r
+               void UpdateHash() \r
+               {\r
+                       //                      if (DEBUGGING) {\r
+                       //                              //Console.WriteLine("updateHash: "+strstart);\r
+                       //                      }\r
+                       ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];\r
+               }\r
+               \r
+               int InsertString() \r
+               {\r
+                       short match;\r
+                       int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) & HASH_MASK;\r
+                       \r
+                       //                      if (DEBUGGING) {\r
+                       //                              if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^ \r
+                       //                                            (window[strstart + 1] << HASH_SHIFT) ^ \r
+                       //                                            (window[strstart + 2])) & HASH_MASK)) {\r
+                       //                                              throw new Exception("hash inconsistent: "+hash+"/"\r
+                       //                                                                  +window[strstart]+","\r
+                       //                                                                  +window[strstart+1]+","\r
+                       //                                                                  +window[strstart+2]+","+HASH_SHIFT);\r
+                       //                                      }\r
+                       //                      }\r
+                       \r
+                       prev[strstart & WMASK] = match = head[hash];\r
+                       head[hash] = (short)strstart;\r
+                       ins_h = hash;\r
+                       return match & 0xffff;\r
+               }\r
+               \r
+               void SlideWindow()\r
+               {\r
+                       Array.Copy(window, WSIZE, window, 0, WSIZE);\r
+                       matchStart -= WSIZE;\r
+                       strstart   -= WSIZE;\r
+                       blockStart -= WSIZE;\r
+                       \r
+                       /* Slide the hash table (could be avoided with 32 bit values\r
+                        * at the expense of memory usage).\r
+                        */\r
+                       for (int i = 0; i < HASH_SIZE; ++i) {\r
+                               int m = head[i] & 0xffff;\r
+                               head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);\r
+                       }\r
+                       \r
+                       /* Slide the prev table. */\r
+                       for (int i = 0; i < WSIZE; i++) {\r
+                               int m = prev[i] & 0xffff;\r
+                               prev[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);\r
+                       }\r
+               }\r
+               \r
+               public void FillWindow()\r
+               {\r
+                       /* If the window is almost full and there is insufficient lookahead,\r
+                        * move the upper half to the lower one to make room in the upper half.\r
+                        */\r
+                       if (strstart >= WSIZE + MAX_DIST) {\r
+                               SlideWindow();\r
+                       }\r
+                       \r
+                       /* If there is not enough lookahead, but still some input left,\r
+                        * read in the input\r
+                        */\r
+                       while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) {\r
+                               int more = 2 * WSIZE - lookahead - strstart;\r
+                               \r
+                               if (more > inputEnd - inputOff) {\r
+                                       more = inputEnd - inputOff;\r
+                               }\r
+                               \r
+                               System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more);\r
+                               adler.Update(inputBuf, inputOff, more);\r
+                               \r
+                               inputOff += more;\r
+                               totalIn  += more;\r
+                               lookahead += more;\r
+                       }\r
+                       \r
+                       if (lookahead >= MIN_MATCH) {\r
+                               UpdateHash();\r
+                       }\r
+               }\r
+               \r
+               bool FindLongestMatch(int curMatch) \r
+               {\r
+                       int chainLength = this.max_chain;\r
+                       int niceLength  = this.niceLength;\r
+                       short[] prev    = this.prev;\r
+                       int scan        = this.strstart;\r
+                       int match;\r
+                       int best_end = this.strstart + matchLen;\r
+                       int best_len = Math.Max(matchLen, MIN_MATCH - 1);\r
+                       \r
+                       int limit = Math.Max(strstart - MAX_DIST, 0);\r
+                       \r
+                       int strend = strstart + MAX_MATCH - 1;\r
+                       byte scan_end1 = window[best_end - 1];\r
+                       byte scan_end  = window[best_end];\r
+                       \r
+                       /* Do not waste too much time if we already have a good match: */\r
+                       if (best_len >= this.goodLength) {\r
+                               chainLength >>= 2;\r
+                       }\r
+                       \r
+                       /* Do not look for matches beyond the end of the input. This is necessary\r
+                       * to make deflate deterministic.\r
+                       */\r
+                       if (niceLength > lookahead) {\r
+                               niceLength = lookahead;\r
+                       }\r
+                       \r
+                       if (DeflaterConstants.DEBUGGING && strstart > 2 * WSIZE - MIN_LOOKAHEAD) {\r
+                               throw new InvalidOperationException("need lookahead");\r
+                       }\r
+                       \r
+                       do {\r
+                               if (DeflaterConstants.DEBUGGING && curMatch >= strstart) {\r
+                                       throw new InvalidOperationException("future match");\r
+                               }\r
+                               if (window[curMatch + best_len] != scan_end      || \r
+                                       window[curMatch + best_len - 1] != scan_end1 || \r
+                                       window[curMatch] != window[scan]             || \r
+                                       window[curMatch + 1] != window[scan + 1]) {\r
+                                       continue;\r
+                               }\r
+                               \r
+                               match = curMatch + 2;\r
+                               scan += 2;\r
+                               \r
+                               /* We check for insufficient lookahead only every 8th comparison;\r
+                               * the 256th check will be made at strstart+258.\r
+                               */\r
+                       while (window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && \r
+                               window[++scan] == window[++match] && scan < strend) ;\r
+                               \r
+                               if (scan > best_end) {\r
+                                       //      if (DeflaterConstants.DEBUGGING && ins_h == 0)\r
+                                       //        System.err.println("Found match: "+curMatch+"-"+(scan-strstart));\r
+                                       matchStart = curMatch;\r
+                                       best_end = scan;\r
+                                       best_len = scan - strstart;\r
+                                       \r
+                                       if (best_len >= niceLength) {\r
+                                               break;\r
+                                       }\r
+                                       \r
+                                       scan_end1  = window[best_end - 1];\r
+                                       scan_end   = window[best_end];\r
+                               }\r
+                               scan = strstart;\r
+                       } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit && --chainLength != 0);\r
+                       \r
+                       matchLen = Math.Min(best_len, lookahead);\r
+                       return matchLen >= MIN_MATCH;\r
+               }\r
+               \r
+               public void SetDictionary(byte[] buffer, int offset, int length) \r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && strstart != 1) {\r
+                               throw new InvalidOperationException("strstart not 1");\r
+                       }\r
+                       adler.Update(buffer, offset, length);\r
+                       if (length < MIN_MATCH) {\r
+                               return;\r
+                       }\r
+                       if (length > MAX_DIST) {\r
+                               offset += length - MAX_DIST;\r
+                               length = MAX_DIST;\r
+                       }\r
+                       \r
+                       System.Array.Copy(buffer, offset, window, strstart, length);\r
+                       \r
+                       UpdateHash();\r
+                       --length;\r
+                       while (--length > 0) {\r
+                               InsertString();\r
+                               strstart++;\r
+                       }\r
+                       strstart += 2;\r
+                       blockStart = strstart;\r
+               }\r
+               \r
+               bool DeflateStored(bool flush, bool finish)\r
+               {\r
+                       if (!flush && lookahead == 0) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       strstart += lookahead;\r
+                       lookahead = 0;\r
+                       \r
+                       int storedLen = strstart - blockStart;\r
+                       \r
+                       if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) || /* Block is full */\r
+                               (blockStart < WSIZE && storedLen >= MAX_DIST) ||   /* Block may move out of window */\r
+                               flush) {\r
+                               bool lastBlock = finish;\r
+                               if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) {\r
+                                       storedLen = DeflaterConstants.MAX_BLOCK_SIZE;\r
+                                       lastBlock = false;\r
+                               }\r
+                               \r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("storedBlock["+storedLen+","+lastBlock+"]");\r
+                               //                              }\r
+                                       \r
+                               huffman.FlushStoredBlock(window, blockStart, storedLen, lastBlock);\r
+                               blockStart += storedLen;\r
+                               return !lastBlock;\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               private bool DeflateFast(bool flush, bool finish)\r
+               {\r
+                       if (lookahead < MIN_LOOKAHEAD && !flush) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       while (lookahead >= MIN_LOOKAHEAD || flush) {\r
+                               if (lookahead == 0) {\r
+                                       /* We are flushing everything */\r
+                                       huffman.FlushBlock(window, blockStart, strstart - blockStart, finish);\r
+                                       blockStart = strstart;\r
+                                       return false;\r
+                               }\r
+                               \r
+                               if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) {\r
+                                       /* slide window, as findLongestMatch need this.\r
+                                        * This should only happen when flushing and the window\r
+                                        * is almost full.\r
+                                        */\r
+                                       SlideWindow();\r
+                               }\r
+                               \r
+                               int hashHead;\r
+                               if (lookahead >= MIN_MATCH && \r
+                                       (hashHead = InsertString()) != 0 && \r
+                                       strategy != DeflateStrategy.HuffmanOnly &&\r
+                                       strstart - hashHead <= MAX_DIST && \r
+                                       FindLongestMatch(hashHead)) {\r
+                                       /* longestMatch sets matchStart and matchLen */\r
+                                       //                                      if (DeflaterConstants.DEBUGGING) {\r
+                                       //                                              for (int i = 0 ; i < matchLen; i++) {\r
+                                       //                                                      if (window[strstart+i] != window[matchStart + i]) {\r
+                                       //                                                              throw new Exception();\r
+                                       //                                                      }\r
+                                       //                                              }\r
+                                       //                                      }\r
+                                       \r
+                                       // -jr- Hak hak hak this stops problems with fast/low compression and index out of range\r
+                                       if (huffman.TallyDist(strstart - matchStart, matchLen)) {\r
+                                               bool lastBlock = finish && lookahead == 0;\r
+                                               huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);\r
+                                               blockStart = strstart;\r
+                                       }\r
+                                       \r
+                                       lookahead -= matchLen;\r
+                                       if (matchLen <= max_lazy && lookahead >= MIN_MATCH) {\r
+                                               while (--matchLen > 0) {\r
+                                                       ++strstart;\r
+                                                       InsertString();\r
+                                               }\r
+                                               ++strstart;\r
+                                       } else {\r
+                                               strstart += matchLen;\r
+                                               if (lookahead >= MIN_MATCH - 1) {\r
+                                                       UpdateHash();\r
+                                               }\r
+                                       }\r
+                                       matchLen = MIN_MATCH - 1;\r
+                                       continue;\r
+                               } else {\r
+                                       /* No match found */\r
+                                       huffman.TallyLit(window[strstart] & 0xff);\r
+                                       ++strstart;\r
+                                       --lookahead;\r
+                               }\r
+                               \r
+                               if (huffman.IsFull()) {\r
+                                       bool lastBlock = finish && lookahead == 0;\r
+                                       huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);\r
+                                       blockStart = strstart;\r
+                                       return !lastBlock;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               bool DeflateSlow(bool flush, bool finish)\r
+               {\r
+                       if (lookahead < MIN_LOOKAHEAD && !flush) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       while (lookahead >= MIN_LOOKAHEAD || flush) {\r
+                               if (lookahead == 0) {\r
+                                       if (prevAvailable) {\r
+                                               huffman.TallyLit(window[strstart-1] & 0xff);\r
+                                       }\r
+                                       prevAvailable = false;\r
+                                       \r
+                                       /* We are flushing everything */\r
+                                       if (DeflaterConstants.DEBUGGING && !flush) {\r
+                                               throw new Exception("Not flushing, but no lookahead");\r
+                                       }\r
+                                       huffman.FlushBlock(window, blockStart, strstart - blockStart,\r
+                                               finish);\r
+                                       blockStart = strstart;\r
+                                       return false;\r
+                               }\r
+                               \r
+                               if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) {\r
+                                       /* slide window, as findLongestMatch need this.\r
+                                        * This should only happen when flushing and the window\r
+                                        * is almost full.\r
+                                        */\r
+                                       SlideWindow();\r
+                               }\r
+                               \r
+                               int prevMatch = matchStart;\r
+                               int prevLen = matchLen;\r
+                               if (lookahead >= MIN_MATCH) {\r
+                                       int hashHead = InsertString();\r
+                                       if (strategy != DeflateStrategy.HuffmanOnly && hashHead != 0 && strstart - hashHead <= MAX_DIST && FindLongestMatch(hashHead)) {\r
+                                               /* longestMatch sets matchStart and matchLen */\r
+                                                       \r
+                                               /* Discard match if too small and too far away */\r
+                                               if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == MIN_MATCH && strstart - matchStart > TOO_FAR))) {\r
+                                                       matchLen = MIN_MATCH - 1;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               \r
+                               /* previous match was better */\r
+                               if (prevLen >= MIN_MATCH && matchLen <= prevLen) {\r
+                                       //                                      if (DeflaterConstants.DEBUGGING) {\r
+                                       //                                              for (int i = 0 ; i < matchLen; i++) {\r
+                                       //                                                      if (window[strstart-1+i] != window[prevMatch + i])\r
+                                       //                                                              throw new Exception();\r
+                                       //                                              }\r
+                                       //                                      }\r
+                                       huffman.TallyDist(strstart - 1 - prevMatch, prevLen);\r
+                                       prevLen -= 2;\r
+                                       do {\r
+                                               strstart++;\r
+                                               lookahead--;\r
+                                               if (lookahead >= MIN_MATCH) {\r
+                                                       InsertString();\r
+                                               }\r
+                                       } while (--prevLen > 0);\r
+                                       strstart ++;\r
+                                       lookahead--;\r
+                                       prevAvailable = false;\r
+                                       matchLen = MIN_MATCH - 1;\r
+                               } else {\r
+                                       if (prevAvailable) {\r
+                                               huffman.TallyLit(window[strstart-1] & 0xff);\r
+                                       }\r
+                                       prevAvailable = true;\r
+                                       strstart++;\r
+                                       lookahead--;\r
+                               }\r
+                               \r
+                               if (huffman.IsFull()) {\r
+                                       int len = strstart - blockStart;\r
+                                       if (prevAvailable) {\r
+                                               len--;\r
+                                       }\r
+                                       bool lastBlock = (finish && lookahead == 0 && !prevAvailable);\r
+                                       huffman.FlushBlock(window, blockStart, len, lastBlock);\r
+                                       blockStart += len;\r
+                                       return !lastBlock;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               public bool Deflate(bool flush, bool finish)\r
+               {\r
+                       bool progress;\r
+                       do {\r
+                               FillWindow();\r
+                               bool canFlush = flush && inputOff == inputEnd;\r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("window: ["+blockStart+","+strstart+","\r
+                               //                                                        +lookahead+"], "+comprFunc+","+canFlush);\r
+                               //                              }\r
+                               switch (comprFunc) {\r
+                                       case DEFLATE_STORED:\r
+                                               progress = DeflateStored(canFlush, finish);\r
+                                               break;\r
+                                       case DEFLATE_FAST:\r
+                                               progress = DeflateFast(canFlush, finish);\r
+                                               break;\r
+                                       case DEFLATE_SLOW:\r
+                                               progress = DeflateSlow(canFlush, finish);\r
+                                               break;\r
+                                       default:\r
+                                               throw new InvalidOperationException("unknown comprFunc");\r
+                               }\r
+                       } while (pending.IsFlushed && progress); /* repeat while we have no pending output and progress was made */\r
+                       return progress;\r
+               }\r
+               \r
+               public void SetInput(byte[] buf, int off, int len)\r
+               {\r
+                       if (inputOff < inputEnd) {\r
+                               throw new InvalidOperationException("Old input was not completely processed");\r
+                       }\r
+                       \r
+                       int end = off + len;\r
+                       \r
+                       /* We want to throw an ArrayIndexOutOfBoundsException early.  The\r
+                       * check is very tricky: it also handles integer wrap around.\r
+                       */\r
+                       if (0 > off || off > end || end > buf.Length) {\r
+                               throw new ArgumentOutOfRangeException();\r
+                       }\r
+                       \r
+                       inputBuf = buf;\r
+                       inputOff = off;\r
+                       inputEnd = end;\r
+               }\r
+               \r
+               public bool NeedsInput()\r
+               {\r
+                       return inputEnd == inputOff;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/DeflaterHuffman.cs b/irc/TechBot/Compression/DeflaterHuffman.cs
new file mode 100644 (file)
index 0000000..338d09e
--- /dev/null
@@ -0,0 +1,780 @@
+// DeflaterHuffman.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       /// <summary>\r
+       /// This is the DeflaterHuffman class.\r
+       /// \r
+       /// This class is <i>not</i> thread safe.  This is inherent in the API, due\r
+       /// to the split of deflate and setInput.\r
+       /// \r
+       /// author of the original java version : Jochen Hoenicke\r
+       /// </summary>\r
+       public class DeflaterHuffman\r
+       {\r
+               private static  int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);\r
+               private static  int LITERAL_NUM = 286;\r
+               private static  int DIST_NUM = 30;\r
+               private static  int BITLEN_NUM = 19;\r
+               private static  int REP_3_6    = 16;\r
+               private static  int REP_3_10   = 17;\r
+               private static  int REP_11_138 = 18;\r
+               private static  int EOF_SYMBOL = 256;\r
+               private static  int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };\r
+               \r
+               private static byte[] bit4Reverse = {\r
+                       0,\r
+                       8,\r
+                       4,\r
+                       12,\r
+                       2,\r
+                       10,\r
+                       6,\r
+                       14,\r
+                       1,\r
+                       9,\r
+                       5,\r
+                       13,\r
+                       3,\r
+                       11,\r
+                       7,\r
+                       15\r
+               };\r
+               \r
+               public class Tree \r
+               {\r
+                       public short[] freqs;\r
+                       public byte[]  length;\r
+                       public int     minNumCodes, numCodes;\r
+                       \r
+                       short[] codes;\r
+                       int[]   bl_counts;\r
+                       int     maxLength;\r
+                       DeflaterHuffman dh;\r
+                       \r
+                       public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) \r
+                       {\r
+                               this.dh =  dh;\r
+                               this.minNumCodes = minCodes;\r
+                               this.maxLength  = maxLength;\r
+                               freqs  = new short[elems];\r
+                               bl_counts = new int[maxLength];\r
+                       }\r
+                       \r
+                       public void Reset() \r
+                       {\r
+                               for (int i = 0; i < freqs.Length; i++) {\r
+                                       freqs[i] = 0;\r
+                               }\r
+                               codes = null;\r
+                               length = null;\r
+                       }\r
+                       \r
+                       public void WriteSymbol(int code)\r
+                       {\r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      freqs[code]--;\r
+                               //                                      //        Console.Write("writeSymbol("+freqs.length+","+code+"): ");\r
+                               //                              }\r
+                               dh.pending.WriteBits(codes[code] & 0xffff, length[code]);\r
+                       }\r
+                       \r
+                       public void CheckEmpty()\r
+                       {\r
+                               bool empty = true;\r
+                               for (int i = 0; i < freqs.Length; i++) {\r
+                                       if (freqs[i] != 0) {\r
+                                               //Console.WriteLine("freqs["+i+"] == "+freqs[i]);\r
+                                               empty = false;\r
+                                       }\r
+                               }\r
+                               if (!empty) {\r
+                                       throw new Exception();\r
+                               }\r
+                               //Console.WriteLine("checkEmpty suceeded!");\r
+                       }\r
+                       \r
+                       public void SetStaticCodes(short[] stCodes, byte[] stLength)\r
+                       {\r
+                               codes = stCodes;\r
+                               length = stLength;\r
+                       }\r
+                       \r
+                       public void BuildCodes() \r
+                       {\r
+                               int numSymbols = freqs.Length;\r
+                               int[] nextCode = new int[maxLength];\r
+                               int code = 0;\r
+                               codes = new short[freqs.Length];\r
+                               \r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("buildCodes: "+freqs.Length);\r
+                               //                              }\r
+                               \r
+                               for (int bits = 0; bits < maxLength; bits++) {\r
+                                       nextCode[bits] = code;\r
+                                       code += bl_counts[bits] << (15 - bits);\r
+                                       //                                      if (DeflaterConstants.DEBUGGING) {\r
+                                       //                                              //Console.WriteLine("bits: "+(bits+1)+" count: "+bl_counts[bits]\r
+                                       //                                                                +" nextCode: "+code); // HACK : Integer.toHexString(\r
+                                       //                                      }\r
+                               }\r
+                               if (DeflaterConstants.DEBUGGING && code != 65536) {\r
+                                       throw new Exception("Inconsistent bl_counts!");\r
+                               }\r
+                               \r
+                               for (int i=0; i < numCodes; i++) {\r
+                                       int bits = length[i];\r
+                                       if (bits > 0) {\r
+                                               //                                              if (DeflaterConstants.DEBUGGING) {\r
+                                               //                                                              //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+")," // HACK : Integer.toHexString(\r
+                                               //                                                                                +bits);\r
+                                               //                                              }\r
+                                               codes[i] = BitReverse(nextCode[bits-1]);\r
+                                               nextCode[bits-1] += 1 << (16 - bits);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       void BuildLength(int[] childs)\r
+                       {\r
+                               this.length = new byte [freqs.Length];\r
+                               int numNodes = childs.Length / 2;\r
+                               int numLeafs = (numNodes + 1) / 2;\r
+                               int overflow = 0;\r
+                               \r
+                               for (int i = 0; i < maxLength; i++) {\r
+                                       bl_counts[i] = 0;\r
+                               }\r
+                               \r
+                               /* First calculate optimal bit lengths */\r
+                               int[] lengths = new int[numNodes];\r
+                               lengths[numNodes-1] = 0;\r
+                               \r
+                               for (int i = numNodes - 1; i >= 0; i--) {\r
+                                       if (childs[2*i+1] != -1) {\r
+                                               int bitLength = lengths[i] + 1;\r
+                                               if (bitLength > maxLength) {\r
+                                                       bitLength = maxLength;\r
+                                                       overflow++;\r
+                                               }\r
+                                               lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength;\r
+                                       } else {\r
+                                               /* A leaf node */\r
+                                               int bitLength = lengths[i];\r
+                                               bl_counts[bitLength - 1]++;\r
+                                               this.length[childs[2*i]] = (byte) lengths[i];\r
+                                       }\r
+                               }\r
+                               \r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("Tree "+freqs.Length+" lengths:");\r
+                               //                                      for (int i=0; i < numLeafs; i++) {\r
+                               //                                              //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]\r
+                               //                                                                + " len: "+length[childs[2*i]]);\r
+                               //                                      }\r
+                               //                              }\r
+                               \r
+                               if (overflow == 0) {\r
+                                       return;\r
+                               }\r
+                               \r
+                               int incrBitLen = maxLength - 1;\r
+                               do {\r
+                                       /* Find the first bit length which could increase: */\r
+                                       while (bl_counts[--incrBitLen] == 0)\r
+                                               ;\r
+                                       \r
+                                       /* Move this node one down and remove a corresponding\r
+                                       * amount of overflow nodes.\r
+                                       */\r
+                                       do {\r
+                                               bl_counts[incrBitLen]--;\r
+                                               bl_counts[++incrBitLen]++;\r
+                                               overflow -= 1 << (maxLength - 1 - incrBitLen);\r
+                                       } while (overflow > 0 && incrBitLen < maxLength - 1);\r
+                               } while (overflow > 0);\r
+                               \r
+                               /* We may have overshot above.  Move some nodes from maxLength to\r
+                               * maxLength-1 in that case.\r
+                               */\r
+                               bl_counts[maxLength-1] += overflow;\r
+                               bl_counts[maxLength-2] -= overflow;\r
+                               \r
+                               /* Now recompute all bit lengths, scanning in increasing\r
+                               * frequency.  It is simpler to reconstruct all lengths instead of\r
+                               * fixing only the wrong ones. This idea is taken from 'ar'\r
+                               * written by Haruhiko Okumura.\r
+                               *\r
+                               * The nodes were inserted with decreasing frequency into the childs\r
+                               * array.\r
+                               */\r
+                               int nodePtr = 2 * numLeafs;\r
+                               for (int bits = maxLength; bits != 0; bits--) {\r
+                                       int n = bl_counts[bits-1];\r
+                                       while (n > 0) {\r
+                                               int childPtr = 2*childs[nodePtr++];\r
+                                               if (childs[childPtr + 1] == -1) {\r
+                                                       /* We found another leaf */\r
+                                                       length[childs[childPtr]] = (byte) bits;\r
+                                                       n--;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("*** After overflow elimination. ***");\r
+                               //                                      for (int i=0; i < numLeafs; i++) {\r
+                               //                                              //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]\r
+                               //                                                                + " len: "+length[childs[2*i]]);\r
+                               //                                      }\r
+                               //                              }\r
+                       }\r
+                       \r
+                       public void BuildTree()\r
+                       {\r
+                               int numSymbols = freqs.Length;\r
+                               \r
+                               /* heap is a priority queue, sorted by frequency, least frequent\r
+                               * nodes first.  The heap is a binary tree, with the property, that\r
+                               * the parent node is smaller than both child nodes.  This assures\r
+                               * that the smallest node is the first parent.\r
+                               *\r
+                               * The binary tree is encoded in an array:  0 is root node and\r
+                               * the nodes 2*n+1, 2*n+2 are the child nodes of node n.\r
+                               */\r
+                               int[] heap = new int[numSymbols];\r
+                               int heapLen = 0;\r
+                               int maxCode = 0;\r
+                               for (int n = 0; n < numSymbols; n++) {\r
+                                       int freq = freqs[n];\r
+                                       if (freq != 0) {\r
+                                               /* Insert n into heap */\r
+                                               int pos = heapLen++;\r
+                                               int ppos;\r
+                                               while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) {\r
+                                                       heap[pos] = heap[ppos];\r
+                                                       pos = ppos;\r
+                                               }\r
+                                               heap[pos] = n;\r
+                                               \r
+                                               maxCode = n;\r
+                                       }\r
+                               }\r
+                               \r
+                               /* We could encode a single literal with 0 bits but then we\r
+                               * don't see the literals.  Therefore we force at least two\r
+                               * literals to avoid this case.  We don't care about order in\r
+                               * this case, both literals get a 1 bit code.\r
+                               */\r
+                               while (heapLen < 2) {\r
+                                       int node = maxCode < 2 ? ++maxCode : 0;\r
+                                       heap[heapLen++] = node;\r
+                               }\r
+                               \r
+                               numCodes = Math.Max(maxCode + 1, minNumCodes);\r
+                               \r
+                               int numLeafs = heapLen;\r
+                               int[] childs = new int[4*heapLen - 2];\r
+                               int[] values = new int[2*heapLen - 1];\r
+                               int numNodes = numLeafs;\r
+                               for (int i = 0; i < heapLen; i++) {\r
+                                       int node = heap[i];\r
+                                       childs[2*i]   = node;\r
+                                       childs[2*i+1] = -1;\r
+                                       values[i] = freqs[node] << 8;\r
+                                       heap[i] = i;\r
+                               }\r
+                               \r
+                               /* Construct the Huffman tree by repeatedly combining the least two\r
+                               * frequent nodes.\r
+                               */\r
+                               do {\r
+                                       int first = heap[0];\r
+                                       int last  = heap[--heapLen];\r
+                                       \r
+                                       /* Propagate the hole to the leafs of the heap */\r
+                                       int ppos = 0;\r
+                                       int path = 1;\r
+                                       \r
+                                       while (path < heapLen) {\r
+                                               if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) {\r
+                                                       path++;\r
+                                               }\r
+                                                       \r
+                                               heap[ppos] = heap[path];\r
+                                               ppos = path;\r
+                                               path = path * 2 + 1;\r
+                                       }\r
+                                               \r
+                                       /* Now propagate the last element down along path.  Normally\r
+                                       * it shouldn't go too deep.\r
+                                       */\r
+                                       int lastVal = values[last];\r
+                                       while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) {\r
+                                               heap[path] = heap[ppos];\r
+                                       }\r
+                                       heap[path] = last;\r
+                                       \r
+                                       \r
+                                       int second = heap[0];\r
+                                       \r
+                                       /* Create a new node father of first and second */\r
+                                       last = numNodes++;\r
+                                       childs[2*last] = first;\r
+                                       childs[2*last+1] = second;\r
+                                       int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff);\r
+                                       values[last] = lastVal = values[first] + values[second] - mindepth + 1;\r
+                                       \r
+                                       /* Again, propagate the hole to the leafs */\r
+                                       ppos = 0;\r
+                                       path = 1;\r
+                                       \r
+                                       while (path < heapLen) {\r
+                                               if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) {\r
+                                                       path++;\r
+                                               }\r
+                                                       \r
+                                               heap[ppos] = heap[path];\r
+                                               ppos = path;\r
+                                               path = ppos * 2 + 1;\r
+                                       }\r
+                                               \r
+                                       /* Now propagate the new element down along path */\r
+                                       while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) {\r
+                                               heap[path] = heap[ppos];\r
+                                       }\r
+                                       heap[path] = last;\r
+                               } while (heapLen > 1);\r
+                               \r
+                               if (heap[0] != childs.Length / 2 - 1) {\r
+                                       throw new Exception("Weird!");\r
+                               }\r
+                               BuildLength(childs);\r
+                       }\r
+                       \r
+                       public int GetEncodedLength()\r
+                       {\r
+                               int len = 0;\r
+                               for (int i = 0; i < freqs.Length; i++) {\r
+                                       len += freqs[i] * length[i];\r
+                               }\r
+                               return len;\r
+                       }\r
+                       \r
+                       public void CalcBLFreq(Tree blTree) \r
+                       {\r
+                               int max_count;               /* max repeat count */\r
+                               int min_count;               /* min repeat count */\r
+                               int count;                   /* repeat count of the current code */\r
+                               int curlen = -1;             /* length of current code */\r
+                               \r
+                               int i = 0;\r
+                               while (i < numCodes) {\r
+                                       count = 1;\r
+                                       int nextlen = length[i];\r
+                                       if (nextlen == 0) {\r
+                                               max_count = 138;\r
+                                               min_count = 3;\r
+                                       } else {\r
+                                               max_count = 6;\r
+                                               min_count = 3;\r
+                                               if (curlen != nextlen) {\r
+                                                       blTree.freqs[nextlen]++;\r
+                                                       count = 0;\r
+                                               }\r
+                                       }\r
+                                       curlen = nextlen;\r
+                                       i++;\r
+                                       \r
+                                       while (i < numCodes && curlen == length[i]) {\r
+                                               i++;\r
+                                               if (++count >= max_count) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       if (count < min_count) {\r
+                                               blTree.freqs[curlen] += (short)count;\r
+                                       } else if (curlen != 0) {\r
+                                               blTree.freqs[REP_3_6]++;\r
+                                       } else if (count <= 10) {\r
+                                               blTree.freqs[REP_3_10]++;\r
+                                       } else {\r
+                                               blTree.freqs[REP_11_138]++;\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       public void WriteTree(Tree blTree)\r
+                       {\r
+                               int max_count;               /* max repeat count */\r
+                               int min_count;               /* min repeat count */\r
+                               int count;                   /* repeat count of the current code */\r
+                               int curlen = -1;             /* length of current code */\r
+                               \r
+                               int i = 0;\r
+                               while (i < numCodes) {\r
+                                       count = 1;\r
+                                       int nextlen = length[i];\r
+                                       if (nextlen == 0) {\r
+                                               max_count = 138;\r
+                                               min_count = 3;\r
+                                       } else {\r
+                                               max_count = 6;\r
+                                               min_count = 3;\r
+                                               if (curlen != nextlen) {\r
+                                                       blTree.WriteSymbol(nextlen);\r
+                                                       count = 0;\r
+                                               }\r
+                                       }\r
+                                       curlen = nextlen;\r
+                                       i++;\r
+                                       \r
+                                       while (i < numCodes && curlen == length[i]) {\r
+                                               i++;\r
+                                               if (++count >= max_count) {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       if (count < min_count) {\r
+                                               while (count-- > 0) {\r
+                                                       blTree.WriteSymbol(curlen);\r
+                                               }\r
+                                       } else if (curlen != 0) {\r
+                                               blTree.WriteSymbol(REP_3_6);\r
+                                               dh.pending.WriteBits(count - 3, 2);\r
+                                       } else if (count <= 10) {\r
+                                               blTree.WriteSymbol(REP_3_10);\r
+                                               dh.pending.WriteBits(count - 3, 3);\r
+                                       } else {\r
+                                               blTree.WriteSymbol(REP_11_138);\r
+                                               dh.pending.WriteBits(count - 11, 7);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               public DeflaterPending pending;\r
+               private Tree literalTree, distTree, blTree;\r
+               \r
+               private short[] d_buf;\r
+               private byte[]  l_buf;\r
+               private int last_lit;\r
+               private int extra_bits;\r
+               \r
+               private static short[] staticLCodes;\r
+               private static byte[]  staticLLength;\r
+               private static short[] staticDCodes;\r
+               private static byte[]  staticDLength;\r
+               \r
+               /// <summary>\r
+               /// Reverse the bits of a 16 bit value.\r
+               /// </summary>\r
+               public static short BitReverse(int value) \r
+               {\r
+                       return (short) (bit4Reverse[value & 0xF] << 12 | \r
+                                       bit4Reverse[(value >> 4) & 0xF] << 8 | \r
+                                       bit4Reverse[(value >> 8) & 0xF] << 4 |\r
+                                       bit4Reverse[value >> 12]);\r
+               }\r
+               \r
+               \r
+               static DeflaterHuffman() \r
+               {\r
+                       /* See RFC 1951 3.2.6 */\r
+                       /* Literal codes */\r
+                       staticLCodes = new short[LITERAL_NUM];\r
+                       staticLLength = new byte[LITERAL_NUM];\r
+                       int i = 0;\r
+                       while (i < 144) {\r
+                               staticLCodes[i] = BitReverse((0x030 + i) << 8);\r
+                               staticLLength[i++] = 8;\r
+                       }\r
+                       while (i < 256) {\r
+                               staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);\r
+                               staticLLength[i++] = 9;\r
+                       }\r
+                       while (i < 280) {\r
+                               staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);\r
+                               staticLLength[i++] = 7;\r
+                       }\r
+                       while (i < LITERAL_NUM) {\r
+                               staticLCodes[i] = BitReverse((0x0c0 - 280 + i)  << 8);\r
+                               staticLLength[i++] = 8;\r
+                       }\r
+                       \r
+                       /* Distant codes */\r
+                       staticDCodes = new short[DIST_NUM];\r
+                       staticDLength = new byte[DIST_NUM];\r
+                       for (i = 0; i < DIST_NUM; i++) {\r
+                               staticDCodes[i] = BitReverse(i << 11);\r
+                               staticDLength[i] = 5;\r
+                       }\r
+               }\r
+               \r
+               public DeflaterHuffman(DeflaterPending pending)\r
+               {\r
+                       this.pending = pending;\r
+                       \r
+                       literalTree = new Tree(this, LITERAL_NUM, 257, 15);\r
+                       distTree    = new Tree(this, DIST_NUM, 1, 15);\r
+                       blTree      = new Tree(this, BITLEN_NUM, 4, 7);\r
+                       \r
+                       d_buf = new short[BUFSIZE];\r
+                       l_buf = new byte [BUFSIZE];\r
+               }\r
+               \r
+               public void Reset() \r
+               {\r
+                       last_lit = 0;\r
+                       extra_bits = 0;\r
+                       literalTree.Reset();\r
+                       distTree.Reset();\r
+                       blTree.Reset();\r
+               }\r
+               \r
+               int Lcode(int len) \r
+               {\r
+                       if (len == 255) {\r
+                               return 285;\r
+                       }\r
+                       \r
+                       int code = 257;\r
+                       while (len >= 8) {\r
+                               code += 4;\r
+                               len >>= 1;\r
+                       }\r
+                       return code + len;\r
+               }\r
+               \r
+               int Dcode(int distance) \r
+               {\r
+                       int code = 0;\r
+                       while (distance >= 4) {\r
+                               code += 2;\r
+                               distance >>= 1;\r
+                       }\r
+                       return code + distance;\r
+               }\r
+               \r
+               public void SendAllTrees(int blTreeCodes)\r
+               {\r
+                       blTree.BuildCodes();\r
+                       literalTree.BuildCodes();\r
+                       distTree.BuildCodes();\r
+                       pending.WriteBits(literalTree.numCodes - 257, 5);\r
+                       pending.WriteBits(distTree.numCodes - 1, 5);\r
+                       pending.WriteBits(blTreeCodes - 4, 4);\r
+                       for (int rank = 0; rank < blTreeCodes; rank++) {\r
+                               pending.WriteBits(blTree.length[BL_ORDER[rank]], 3);\r
+                       }\r
+                       literalTree.WriteTree(blTree);\r
+                       distTree.WriteTree(blTree);\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              blTree.CheckEmpty();\r
+                       //                      }\r
+               }\r
+               \r
+               public void CompressBlock()\r
+               {\r
+                       for (int i = 0; i < last_lit; i++) {\r
+                               int litlen = l_buf[i] & 0xff;\r
+                               int dist = d_buf[i];\r
+                               if (dist-- != 0) {\r
+                                       //                                      if (DeflaterConstants.DEBUGGING) {\r
+                                       //                                              Console.Write("["+(dist+1)+","+(litlen+3)+"]: ");\r
+                                       //                                      }\r
+                                       \r
+                                       int lc = Lcode(litlen);\r
+                                       literalTree.WriteSymbol(lc);\r
+                                       \r
+                                       int bits = (lc - 261) / 4;\r
+                                       if (bits > 0 && bits <= 5) {\r
+                                               pending.WriteBits(litlen & ((1 << bits) - 1), bits);\r
+                                       }\r
+                                       \r
+                                       int dc = Dcode(dist);\r
+                                       distTree.WriteSymbol(dc);\r
+                                       \r
+                                       bits = dc / 2 - 1;\r
+                                       if (bits > 0) {\r
+                                               pending.WriteBits(dist & ((1 << bits) - 1), bits);\r
+                                       }\r
+                               } else {\r
+                                       //                                      if (DeflaterConstants.DEBUGGING) {\r
+                                       //                                              if (litlen > 32 && litlen < 127) {\r
+                                       //                                                      Console.Write("("+(char)litlen+"): ");\r
+                                       //                                              } else {\r
+                                       //                                                      Console.Write("{"+litlen+"}: ");\r
+                                       //                                              }\r
+                                       //                                      }\r
+                                       literalTree.WriteSymbol(litlen);\r
+                               }\r
+                       }\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              Console.Write("EOF: ");\r
+                       //                      }\r
+                       literalTree.WriteSymbol(EOF_SYMBOL);\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              literalTree.CheckEmpty();\r
+                       //                              distTree.CheckEmpty();\r
+                       //                      }\r
+               }\r
+               \r
+               public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)\r
+               {\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              //Console.WriteLine("Flushing stored block "+ storedLength);\r
+                       //                      }\r
+                       pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3);\r
+                       pending.AlignToByte();\r
+                       pending.WriteShort(storedLength);\r
+                       pending.WriteShort(~storedLength);\r
+                       pending.WriteBlock(stored, storedOffset, storedLength);\r
+                       Reset();\r
+               }\r
+               \r
+               public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)\r
+               {\r
+                       literalTree.freqs[EOF_SYMBOL]++;\r
+                       \r
+                       /* Build trees */\r
+                       literalTree.BuildTree();\r
+                       distTree.BuildTree();\r
+                       \r
+                       /* Calculate bitlen frequency */\r
+                       literalTree.CalcBLFreq(blTree);\r
+                       distTree.CalcBLFreq(blTree);\r
+                       \r
+                       /* Build bitlen tree */\r
+                       blTree.BuildTree();\r
+                       \r
+                       int blTreeCodes = 4;\r
+                       for (int i = 18; i > blTreeCodes; i--) {\r
+                               if (blTree.length[BL_ORDER[i]] > 0) {\r
+                                       blTreeCodes = i+1;\r
+                               }\r
+                       }\r
+                       int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() + \r
+                               literalTree.GetEncodedLength() + distTree.GetEncodedLength() + \r
+                               extra_bits;\r
+                       \r
+                       int static_len = extra_bits;\r
+                       for (int i = 0; i < LITERAL_NUM; i++) {\r
+                               static_len += literalTree.freqs[i] * staticLLength[i];\r
+                       }\r
+                       for (int i = 0; i < DIST_NUM; i++) {\r
+                               static_len += distTree.freqs[i] * staticDLength[i];\r
+                       }\r
+                       if (opt_len >= static_len) {\r
+                               /* Force static trees */\r
+                               opt_len = static_len;\r
+                       }\r
+                       \r
+                       if (storedOffset >= 0 && storedLength+4 < opt_len >> 3) {\r
+                               /* Store Block */\r
+                               //                              if (DeflaterConstants.DEBUGGING) {\r
+                               //                                      //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len\r
+                               //                                                        + " <= " + static_len);\r
+                               //                              }\r
+                               FlushStoredBlock(stored, storedOffset, storedLength, lastBlock);\r
+                       } else if (opt_len == static_len) {\r
+                               /* Encode with static tree */\r
+                               pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3);\r
+                               literalTree.SetStaticCodes(staticLCodes, staticLLength);\r
+                               distTree.SetStaticCodes(staticDCodes, staticDLength);\r
+                               CompressBlock();\r
+                               Reset();\r
+                       } else {\r
+                               /* Encode with dynamic tree */\r
+                               pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3);\r
+                               SendAllTrees(blTreeCodes);\r
+                               CompressBlock();\r
+                               Reset();\r
+                       }\r
+               }\r
+               \r
+               public bool IsFull()\r
+               {\r
+//                     return last_lit + 16 >= BUFSIZE; // HACK: This was == 'last_lit == BUFSIZE', but errors occured with DeflateFast\r
+                       return last_lit >= BUFSIZE; // -jr- This is the correct form!\r
+               }\r
+               \r
+               public bool TallyLit(int lit)\r
+               {\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              if (lit > 32 && lit < 127) {\r
+                       //                                      //Console.WriteLine("("+(char)lit+")");\r
+                       //                              } else {\r
+                       //                                      //Console.WriteLine("{"+lit+"}");\r
+                       //                              }\r
+                       //                      }\r
+                       d_buf[last_lit] = 0;\r
+                       l_buf[last_lit++] = (byte)lit;\r
+                       literalTree.freqs[lit]++;\r
+                       return IsFull();\r
+               }\r
+               \r
+               public bool TallyDist(int dist, int len)\r
+               {\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              //Console.WriteLine("["+dist+","+len+"]");\r
+                       //                      }\r
+                       \r
+                       d_buf[last_lit]   = (short)dist;\r
+                       l_buf[last_lit++] = (byte)(len - 3);\r
+                       \r
+                       int lc = Lcode(len - 3);\r
+                       literalTree.freqs[lc]++;\r
+                       if (lc >= 265 && lc < 285) {\r
+                               extra_bits += (lc - 261) / 4;\r
+                       }\r
+                       \r
+                       int dc = Dcode(dist - 1);\r
+                       distTree.freqs[dc]++;\r
+                       if (dc >= 4) {\r
+                               extra_bits += dc / 2 - 1;\r
+                       }\r
+                       return IsFull();\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/DeflaterPending.cs b/irc/TechBot/Compression/DeflaterPending.cs
new file mode 100644 (file)
index 0000000..8144ff3
--- /dev/null
@@ -0,0 +1,52 @@
+// DeflaterPending.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       /// <summary>\r
+       /// This class stores the pending output of the Deflater.\r
+       /// \r
+       /// author of the original java version : Jochen Hoenicke\r
+       /// </summary>\r
+       public class DeflaterPending : PendingBuffer\r
+       {\r
+               public DeflaterPending() : base(DeflaterConstants.PENDING_BUF_SIZE)\r
+               {\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Inflater.cs b/irc/TechBot/Compression/Inflater.cs
new file mode 100644 (file)
index 0000000..b915c8f
--- /dev/null
@@ -0,0 +1,782 @@
+// Inflater.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+using ICSharpCode.SharpZipLib.Checksums;\r
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       /// <summary>\r
+       /// Inflater is used to decompress data that has been compressed according\r
+       /// to the "deflate" standard described in rfc1950.\r
+       ///\r
+       /// The usage is as following.  First you have to set some input with\r
+       /// <code>setInput()</code>, then inflate() it.  If inflate doesn't\r
+       /// inflate any bytes there may be three reasons:\r
+       /// <ul>\r
+       /// <li>needsInput() returns true because the input buffer is empty.\r
+       /// You have to provide more input with <code>setInput()</code>.\r
+       /// NOTE: needsInput() also returns true when, the stream is finished.\r
+       /// </li>\r
+       /// <li>needsDictionary() returns true, you have to provide a preset\r
+       ///    dictionary with <code>setDictionary()</code>.</li>\r
+       /// <li>finished() returns true, the inflater has finished.</li>\r
+       /// </ul>\r
+       /// Once the first output byte is produced, a dictionary will not be\r
+       /// needed at a later stage.\r
+       ///\r
+       /// author of the original java version : John Leuner, Jochen Hoenicke\r
+       /// </summary>\r
+       public class Inflater\r
+       {\r
+               /// <summary>\r
+               /// Copy lengths for literal codes 257..285\r
+               /// </summary>\r
+               private static int[] CPLENS = {\r
+                                                                                 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,\r
+                                                                                 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258\r
+                                                                         };\r
+               \r
+               /// <summary>\r
+               /// Extra bits for literal codes 257..285\r
+               /// </summary>\r
+               private static int[] CPLEXT = {\r
+                                                                                 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,\r
+                                                                                 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0\r
+                                                                         };\r
+               \r
+               /// <summary>\r
+               /// Copy offsets for distance codes 0..29\r
+               /// </summary>\r
+               private static int[] CPDIST = {\r
+                                                                                 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,\r
+                                                                                 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,\r
+                                                                                 8193, 12289, 16385, 24577\r
+                                                                         };\r
+               \r
+               /// <summary>\r
+               /// Extra bits for distance codes\r
+               /// </summary>\r
+               private static int[] CPDEXT = {\r
+                                                                                 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,\r
+                                                                                 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,\r
+                                                                                 12, 12, 13, 13\r
+                                                                         };\r
+               \r
+               /// <summary>\r
+               /// This are the state in which the inflater can be.\r
+               /// </summary>\r
+               private const int DECODE_HEADER           = 0;\r
+               private const int DECODE_DICT             = 1;\r
+               private const int DECODE_BLOCKS           = 2;\r
+               private const int DECODE_STORED_LEN1      = 3;\r
+               private const int DECODE_STORED_LEN2      = 4;\r
+               private const int DECODE_STORED           = 5;\r
+               private const int DECODE_DYN_HEADER       = 6;\r
+               private const int DECODE_HUFFMAN          = 7;\r
+               private const int DECODE_HUFFMAN_LENBITS  = 8;\r
+               private const int DECODE_HUFFMAN_DIST     = 9;\r
+               private const int DECODE_HUFFMAN_DISTBITS = 10;\r
+               private const int DECODE_CHKSUM           = 11;\r
+               private const int FINISHED                = 12;\r
+               \r
+               /// <summary>\r
+               /// This variable contains the current state.\r
+               /// </summary>\r
+               private int mode;\r
+               \r
+               /// <summary>\r
+               /// The adler checksum of the dictionary or of the decompressed\r
+               /// stream, as it is written in the header resp. footer of the\r
+               /// compressed stream. \r
+               /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM.\r
+               /// </summary>\r
+               private int readAdler;\r
+               \r
+               /// <summary>\r
+               /// The number of bits needed to complete the current state.  This\r
+               /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM,\r
+               /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.\r
+               /// </summary>\r
+               private int neededBits;\r
+               private int repLength, repDist;\r
+               private int uncomprLen;\r
+               \r
+               /// <summary>\r
+               /// True, if the last block flag was set in the last block of the\r
+               /// inflated stream.  This means that the stream ends after the\r
+               /// current block.\r
+               /// </summary>\r
+               private bool isLastBlock;\r
+               \r
+               /// <summary>\r
+               /// The total number of inflated bytes.\r
+               /// </summary>\r
+               private int totalOut;\r
+               \r
+               /// <summary>\r
+               /// The total number of bytes set with setInput().  This is not the\r
+               /// value returned by getTotalIn(), since this also includes the\r
+               /// unprocessed input.\r
+               /// </summary>\r
+               private int totalIn;\r
+               \r
+               /// <summary>\r
+               /// This variable stores the nowrap flag that was given to the constructor.\r
+               /// True means, that the inflated stream doesn't contain a header nor the\r
+               /// checksum in the footer.\r
+               /// </summary>\r
+               private bool nowrap;\r
+               \r
+               private StreamManipulator input;\r
+               private OutputWindow outputWindow;\r
+               private InflaterDynHeader dynHeader;\r
+               private InflaterHuffmanTree litlenTree, distTree;\r
+               private Adler32 adler;\r
+               \r
+               /// <summary>\r
+               /// Creates a new inflater.\r
+               /// </summary>\r
+               public Inflater() : this(false)\r
+               {\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new inflater.\r
+               /// </summary>\r
+               /// <param name="nowrap">\r
+               /// true if no header and checksum field appears in the\r
+               /// stream.  This is used for GZIPed input.  For compatibility with\r
+               /// Sun JDK you should provide one byte of input more than needed in\r
+               /// this case.\r
+               /// </param>\r
+               public Inflater(bool nowrap)\r
+               {\r
+                       this.nowrap = nowrap;\r
+                       this.adler = new Adler32();\r
+                       input = new StreamManipulator();\r
+                       outputWindow = new OutputWindow();\r
+                       mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Resets the inflater so that a new stream can be decompressed.  All\r
+               /// pending input and output will be discarded.\r
+               /// </summary>\r
+               public void Reset()\r
+               {\r
+                       mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;\r
+                       totalIn = totalOut = 0;\r
+                       input.Reset();\r
+                       outputWindow.Reset();\r
+                       dynHeader = null;\r
+                       litlenTree = null;\r
+                       distTree = null;\r
+                       isLastBlock = false;\r
+                       adler.Reset();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decodes the deflate header.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// false if more input is needed.\r
+               /// </returns>\r
+               /// <exception cref="System.FormatException">\r
+               /// if header is invalid.\r
+               /// </exception>\r
+               private bool DecodeHeader()\r
+               {\r
+                       int header = input.PeekBits(16);\r
+                       if (header < 0) {\r
+                               return false;\r
+                       }\r
+                       input.DropBits(16);\r
+                       /* The header is written in "wrong" byte order */\r
+                       header = ((header << 8) | (header >> 8)) & 0xffff;\r
+                       if (header % 31 != 0) {\r
+                               throw new FormatException("Header checksum illegal");\r
+                       }\r
+                       \r
+                       if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) {\r
+                               throw new FormatException("Compression Method unknown");\r
+                       }\r
+                       \r
+                       /* Maximum size of the backwards window in bits.\r
+                       * We currently ignore this, but we could use it to make the\r
+                       * inflater window more space efficient. On the other hand the\r
+                       * full window (15 bits) is needed most times, anyway.\r
+                       int max_wbits = ((header & 0x7000) >> 12) + 8;\r
+                       */\r
+                       \r
+                       if ((header & 0x0020) == 0) { // Dictionary flag?\r
+                               mode = DECODE_BLOCKS;\r
+                       } else {\r
+                               mode = DECODE_DICT;\r
+                               neededBits = 32;\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decodes the dictionary checksum after the deflate header.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// false if more input is needed.\r
+               /// </returns>\r
+               private bool DecodeDict()\r
+               {\r
+                       while (neededBits > 0) {\r
+                               int dictByte = input.PeekBits(8);\r
+                               if (dictByte < 0) {\r
+                                       return false;\r
+                               }\r
+                               input.DropBits(8);\r
+                               readAdler = (readAdler << 8) | dictByte;\r
+                               neededBits -= 8;\r
+                       }\r
+                       return false;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decodes the huffman encoded symbols in the input stream.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// false if more input is needed, true if output window is\r
+               /// full or the current block ends.\r
+               /// </returns>\r
+               /// <exception cref="System.FormatException">\r
+               /// if deflated stream is invalid.\r
+               /// </exception>\r
+               private bool DecodeHuffman()\r
+               {\r
+                       int free = outputWindow.GetFreeSpace();\r
+                       while (free >= 258) {\r
+                               int symbol;\r
+                               switch (mode) {\r
+                                       case DECODE_HUFFMAN:\r
+                                               /* This is the inner loop so it is optimized a bit */\r
+                                               while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) {\r
+                                                       outputWindow.Write(symbol);\r
+                                                       if (--free < 258) {\r
+                                                               return true;\r
+                                                       }\r
+                                               }\r
+                                               if (symbol < 257) {\r
+                                                       if (symbol < 0) {\r
+                                                               return false;\r
+                                                       } else {\r
+                                                               /* symbol == 256: end of block */\r
+                                                               distTree = null;\r
+                                                               litlenTree = null;\r
+                                                               mode = DECODE_BLOCKS;\r
+                                                               return true;\r
+                                                       }\r
+                                               }\r
+                                               \r
+                                               try {\r
+                                                       repLength = CPLENS[symbol - 257];\r
+                                                       neededBits = CPLEXT[symbol - 257];\r
+                                               } catch (Exception) {\r
+                                                       throw new FormatException("Illegal rep length code");\r
+                                               }\r
+                                               goto case DECODE_HUFFMAN_LENBITS;/* fall through */\r
+                                       case DECODE_HUFFMAN_LENBITS:\r
+                                               if (neededBits > 0) {\r
+                                                       mode = DECODE_HUFFMAN_LENBITS;\r
+                                                       int i = input.PeekBits(neededBits);\r
+                                                       if (i < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                                       input.DropBits(neededBits);\r
+                                                       repLength += i;\r
+                                               }\r
+                                               mode = DECODE_HUFFMAN_DIST;\r
+                                               goto case DECODE_HUFFMAN_DIST;/* fall through */\r
+                                       case DECODE_HUFFMAN_DIST:\r
+                                               symbol = distTree.GetSymbol(input);\r
+                                               if (symbol < 0) {\r
+                                                       return false;\r
+                                               }\r
+                                               try {\r
+                                                       repDist = CPDIST[symbol];\r
+                                                       neededBits = CPDEXT[symbol];\r
+                                               } catch (Exception) {\r
+                                                       throw new FormatException("Illegal rep dist code");\r
+                                               }\r
+                                               \r
+                                               goto case DECODE_HUFFMAN_DISTBITS;/* fall through */\r
+                                       case DECODE_HUFFMAN_DISTBITS:\r
+                                               if (neededBits > 0) {\r
+                                                       mode = DECODE_HUFFMAN_DISTBITS;\r
+                                                       int i = input.PeekBits(neededBits);\r
+                                                       if (i < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                                       input.DropBits(neededBits);\r
+                                                       repDist += i;\r
+                                               }\r
+                                               outputWindow.Repeat(repLength, repDist);\r
+                                               free -= repLength;\r
+                                               mode = DECODE_HUFFMAN;\r
+                                               break;\r
+                                       default:\r
+                                               throw new FormatException();\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decodes the adler checksum after the deflate stream.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// false if more input is needed.\r
+               /// </returns>\r
+               /// <exception cref="System.FormatException">\r
+               /// DataFormatException, if checksum doesn't match.\r
+               /// </exception>\r
+               private bool DecodeChksum()\r
+               {\r
+                       while (neededBits > 0) {\r
+                               int chkByte = input.PeekBits(8);\r
+                               if (chkByte < 0) {\r
+                                       return false;\r
+                               }\r
+                               input.DropBits(8);\r
+                               readAdler = (readAdler << 8) | chkByte;\r
+                               neededBits -= 8;\r
+                       }\r
+                       if ((int) adler.Value != readAdler) {\r
+                               throw new FormatException("Adler chksum doesn't match: " + (int)adler.Value + " vs. " + readAdler);\r
+                       }\r
+                       mode = FINISHED;\r
+                       return false;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decodes the deflated stream.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// false if more input is needed, or if finished.\r
+               /// </returns>\r
+               /// <exception cref="System.FormatException">\r
+               /// DataFormatException, if deflated stream is invalid.\r
+               /// </exception>\r
+               private bool Decode()\r
+               {\r
+                       switch (mode) {\r
+                               case DECODE_HEADER:\r
+                                       return DecodeHeader();\r
+                               case DECODE_DICT:\r
+                                       return DecodeDict();\r
+                               case DECODE_CHKSUM:\r
+                                       return DecodeChksum();\r
+                               \r
+                               case DECODE_BLOCKS:\r
+                                       if (isLastBlock) {\r
+                                               if (nowrap) {\r
+                                                       mode = FINISHED;\r
+                                                       return false;\r
+                                               } else {\r
+                                                       input.SkipToByteBoundary();\r
+                                                       neededBits = 32;\r
+                                                       mode = DECODE_CHKSUM;\r
+                                                       return true;\r
+                                               }\r
+                                       }\r
+                                       \r
+                                       int type = input.PeekBits(3);\r
+                                       if (type < 0) {\r
+                                               return false;\r
+                                       }\r
+                                       input.DropBits(3);\r
+                                       \r
+                                       if ((type & 1) != 0) {\r
+                                               isLastBlock = true;\r
+                                       }\r
+                                       switch (type >> 1){\r
+                                               case DeflaterConstants.STORED_BLOCK:\r
+                                                       input.SkipToByteBoundary();\r
+                                                       mode = DECODE_STORED_LEN1;\r
+                                                       break;\r
+                                               case DeflaterConstants.STATIC_TREES:\r
+                                                       litlenTree = InflaterHuffmanTree.defLitLenTree;\r
+                                                       distTree = InflaterHuffmanTree.defDistTree;\r
+                                                       mode = DECODE_HUFFMAN;\r
+                                                       break;\r
+                                               case DeflaterConstants.DYN_TREES:\r
+                                                       dynHeader = new InflaterDynHeader();\r
+                                                       mode = DECODE_DYN_HEADER;\r
+                                                       break;\r
+                                               default:\r
+                                                       throw new FormatException("Unknown block type "+type);\r
+                                       }\r
+                                       return true;\r
+                               \r
+                               case DECODE_STORED_LEN1: \r
+                               {\r
+                                       if ((uncomprLen = input.PeekBits(16)) < 0) {\r
+                                               return false;\r
+                                       }\r
+                                       input.DropBits(16);\r
+                                       mode = DECODE_STORED_LEN2;\r
+                               }\r
+                                       goto case DECODE_STORED_LEN2; /* fall through */\r
+                               case DECODE_STORED_LEN2: \r
+                               {\r
+                                       int nlen = input.PeekBits(16);\r
+                                       if (nlen < 0) {\r
+                                               return false;\r
+                                       }\r
+                                       input.DropBits(16);\r
+                                       if (nlen != (uncomprLen ^ 0xffff)) {\r
+                                               throw new FormatException("broken uncompressed block");\r
+                                       }\r
+                                       mode = DECODE_STORED;\r
+                               }\r
+                                       goto case DECODE_STORED;/* fall through */\r
+                               case DECODE_STORED: \r
+                               {\r
+                                       int more = outputWindow.CopyStored(input, uncomprLen);\r
+                                       uncomprLen -= more;\r
+                                       if (uncomprLen == 0) {\r
+                                               mode = DECODE_BLOCKS;\r
+                                               return true;\r
+                                       }\r
+                                       return !input.IsNeedingInput;\r
+                               }\r
+                               \r
+                               case DECODE_DYN_HEADER:\r
+                                       if (!dynHeader.Decode(input)) {\r
+                                               return false;\r
+                                       }\r
+                                       \r
+                                       litlenTree = dynHeader.BuildLitLenTree();\r
+                                       distTree = dynHeader.BuildDistTree();\r
+                                       mode = DECODE_HUFFMAN;\r
+                                       goto case DECODE_HUFFMAN; /* fall through */\r
+                               case DECODE_HUFFMAN:\r
+                               case DECODE_HUFFMAN_LENBITS:\r
+                               case DECODE_HUFFMAN_DIST:\r
+                               case DECODE_HUFFMAN_DISTBITS:\r
+                                       return DecodeHuffman();\r
+                               case FINISHED:\r
+                                       return false;\r
+                               default:\r
+                                       throw new FormatException();\r
+                       }\r
+               }\r
+                       \r
+               /// <summary>\r
+               /// Sets the preset dictionary.  This should only be called, if\r
+               /// needsDictionary() returns true and it should set the same\r
+               /// dictionary, that was used for deflating.  The getAdler()\r
+               /// function returns the checksum of the dictionary needed.\r
+               /// </summary>\r
+               /// <param name="buffer">\r
+               /// the dictionary.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if no dictionary is needed.\r
+               /// </exception>\r
+               /// <exception cref="System.ArgumentException">\r
+               /// if the dictionary checksum is wrong.\r
+               /// </exception>\r
+               public void SetDictionary(byte[] buffer)\r
+               {\r
+                       SetDictionary(buffer, 0, buffer.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the preset dictionary.  This should only be called, if\r
+               /// needsDictionary() returns true and it should set the same\r
+               /// dictionary, that was used for deflating.  The getAdler()\r
+               /// function returns the checksum of the dictionary needed.\r
+               /// </summary>\r
+               /// <param name="buffer">\r
+               /// the dictionary.\r
+               /// </param>\r
+               /// <param name="off">\r
+               /// the offset into buffer where the dictionary starts.\r
+               /// </param>\r
+               /// <param name="len">\r
+               /// the length of the dictionary.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if no dictionary is needed.\r
+               /// </exception>\r
+               /// <exception cref="System.ArgumentException">\r
+               /// if the dictionary checksum is wrong.\r
+               /// </exception>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">\r
+               /// if the off and/or len are wrong.\r
+               /// </exception>\r
+               public void SetDictionary(byte[] buffer, int off, int len)\r
+               {\r
+                       if (!IsNeedingDictionary) {\r
+                               throw new InvalidOperationException();\r
+                       }\r
+                       \r
+                       adler.Update(buffer, off, len);\r
+                       if ((int)adler.Value != readAdler) {\r
+                               throw new ArgumentException("Wrong adler checksum");\r
+                       }\r
+                       adler.Reset();\r
+                       outputWindow.CopyDict(buffer, off, len);\r
+                       mode = DECODE_BLOCKS;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the input.  This should only be called, if needsInput()\r
+               /// returns true.\r
+               /// </summary>\r
+               /// <param name="buf">\r
+               /// the input.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if no input is needed.\r
+               /// </exception>\r
+               public void SetInput(byte[] buf)\r
+               {\r
+                       SetInput(buf, 0, buf.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Sets the input.  This should only be called, if needsInput()\r
+               /// returns true.\r
+               /// </summary>\r
+               /// <param name="buf">\r
+               /// the input.\r
+               /// </param>\r
+               /// <param name="off">\r
+               /// the offset into buffer where the input starts.\r
+               /// </param>\r
+               /// <param name="len">\r
+               /// the length of the input.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if no input is needed.\r
+               /// </exception>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">\r
+               /// if the off and/or len are wrong.\r
+               /// </exception>\r
+               public void SetInput(byte[] buf, int off, int len)\r
+               {\r
+                       input.SetInput(buf, off, len);\r
+                       totalIn += len;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Inflates the compressed stream to the output buffer.  If this\r
+               /// returns 0, you should check, whether needsDictionary(),\r
+               /// needsInput() or finished() returns true, to determine why no\r
+               /// further output is produced.\r
+               /// </summary>\r
+               /// <param name = "buf">\r
+               /// the output buffer.\r
+               /// </param>\r
+               /// <returns>\r
+               /// the number of bytes written to the buffer, 0 if no further\r
+               /// output can be produced.\r
+               /// </returns>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">\r
+               /// if buf has length 0.\r
+               /// </exception>\r
+               /// <exception cref="System.FormatException">\r
+               /// if deflated stream is invalid.\r
+               /// </exception>\r
+               public int Inflate(byte[] buf)\r
+               {\r
+                       return Inflate(buf, 0, buf.Length);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Inflates the compressed stream to the output buffer.  If this\r
+               /// returns 0, you should check, whether needsDictionary(),\r
+               /// needsInput() or finished() returns true, to determine why no\r
+               /// further output is produced.\r
+               /// </summary>\r
+               /// <param name = "buf">\r
+               /// the output buffer.\r
+               /// </param>\r
+               /// <param name = "off">\r
+               /// the offset into buffer where the output should start.\r
+               /// </param>\r
+               /// <param name = "len">\r
+               /// the maximum length of the output.\r
+               /// </param>\r
+               /// <returns>\r
+               /// the number of bytes written to the buffer, 0 if no further output can be produced.\r
+               /// </returns>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">\r
+               /// if len is &lt;= 0.\r
+               /// </exception>\r
+               /// <exception cref="System.ArgumentOutOfRangeException">\r
+               /// if the off and/or len are wrong.\r
+               /// </exception>\r
+               /// <exception cref="System.FormatException">\r
+               /// if deflated stream is invalid.\r
+               /// </exception>\r
+               public int Inflate(byte[] buf, int off, int len)\r
+               {\r
+                       if (len < 0) {\r
+                               throw new ArgumentOutOfRangeException("len < 0");\r
+                       }\r
+                       // Special case: len may be zero\r
+                       if (len == 0) {\r
+                               if (IsFinished == false) {// -jr- 08-Nov-2003 INFLATE_BUG fix..\r
+                                       Decode();\r
+                               }\r
+                               return 0;\r
+                       }\r
+                       /*                      // Check for correct buff, off, len triple\r
+                                               if (off < 0 || off + len >= buf.Length) {\r
+                                                       throw new ArgumentException("off/len outside buf bounds");\r
+                                               }*/\r
+                       int count = 0;\r
+                       int more;\r
+                       do {\r
+                               if (mode != DECODE_CHKSUM) {\r
+                                       /* Don't give away any output, if we are waiting for the\r
+                                       * checksum in the input stream.\r
+                                       *\r
+                                       * With this trick we have always:\r
+                                       *   needsInput() and not finished()\r
+                                       *   implies more output can be produced.\r
+                                       */\r
+                                       more = outputWindow.CopyOutput(buf, off, len);\r
+                                       adler.Update(buf, off, more);\r
+                                       off += more;\r
+                                       count += more;\r
+                                       totalOut += more;\r
+                                       len -= more;\r
+                                       if (len == 0) {\r
+                                               return count;\r
+                                       }\r
+                               }\r
+                       } while (Decode() || (outputWindow.GetAvailable() > 0 && mode != DECODE_CHKSUM));\r
+                       return count;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Returns true, if the input buffer is empty.\r
+               /// You should then call setInput(). \r
+               /// NOTE: This method also returns true when the stream is finished.\r
+               /// </summary>\r
+               public bool IsNeedingInput {\r
+                       get {\r
+                               return input.IsNeedingInput;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Returns true, if a preset dictionary is needed to inflate the input.\r
+               /// </summary>\r
+               public bool IsNeedingDictionary {\r
+                       get {\r
+                               return mode == DECODE_DICT && neededBits == 0;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Returns true, if the inflater has finished.  This means, that no\r
+               /// input is needed and no output can be produced.\r
+               /// </summary>\r
+               public bool IsFinished {\r
+                       get {\r
+                               return mode == FINISHED && outputWindow.GetAvailable() == 0;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the adler checksum.  This is either the checksum of all\r
+               /// uncompressed bytes returned by inflate(), or if needsDictionary()\r
+               /// returns true (and thus no output was yet produced) this is the\r
+               /// adler checksum of the expected dictionary.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the adler checksum.\r
+               /// </returns>\r
+               public int Adler {\r
+                       get {\r
+                               return IsNeedingDictionary ? readAdler : (int) adler.Value;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the total number of output bytes returned by inflate().\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the total number of output bytes.\r
+               /// </returns>\r
+               public int TotalOut {\r
+                       get {\r
+                               return totalOut;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the total number of processed compressed input bytes.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the total number of bytes of processed input bytes.\r
+               /// </returns>\r
+               public int TotalIn {\r
+                       get {\r
+                               return totalIn - RemainingInput;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the number of unprocessed input.  Useful, if the end of the\r
+               /// stream is reached and you want to further process the bytes after\r
+               /// the deflate stream.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the number of bytes of the input which were not processed.\r
+               /// </returns>\r
+               public int RemainingInput {\r
+                       get {\r
+                               return input.AvailableBytes;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/InflaterDynHeader.cs b/irc/TechBot/Compression/InflaterDynHeader.cs
new file mode 100644 (file)
index 0000000..653c784
--- /dev/null
@@ -0,0 +1,207 @@
+// InflaterDynHeader.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+//\r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       class InflaterDynHeader\r
+       {\r
+               const int LNUM   = 0;\r
+               const int DNUM   = 1;\r
+               const int BLNUM  = 2;\r
+               const int BLLENS = 3;\r
+               const int LENS   = 4;\r
+               const int REPS   = 5;\r
+               \r
+               static readonly int[] repMin  = { 3, 3, 11 };\r
+               static readonly int[] repBits = { 2, 3,  7 };\r
+               \r
+               byte[] blLens;\r
+               byte[] litdistLens;\r
+               \r
+               InflaterHuffmanTree blTree;\r
+               \r
+               int mode;\r
+               int lnum, dnum, blnum, num;\r
+               int repSymbol;\r
+               byte lastLen;\r
+               int ptr;\r
+               \r
+               static readonly int[] BL_ORDER = \r
+               { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };\r
+               \r
+               public InflaterDynHeader()\r
+               {\r
+               }\r
+               \r
+               public bool Decode(StreamManipulator input)\r
+               {\r
+                       decode_loop:\r
+                               for (;;) {\r
+                                       switch (mode) {\r
+                                               case LNUM:\r
+                                                       lnum = input.PeekBits(5);\r
+                                                       if (lnum < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                                       lnum += 257;\r
+                                                       input.DropBits(5);\r
+                                                       //          System.err.println("LNUM: "+lnum);\r
+                                                       mode = DNUM;\r
+                                                       goto case DNUM; // fall through\r
+                                               case DNUM:\r
+                                                       dnum = input.PeekBits(5);\r
+                                                       if (dnum < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                                       dnum++;\r
+                                                       input.DropBits(5);\r
+                                                       //          System.err.println("DNUM: "+dnum);\r
+                                                       num = lnum+dnum;\r
+                                                       litdistLens = new byte[num];\r
+                                                       mode = BLNUM;\r
+                                                       goto case BLNUM; // fall through\r
+                                               case BLNUM:\r
+                                                       blnum = input.PeekBits(4);\r
+                                                       if (blnum < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                                       blnum += 4;\r
+                                                       input.DropBits(4);\r
+                                                       blLens = new byte[19];\r
+                                                       ptr = 0;\r
+                                                       //          System.err.println("BLNUM: "+blnum);\r
+                                                       mode = BLLENS;\r
+                                                       goto case BLLENS; // fall through\r
+                                               case BLLENS:\r
+                                                       while (ptr < blnum) {\r
+                                                               int len = input.PeekBits(3);\r
+                                                               if (len < 0) {\r
+                                                                       return false;\r
+                                                               }\r
+                                                               input.DropBits(3);\r
+                                                               //              System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);\r
+                                                               blLens[BL_ORDER[ptr]] = (byte) len;\r
+                                                               ptr++;\r
+                                                       }\r
+                                                       blTree = new InflaterHuffmanTree(blLens);\r
+                                                       blLens = null;\r
+                                                       ptr = 0;\r
+                                                       mode = LENS;\r
+                                                       goto case LENS; // fall through\r
+                                               case LENS: \r
+                                               {\r
+                                                       int symbol;\r
+                                                       while (((symbol = blTree.GetSymbol(input)) & ~15) == 0) {\r
+                                                               /* Normal case: symbol in [0..15] */\r
+                                                       \r
+                                                               //                System.err.println("litdistLens["+ptr+"]: "+symbol);\r
+                                                               litdistLens[ptr++] = lastLen = (byte)symbol;\r
+                                                       \r
+                                                               if (ptr == num) {\r
+                                                                       /* Finished */\r
+                                                                       return true;\r
+                                                               }\r
+                                                       }\r
+                                               \r
+                                                       /* need more input ? */\r
+                                                       if (symbol < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                               \r
+                                                       /* otherwise repeat code */\r
+                                                       if (symbol >= 17) {\r
+                                                               /* repeat zero */\r
+                                                               //                System.err.println("repeating zero");\r
+                                                               lastLen = 0;\r
+                                                       } else {\r
+                                                               if (ptr == 0) {\r
+                                                                       throw new Exception();\r
+                                                               }\r
+                                                       }\r
+                                                       repSymbol = symbol-16;\r
+                                               }\r
+                                                       mode = REPS;\r
+                                                       goto case REPS; // fall through\r
+                                               case REPS:\r
+                                               {\r
+                                                       int bits = repBits[repSymbol];\r
+                                                       int count = input.PeekBits(bits);\r
+                                                       if (count < 0) {\r
+                                                               return false;\r
+                                                       }\r
+                                                       input.DropBits(bits);\r
+                                                       count += repMin[repSymbol];\r
+                                                       //            System.err.println("litdistLens repeated: "+count);\r
+                                                       \r
+                                                       if (ptr + count > num) {\r
+                                                               throw new Exception();\r
+                                                       }\r
+                                                       while (count-- > 0) {\r
+                                                               litdistLens[ptr++] = lastLen;\r
+                                                       }\r
+                                                       \r
+                                                       if (ptr == num) {\r
+                                                               /* Finished */\r
+                                                               return true;\r
+                                                       }\r
+                                               }\r
+                                                       mode = LENS;\r
+                                                       goto decode_loop;\r
+                                       }\r
+                               }\r
+               }\r
+               \r
+               public InflaterHuffmanTree BuildLitLenTree()\r
+               {\r
+                       byte[] litlenLens = new byte[lnum];\r
+                       Array.Copy(litdistLens, 0, litlenLens, 0, lnum);\r
+                       return new InflaterHuffmanTree(litlenLens);\r
+               }\r
+               \r
+               public InflaterHuffmanTree BuildDistTree()\r
+               {\r
+                       byte[] distLens = new byte[dnum];\r
+                       Array.Copy(litdistLens, lnum, distLens, 0, dnum);\r
+                       return new InflaterHuffmanTree(distLens);\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/InflaterHuffmanTree.cs b/irc/TechBot/Compression/InflaterHuffmanTree.cs
new file mode 100644 (file)
index 0000000..02beb60
--- /dev/null
@@ -0,0 +1,213 @@
+// InflaterHuffmanTree.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       public class InflaterHuffmanTree \r
+       {\r
+               private static int MAX_BITLEN = 15;\r
+               private short[] tree;\r
+               \r
+               public static InflaterHuffmanTree defLitLenTree, defDistTree;\r
+               \r
+               static InflaterHuffmanTree()\r
+               {\r
+                       try {\r
+                               byte[] codeLengths = new byte[288];\r
+                               int i = 0;\r
+                               while (i < 144) {\r
+                                       codeLengths[i++] = 8;\r
+                               }\r
+                               while (i < 256) {\r
+                                       codeLengths[i++] = 9;\r
+                               }\r
+                               while (i < 280) {\r
+                                       codeLengths[i++] = 7;\r
+                               }\r
+                               while (i < 288) {\r
+                                       codeLengths[i++] = 8;\r
+                               }\r
+                               defLitLenTree = new InflaterHuffmanTree(codeLengths);\r
+                               \r
+                               codeLengths = new byte[32];\r
+                               i = 0;\r
+                               while (i < 32) {\r
+                                       codeLengths[i++] = 5;\r
+                               }\r
+                               defDistTree = new InflaterHuffmanTree(codeLengths);\r
+                       } catch (Exception) {\r
+                               throw new ApplicationException("InflaterHuffmanTree: static tree length illegal");\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Constructs a Huffman tree from the array of code lengths.\r
+               /// </summary>\r
+               /// <param name = "codeLengths">\r
+               /// the array of code lengths\r
+               /// </param>\r
+               public InflaterHuffmanTree(byte[] codeLengths)\r
+               {\r
+                       BuildTree(codeLengths);\r
+               }\r
+               \r
+               private void BuildTree(byte[] codeLengths)\r
+               {\r
+                       int[] blCount  = new int[MAX_BITLEN + 1];\r
+                       int[] nextCode = new int[MAX_BITLEN + 1];\r
+                       \r
+                       for (int i = 0; i < codeLengths.Length; i++) {\r
+                               int bits = codeLengths[i];\r
+                               if (bits > 0) {\r
+                                       blCount[bits]++;\r
+                               }\r
+                       }\r
+                       \r
+                       int code = 0;\r
+                       int treeSize = 512;\r
+                       for (int bits = 1; bits <= MAX_BITLEN; bits++) {\r
+                               nextCode[bits] = code;\r
+                               code += blCount[bits] << (16 - bits);\r
+                               if (bits >= 10) {\r
+                                       /* We need an extra table for bit lengths >= 10. */\r
+                                       int start = nextCode[bits] & 0x1ff80;\r
+                                       int end   = code & 0x1ff80;\r
+                                       treeSize += (end - start) >> (16 - bits);\r
+                               }\r
+                       }\r
+/* -jr comment this out! doesnt work for dynamic trees and pkzip 2.04g\r
+                       if (code != 65536) \r
+                       {\r
+                               throw new Exception("Code lengths don't add up properly.");\r
+                       }\r
+*/\r
+                       /* Now create and fill the extra tables from longest to shortest\r
+                       * bit len.  This way the sub trees will be aligned.\r
+                       */\r
+                       tree = new short[treeSize];\r
+                       int treePtr = 512;\r
+                       for (int bits = MAX_BITLEN; bits >= 10; bits--) {\r
+                               int end   = code & 0x1ff80;\r
+                               code -= blCount[bits] << (16 - bits);\r
+                               int start = code & 0x1ff80;\r
+                               for (int i = start; i < end; i += 1 << 7) {\r
+                                       tree[DeflaterHuffman.BitReverse(i)] = (short) ((-treePtr << 4) | bits);\r
+                                       treePtr += 1 << (bits-9);\r
+                               }\r
+                       }\r
+                       \r
+                       for (int i = 0; i < codeLengths.Length; i++) {\r
+                               int bits = codeLengths[i];\r
+                               if (bits == 0) {\r
+                                       continue;\r
+                               }\r
+                               code = nextCode[bits];\r
+                               int revcode = DeflaterHuffman.BitReverse(code);\r
+                               if (bits <= 9) {\r
+                                       do {\r
+                                               tree[revcode] = (short) ((i << 4) | bits);\r
+                                               revcode += 1 << bits;\r
+                                       } while (revcode < 512);\r
+                               } else {\r
+                                       int subTree = tree[revcode & 511];\r
+                                       int treeLen = 1 << (subTree & 15);\r
+                                       subTree = -(subTree >> 4);\r
+                                       do {\r
+                                               tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);\r
+                                               revcode += 1 << bits;\r
+                                       } while (revcode < treeLen);\r
+                               }\r
+                               nextCode[bits] = code + (1 << (16 - bits));\r
+                       }\r
+                       \r
+               }\r
+               \r
+               /// <summary>\r
+               /// Reads the next symbol from input.  The symbol is encoded using the\r
+               /// huffman tree.\r
+               /// </summary>\r
+               /// <param name="input">\r
+               /// input the input source.\r
+               /// </param>\r
+               /// <returns>\r
+               /// the next symbol, or -1 if not enough input is available.\r
+               /// </returns>\r
+               public int GetSymbol(StreamManipulator input)\r
+               {\r
+                       int lookahead, symbol;\r
+                       if ((lookahead = input.PeekBits(9)) >= 0) {\r
+                               if ((symbol = tree[lookahead]) >= 0) {\r
+                                       input.DropBits(symbol & 15);\r
+                                       return symbol >> 4;\r
+                               }\r
+                               int subtree = -(symbol >> 4);\r
+                               int bitlen = symbol & 15;\r
+                               if ((lookahead = input.PeekBits(bitlen)) >= 0) {\r
+                                       symbol = tree[subtree | (lookahead >> 9)];\r
+                                       input.DropBits(symbol & 15);\r
+                                       return symbol >> 4;\r
+                               } else {\r
+                                       int bits = input.AvailableBits;\r
+                                       lookahead = input.PeekBits(bits);\r
+                                       symbol = tree[subtree | (lookahead >> 9)];\r
+                                       if ((symbol & 15) <= bits) {\r
+                                               input.DropBits(symbol & 15);\r
+                                               return symbol >> 4;\r
+                                       } else {\r
+                                               return -1;\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               int bits = input.AvailableBits;\r
+                               lookahead = input.PeekBits(bits);\r
+                               symbol = tree[lookahead];\r
+                               if (symbol >= 0 && (symbol & 15) <= bits) {\r
+                                       input.DropBits(symbol & 15);\r
+                                       return symbol >> 4;\r
+                               } else {\r
+                                       return -1;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
diff --git a/irc/TechBot/Compression/PendingBuffer.cs b/irc/TechBot/Compression/PendingBuffer.cs
new file mode 100644 (file)
index 0000000..e0bc96b
--- /dev/null
@@ -0,0 +1,210 @@
+// PendingBuffer.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression \r
+{\r
+       \r
+       /// <summary>\r
+       /// This class is general purpose class for writing data to a buffer.\r
+       /// \r
+       /// It allows you to write bits as well as bytes\r
+       /// Based on DeflaterPending.java\r
+       /// \r
+       /// author of the original java version : Jochen Hoenicke\r
+       /// </summary>\r
+       public class PendingBuffer\r
+       {\r
+               protected byte[] buf;\r
+               int    start;\r
+               int    end;\r
+               \r
+               uint    bits;\r
+               int    bitCount;\r
+               \r
+               public PendingBuffer() : this( 4096 )\r
+               {\r
+                       \r
+               }\r
+               \r
+               public PendingBuffer(int bufsize)\r
+               {\r
+                       buf = new byte[bufsize];\r
+               }\r
+               \r
+               public void Reset() \r
+               {\r
+                       start = end = bitCount = 0;\r
+               }\r
+               \r
+               public void WriteByte(int b)\r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       buf[end++] = (byte) b;\r
+               }\r
+               \r
+               public void WriteShort(int s)\r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       buf[end++] = (byte) s;\r
+                       buf[end++] = (byte) (s >> 8);\r
+               }\r
+               \r
+               public void WriteInt(int s)\r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       buf[end++] = (byte) s;\r
+                       buf[end++] = (byte) (s >> 8);\r
+                       buf[end++] = (byte) (s >> 16);\r
+                       buf[end++] = (byte) (s >> 24);\r
+               }\r
+               \r
+               public void WriteBlock(byte[] block, int offset, int len)\r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       System.Array.Copy(block, offset, buf, end, len);\r
+                       end += len;\r
+               }\r
+               \r
+               public int BitCount {\r
+                       get {\r
+                               return bitCount;\r
+                       }\r
+               }\r
+               \r
+               public void AlignToByte() \r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       if (bitCount > 0) {\r
+                               buf[end++] = (byte) bits;\r
+                               if (bitCount > 8) {\r
+                                       buf[end++] = (byte) (bits >> 8);\r
+                               }\r
+                       }\r
+                       bits = 0;\r
+                       bitCount = 0;\r
+               }\r
+               \r
+               public void WriteBits(int b, int count)\r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       //                      if (DeflaterConstants.DEBUGGING) {\r
+                       //                              //Console.WriteLine("writeBits("+b+","+count+")");\r
+                       //                      }\r
+                       bits |= (uint)(b << bitCount);\r
+                       bitCount += count;\r
+                       if (bitCount >= 16) {\r
+                               buf[end++] = (byte) bits;\r
+                               buf[end++] = (byte) (bits >> 8);\r
+                               bits >>= 16;\r
+                               bitCount -= 16;\r
+                       }\r
+               }\r
+               \r
+               public void WriteShortMSB(int s) \r
+               {\r
+                       if (DeflaterConstants.DEBUGGING && start != 0) {\r
+                               throw new Exception();\r
+                       }\r
+                       buf[end++] = (byte) (s >> 8);\r
+                       buf[end++] = (byte) s;\r
+               }\r
+               \r
+               public bool IsFlushed {\r
+                       get {\r
+                               return end == 0;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Flushes the pending buffer into the given output array.  If the\r
+               /// output array is to small, only a partial flush is done.\r
+               /// </summary>\r
+               /// <param name="output">\r
+               /// the output array;\r
+               /// </param>\r
+               /// <param name="offset">\r
+               /// the offset into output array;\r
+               /// </param>\r
+               /// <param name="length">               \r
+               /// length the maximum number of bytes to store;\r
+               /// </param>\r
+               /// <exception name="ArgumentOutOfRangeException">\r
+               /// IndexOutOfBoundsException if offset or length are invalid.\r
+               /// </exception>\r
+               public int Flush(byte[] output, int offset, int length) \r
+               {\r
+                       if (bitCount >= 8) {\r
+                               buf[end++] = (byte) bits;\r
+                               bits >>= 8;\r
+                               bitCount -= 8;\r
+                       }\r
+                       if (length > end - start) {\r
+                               length = end - start;\r
+                               System.Array.Copy(buf, start, output, offset, length);\r
+                               start = 0;\r
+                               end = 0;\r
+                       } else {\r
+                               System.Array.Copy(buf, start, output, offset, length);\r
+                               start += length;\r
+                       }\r
+                       return length;\r
+               }\r
+               \r
+               public byte[] ToByteArray()\r
+               {\r
+                       byte[] ret = new byte[end - start];\r
+                       System.Array.Copy(buf, start, ret, 0, ret.Length);\r
+                       start = 0;\r
+                       end = 0;\r
+                       return ret;\r
+               }\r
+       }\r
+}      \r
diff --git a/irc/TechBot/Compression/Streams/DeflaterOutputStream.cs b/irc/TechBot/Compression/Streams/DeflaterOutputStream.cs
new file mode 100644 (file)
index 0000000..dd8c658
--- /dev/null
@@ -0,0 +1,379 @@
+// DeflaterOutputStream.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+using System.IO;\r
+using ICSharpCode.SharpZipLib.Checksums;\r
+using ICSharpCode.SharpZipLib.Zip.Compression;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams \r
+{\r
+\r
+       /// <summary>\r
+       /// This is a special FilterOutputStream deflating the bytes that are\r
+       /// written through it.  It uses the Deflater for deflating.\r
+       /// \r
+       /// authors of the original java version : Tom Tromey, Jochen Hoenicke \r
+       /// </summary>\r
+       public class DeflaterOutputStream : Stream\r
+       {\r
+               /// <summary>\r
+               /// This buffer is used temporarily to retrieve the bytes from the\r
+               /// deflater and write them to the underlying output stream.\r
+               /// </summary>\r
+               protected byte[] buf;\r
+               \r
+               /// <summary>\r
+               /// The deflater which is used to deflate the stream.\r
+               /// </summary>\r
+               protected Deflater def;\r
+               \r
+               /// <summary>\r
+               /// base stream the deflater depends on.\r
+               /// </summary>\r
+               protected Stream baseOutputStream;\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override bool CanRead {\r
+                       get {\r
+                               return baseOutputStream.CanRead;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override bool CanSeek {\r
+                       get {\r
+                               return false;\r
+//                             return baseOutputStream.CanSeek;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override bool CanWrite {\r
+                       get {\r
+                               return baseOutputStream.CanWrite;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override long Length {\r
+                       get {\r
+                               return baseOutputStream.Length;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override long Position {\r
+                       get {\r
+                               return baseOutputStream.Position;\r
+                       }\r
+                       set {\r
+                               baseOutputStream.Position = value;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override long Seek(long offset, SeekOrigin origin)\r
+               {\r
+                       throw new NotSupportedException("Seek not supported"); // -jr- 01-Dec-2003\r
+//                     return baseOutputStream.Seek(offset, origin);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override void SetLength(long val)\r
+               {\r
+                       baseOutputStream.SetLength(val);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override int ReadByte()\r
+               {\r
+                       return baseOutputStream.ReadByte();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override int Read(byte[] b, int off, int len)\r
+               {\r
+                       return baseOutputStream.Read(b, off, len);\r
+               }\r
+               // -jr- 01-Dec-2003\r
+               public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)\r
+               {\r
+                       throw new NotSupportedException("Asynch read not currently supported");\r
+               }\r
+               \r
+               // -jr- 01-Dec-2003\r
+               public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)\r
+               {\r
+                       throw new NotSupportedException("Asynch write not currently supported");\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Deflates everything in the def's input buffers.  This will call\r
+               /// <code>def.deflate()</code> until all bytes from the input buffers\r
+               /// are processed.\r
+               /// </summary>\r
+               protected void Deflate()\r
+               {\r
+                       while (!def.IsNeedingInput) {\r
+                               int len = def.Deflate(buf, 0, buf.Length);\r
+                               \r
+                               //      System.err.println("DOS deflated " + len + " baseOutputStream of " + buf.length);\r
+                               if (len <= 0) {\r
+                                       break;\r
+                               }\r
+                               baseOutputStream.Write(buf, 0, len);\r
+                       }\r
+                       \r
+                       if (!def.IsNeedingInput) {\r
+                               throw new ApplicationException("Can't deflate all input?");\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.\r
+               /// </summary>\r
+               /// <param name="baseOutputStream">\r
+               /// the output stream where deflated output should be written.\r
+               /// </param>\r
+               public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512)\r
+               {\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new DeflaterOutputStream with the given Deflater and\r
+               /// default buffer size.\r
+               /// </summary>\r
+               /// <param name="baseOutputStream">\r
+               /// the output stream where deflated output should be written.\r
+               /// </param>\r
+               /// <param name="defl">\r
+               /// the underlying deflater.\r
+               /// </param>\r
+               public DeflaterOutputStream(Stream baseOutputStream, Deflater defl) :this(baseOutputStream, defl, 512)\r
+               {\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Creates a new DeflaterOutputStream with the given Deflater and\r
+               /// buffer size.\r
+               /// </summary>\r
+               /// <param name="baseOutputStream">\r
+               /// the output stream where deflated output should be written.\r
+               /// </param>\r
+               /// <param name="defl">\r
+               /// the underlying deflater.\r
+               /// </param>\r
+               /// <param name="bufsize">\r
+               /// the buffer size.\r
+               /// </param>\r
+               /// <exception cref="System.InvalidOperationException">\r
+               /// if bufsize isn't positive.\r
+               /// </exception>\r
+               public DeflaterOutputStream(Stream baseOutputStream, Deflater defl, int bufsize)\r
+               {\r
+                       this.baseOutputStream = baseOutputStream;\r
+                       if (bufsize <= 0) {\r
+                               throw new InvalidOperationException("bufsize <= 0");\r
+                       }\r
+                       buf = new byte[bufsize];\r
+                       def = defl;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Flushes the stream by calling flush() on the deflater and then\r
+               /// on the underlying stream.  This ensures that all bytes are\r
+               /// flushed.\r
+               /// </summary>\r
+               public override void Flush()\r
+               {\r
+                       def.Flush();\r
+                       Deflate();\r
+                       baseOutputStream.Flush();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Finishes the stream by calling finish() on the deflater. \r
+               /// </summary>\r
+               public virtual void Finish()\r
+               {\r
+                       def.Finish();\r
+                       while (!def.IsFinished)  {\r
+                               int len = def.Deflate(buf, 0, buf.Length);\r
+                               if (len <= 0) {\r
+                                       break;\r
+                               }\r
+                               \r
+                               // kidnthrain encryption alteration\r
+                               if (this.Password != null) {\r
+                                       // plain data has been deflated. Now encrypt result\r
+                                       this.EncryptBlock(buf, 0, len);\r
+                               }\r
+                               \r
+                               baseOutputStream.Write(buf, 0, len);\r
+                       }\r
+                       if (!def.IsFinished) {\r
+                               throw new ApplicationException("Can't deflate all input?");\r
+                       }\r
+                       baseOutputStream.Flush();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Calls finish () and closes the stream.\r
+               /// </summary>\r
+               public override void Close()\r
+               {\r
+                       Finish();\r
+                       baseOutputStream.Close();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Writes a single byte to the compressed output stream.\r
+               /// </summary>\r
+               /// <param name="bval">\r
+               /// the byte value.\r
+               /// </param>\r
+               public override void WriteByte(byte bval)\r
+               {\r
+                       byte[] b = new byte[1];\r
+                       b[0] = (byte) bval;\r
+                       Write(b, 0, 1);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Writes a len bytes from an array to the compressed stream.\r
+               /// </summary>\r
+               /// <param name="buf">\r
+               /// the byte array.\r
+               /// </param>\r
+               /// <param name="off">\r
+               /// the offset into the byte array where to start.\r
+               /// </param>\r
+               /// <param name="len">\r
+               /// the number of bytes to write.\r
+               /// </param>\r
+               public override void Write(byte[] buf, int off, int len)\r
+               {\r
+                       //    System.err.println("DOS with off " + off + " and len " + len);\r
+                       def.SetInput(buf, off, len);\r
+                       Deflate();\r
+               }\r
+               \r
+               #region Encryption\r
+               string password = null;\r
+               uint[] keys     = null;\r
+               \r
+               public string Password {\r
+                       get { \r
+                               return password; \r
+                       }\r
+                       set { \r
+                               password = value; \r
+                       }\r
+               }\r
+               \r
+               \r
+               //The beauty of xor-ing bits is that\r
+               //plain ^ key = enc\r
+               //and enc ^ key = plain\r
+               //accordingly, this is the exact same as the decrypt byte\r
+               //function in InflaterInputStream\r
+               protected byte EncryptByte()\r
+               {\r
+                       uint temp = ((keys[2] & 0xFFFF) | 2);\r
+                       return (byte)((temp * (temp ^ 1)) >> 8);\r
+               }\r
+               \r
+               \r
+               /// <summary>\r
+               /// Takes a buffer of data and uses the keys\r
+               /// that have been previously initialized from a\r
+               /// password and then updated via a random encryption header\r
+               /// to encrypt that data\r
+               /// </summary>\r
+               protected void EncryptBlock(byte[] buf, int off, int len)\r
+               {\r
+                       for (int i = off; i < off + len; ++i) {\r
+                               byte oldbyte = buf[i];\r
+                               buf[i] ^= EncryptByte();\r
+                               UpdateKeys(oldbyte);\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Initializes our encryption keys using a given password\r
+               /// </summary>\r
+               protected void InitializePassword(string password) {\r
+                       keys = new uint[] {\r
+                               0x12345678,\r
+                               0x23456789,\r
+                               0x34567890\r
+                       };\r
+                       \r
+                       for (int i = 0; i < password.Length; ++i) {\r
+                               UpdateKeys((byte)password[i]);\r
+                       }\r
+               }\r
+               \r
+               protected void UpdateKeys(byte ch)\r
+               {\r
+                       keys[0] = Crc32.ComputeCrc32(keys[0], ch);\r
+                       keys[1] = keys[1] + (byte)keys[0];\r
+                       keys[1] = keys[1] * 134775813 + 1;\r
+                       keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));\r
+               }\r
+               #endregion\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Streams/InflaterInputStream.cs b/irc/TechBot/Compression/Streams/InflaterInputStream.cs
new file mode 100644 (file)
index 0000000..9317e5e
--- /dev/null
@@ -0,0 +1,386 @@
+// InflaterInputStream.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+using System.IO;\r
+\r
+using ICSharpCode.SharpZipLib.Zip.Compression;\r
+using ICSharpCode.SharpZipLib.Checksums;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams \r
+{\r
+       \r
+       /// <summary>\r
+       /// This filter stream is used to decompress data compressed baseInputStream the "deflate"\r
+       /// format. The "deflate" format is described baseInputStream RFC 1951.\r
+       ///\r
+       /// This stream may form the basis for other decompression filters, such\r
+       /// as the <code>GzipInputStream</code>.\r
+       ///\r
+       /// author of the original java version : John Leuner\r
+       /// </summary>\r
+       public class InflaterInputStream : Stream\r
+       {\r
+               //Variables\r
+               \r
+               /// <summary>\r
+               /// Decompressor for this filter\r
+               /// </summary>\r
+               protected Inflater inf;\r
+               \r
+               /// <summary>\r
+               /// Byte array used as a buffer\r
+               /// </summary>\r
+               protected byte[] buf;\r
+               \r
+               /// <summary>\r
+               /// Size of buffer\r
+               /// </summary>\r
+               protected int len;\r
+               \r
+               //We just use this if we are decoding one byte at a time with the read() call\r
+               private byte[] onebytebuffer = new byte[1];\r
+               \r
+               /// <summary>\r
+               /// base stream the inflater depends on.\r
+               /// </summary>\r
+               protected Stream baseInputStream;\r
+               \r
+               protected long csize;\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override bool CanRead {\r
+                       get {\r
+                               return baseInputStream.CanRead;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override bool CanSeek {\r
+                       get {\r
+                               return false;\r
+                               //                              return baseInputStream.CanSeek;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override bool CanWrite {\r
+                       get {\r
+                               return baseInputStream.CanWrite;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override long Length {\r
+                       get {\r
+                               return len;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override long Position {\r
+                       get {\r
+                               return baseInputStream.Position;\r
+                       }\r
+                       set {\r
+                               baseInputStream.Position = value;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Flushes the baseInputStream\r
+               /// </summary>\r
+               public override void Flush()\r
+               {\r
+                       baseInputStream.Flush();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override long Seek(long offset, SeekOrigin origin)\r
+               {\r
+                       throw new NotSupportedException("Seek not supported"); // -jr- 01-Dec-2003\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override void SetLength(long val)\r
+               {\r
+                       baseInputStream.SetLength(val);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override void Write(byte[] array, int offset, int count)\r
+               {\r
+                       baseInputStream.Write(array, offset, count);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// I needed to implement the abstract member.\r
+               /// </summary>\r
+               public override void WriteByte(byte val)\r
+               {\r
+                       baseInputStream.WriteByte(val);\r
+               }\r
+               \r
+               // -jr- 01-Dec-2003 This may be flawed for some base streams?  Depends on implementation of BeginWrite\r
+               public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)\r
+               {\r
+                       throw new NotSupportedException("Asynch write not currently supported");\r
+               }\r
+               \r
+               //Constructors\r
+               \r
+               /// <summary>\r
+               /// Create an InflaterInputStream with the default decompresseor\r
+               /// and a default buffer size.\r
+               /// </summary>\r
+               /// <param name = "baseInputStream">\r
+               /// the InputStream to read bytes from\r
+               /// </param>\r
+               public InflaterInputStream(Stream baseInputStream) : this(baseInputStream, new Inflater(), 4096)\r
+               {\r
+                       \r
+               }\r
+               \r
+               /// <summary>\r
+               /// Create an InflaterInputStream with the specified decompresseor\r
+               /// and a default buffer size.\r
+               /// </summary>\r
+               /// <param name = "baseInputStream">\r
+               /// the InputStream to read bytes from\r
+               /// </param>\r
+               /// <param name = "inf">\r
+               /// the decompressor used to decompress data read from baseInputStream\r
+               /// </param>\r
+               public InflaterInputStream(Stream baseInputStream, Inflater inf) : this(baseInputStream, inf, 4096)\r
+               {\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Create an InflaterInputStream with the specified decompresseor\r
+               /// and a specified buffer size.\r
+               /// </summary>\r
+               /// <param name = "baseInputStream">\r
+               /// the InputStream to read bytes from\r
+               /// </param>\r
+               /// <param name = "inf">\r
+               /// the decompressor used to decompress data read from baseInputStream\r
+               /// </param>\r
+               /// <param name = "size">\r
+               /// size of the buffer to use\r
+               /// </param>\r
+               public InflaterInputStream(Stream baseInputStream, Inflater inf, int size)\r
+               {\r
+                       this.baseInputStream = baseInputStream;\r
+                       this.inf = inf;\r
+                       try {\r
+                               this.len = (int)baseInputStream.Length;\r
+                       } catch (Exception) {\r
+                               // the stream may not support .Length\r
+                               this.len = 0;\r
+                       }\r
+                       \r
+                       if (size <= 0) {\r
+                               throw new ArgumentOutOfRangeException("size <= 0");\r
+                       }\r
+                       \r
+                       buf = new byte[size]; //Create the buffer\r
+               }\r
+               \r
+               //Methods\r
+               \r
+               /// <summary>\r
+               /// Returns 0 once the end of the stream (EOF) has been reached.\r
+               /// Otherwise returns 1.\r
+               /// </summary>\r
+               public virtual int Available {\r
+                       get {\r
+                               return inf.IsFinished ? 0 : 1;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Closes the input stream\r
+               /// </summary>\r
+               public override void Close()\r
+               {\r
+                       baseInputStream.Close();\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Fills the buffer with more data to decompress.\r
+               /// </summary>\r
+               protected void Fill()\r
+               {\r
+                       len = baseInputStream.Read(buf, 0, buf.Length);\r
+                       // decrypting crypted data\r
+                       if (cryptbuffer != null) {\r
+                               DecryptBlock(buf, 0, System.Math.Min((int)(csize - inf.TotalIn), buf.Length));\r
+                       }\r
+                       \r
+                       if (len <= 0) {\r
+                               throw new ApplicationException("Deflated stream ends early.");\r
+                       }\r
+                       inf.SetInput(buf, 0, len);\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Reads one byte of decompressed data.\r
+               ///\r
+               /// The byte is baseInputStream the lower 8 bits of the int.\r
+               /// </summary>\r
+               public override int ReadByte()\r
+               {\r
+                       int nread = Read(onebytebuffer, 0, 1); //read one byte\r
+                       if (nread > 0) {\r
+                               return onebytebuffer[0] & 0xff;\r
+                       }\r
+                       return -1; // ok\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Decompresses data into the byte array\r
+               /// </summary>\r
+               /// <param name ="b">\r
+               /// the array to read and decompress data into\r
+               /// </param>\r
+               /// <param name ="off">\r
+               /// the offset indicating where the data should be placed\r
+               /// </param>\r
+               /// <param name ="len">\r
+               /// the number of bytes to decompress\r
+               /// </param>\r
+               public override int Read(byte[] b, int off, int len)\r
+               {\r
+                       for (;;) {\r
+                               int count;\r
+                               try {\r
+                                       count = inf.Inflate(b, off, len);\r
+                               } catch (Exception e) {\r
+                                       throw new ZipException(e.ToString());\r
+                               }\r
+                               \r
+                               if (count > 0) {\r
+                                       return count;\r
+                               }\r
+                               \r
+                               if (inf.IsNeedingDictionary) {\r
+                                       throw new ZipException("Need a dictionary");\r
+                               } else if (inf.IsFinished) {\r
+                                       return 0;\r
+                               } else if (inf.IsNeedingInput) {\r
+                                       Fill();\r
+                               } else {\r
+                                       throw new InvalidOperationException("Don't know what to do");\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Skip specified number of bytes of uncompressed data\r
+               /// </summary>\r
+               /// <param name ="n">\r
+               /// number of bytes to skip\r
+               /// </param>\r
+               public long Skip(long n)\r
+               {\r
+                       if (n < 0) {\r
+                               throw new ArgumentOutOfRangeException("n");\r
+                       }\r
+                       int len = 2048;\r
+                       if (n < len) {\r
+                               len = (int) n;\r
+                       }\r
+                       byte[] tmp = new byte[len];\r
+                       return (long)baseInputStream.Read(tmp, 0, tmp.Length);\r
+               }\r
+               \r
+               #region Encryption stuff\r
+               protected byte[] cryptbuffer = null;\r
+               \r
+               uint[] keys = null;\r
+               protected byte DecryptByte()\r
+               {\r
+                       uint temp = ((keys[2] & 0xFFFF) | 2);\r
+                       return (byte)((temp * (temp ^ 1)) >> 8);\r
+               }\r
+               \r
+               protected void DecryptBlock(byte[] buf, int off, int len)\r
+               {\r
+                       for (int i = off; i < off + len; ++i) {\r
+                               buf[i] ^= DecryptByte();\r
+                               UpdateKeys(buf[i]);\r
+                       }\r
+               }\r
+               \r
+               protected void InitializePassword(string password)\r
+               {\r
+                       keys = new uint[] {\r
+                               0x12345678,\r
+                               0x23456789,\r
+                               0x34567890\r
+                       };\r
+                       for (int i = 0; i < password.Length; ++i) {\r
+                               UpdateKeys((byte)password[i]);\r
+                       }\r
+               }\r
+               \r
+               protected void UpdateKeys(byte ch)\r
+               {\r
+                       keys[0] = Crc32.ComputeCrc32(keys[0], ch);\r
+                       keys[1] = keys[1] + (byte)keys[0];\r
+                       keys[1] = keys[1] * 134775813 + 1;\r
+                       keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));\r
+               }\r
+               #endregion\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Streams/OutputWindow.cs b/irc/TechBot/Compression/Streams/OutputWindow.cs
new file mode 100644 (file)
index 0000000..426c1f7
--- /dev/null
@@ -0,0 +1,176 @@
+// OutputWindow.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams \r
+{\r
+       \r
+       /// <summary>\r
+       /// Contains the output from the Inflation process.\r
+       /// We need to have a window so that we can refer backwards into the output stream\r
+       /// to repeat stuff.\r
+       ///\r
+       /// author of the original java version : John Leuner\r
+       /// </summary>\r
+       public class OutputWindow\r
+       {\r
+               private static int WINDOW_SIZE = 1 << 15;\r
+               private static int WINDOW_MASK = WINDOW_SIZE - 1;\r
+               \r
+               private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes\r
+               private int windowEnd  = 0;\r
+               private int windowFilled = 0;\r
+               \r
+               public void Write(int abyte)\r
+               {\r
+                       if (windowFilled++ == WINDOW_SIZE) {\r
+                               throw new InvalidOperationException("Window full");\r
+                       }\r
+                       window[windowEnd++] = (byte) abyte;\r
+                       windowEnd &= WINDOW_MASK;\r
+               }\r
+               \r
+               \r
+               private void SlowRepeat(int repStart, int len, int dist)\r
+               {\r
+                       while (len-- > 0) {\r
+                               window[windowEnd++] = window[repStart++];\r
+                               windowEnd &= WINDOW_MASK;\r
+                               repStart &= WINDOW_MASK;\r
+                       }\r
+               }\r
+               \r
+               public void Repeat(int len, int dist)\r
+               {\r
+                       if ((windowFilled += len) > WINDOW_SIZE) {\r
+                               throw new InvalidOperationException("Window full");\r
+                       }\r
+                       \r
+                       int rep_start = (windowEnd - dist) & WINDOW_MASK;\r
+                       int border = WINDOW_SIZE - len;\r
+                       if (rep_start <= border && windowEnd < border) {\r
+                               if (len <= dist) {\r
+                                       System.Array.Copy(window, rep_start, window, windowEnd, len);\r
+                                       windowEnd += len;\r
+                               } else {\r
+                                       /* We have to copy manually, since the repeat pattern overlaps.\r
+                                       */\r
+                                       while (len-- > 0) {\r
+                                               window[windowEnd++] = window[rep_start++];\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               SlowRepeat(rep_start, len, dist);\r
+                       }\r
+               }\r
+               \r
+               public int CopyStored(StreamManipulator input, int len)\r
+               {\r
+                       len = Math.Min(Math.Min(len, WINDOW_SIZE - windowFilled), input.AvailableBytes);\r
+                       int copied;\r
+                       \r
+                       int tailLen = WINDOW_SIZE - windowEnd;\r
+                       if (len > tailLen) {\r
+                               copied = input.CopyBytes(window, windowEnd, tailLen);\r
+                               if (copied == tailLen) {\r
+                                       copied += input.CopyBytes(window, 0, len - tailLen);\r
+                               }\r
+                       } else {\r
+                               copied = input.CopyBytes(window, windowEnd, len);\r
+                       }\r
+                       \r
+                       windowEnd = (windowEnd + copied) & WINDOW_MASK;\r
+                       windowFilled += copied;\r
+                       return copied;\r
+               }\r
+               \r
+               public void CopyDict(byte[] dict, int offset, int len)\r
+               {\r
+                       if (windowFilled > 0) {\r
+                               throw new InvalidOperationException();\r
+                       }\r
+                       \r
+                       if (len > WINDOW_SIZE) {\r
+                               offset += len - WINDOW_SIZE;\r
+                               len = WINDOW_SIZE;\r
+                       }\r
+                       System.Array.Copy(dict, offset, window, 0, len);\r
+                       windowEnd = len & WINDOW_MASK;\r
+               }\r
+               \r
+               public int GetFreeSpace()\r
+               {\r
+                       return WINDOW_SIZE - windowFilled;\r
+               }\r
+               \r
+               public int GetAvailable()\r
+               {\r
+                       return windowFilled;\r
+               }\r
+               \r
+               public int CopyOutput(byte[] output, int offset, int len)\r
+               {\r
+                       int copy_end = windowEnd;\r
+                       if (len > windowFilled) {\r
+                               len = windowFilled;\r
+                       } else {\r
+                               copy_end = (windowEnd - windowFilled + len) & WINDOW_MASK;\r
+                       }\r
+                       \r
+                       int copied = len;\r
+                       int tailLen = len - copy_end;\r
+                       \r
+                       if (tailLen > 0) {\r
+                               System.Array.Copy(window, WINDOW_SIZE - tailLen, output, offset, tailLen);\r
+                               offset += tailLen;\r
+                               len = copy_end;\r
+                       }\r
+                       System.Array.Copy(window, copy_end - len, output, offset, len);\r
+                       windowFilled -= copied;\r
+                       if (windowFilled < 0) {\r
+                               throw new InvalidOperationException();\r
+                       }\r
+                       return copied;\r
+               }\r
+               \r
+               public void Reset()\r
+               {\r
+                       windowFilled = windowEnd = 0;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/Streams/StreamManipulator.cs b/irc/TechBot/Compression/Streams/StreamManipulator.cs
new file mode 100644 (file)
index 0000000..35f98cc
--- /dev/null
@@ -0,0 +1,245 @@
+// StreamManipulator.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams \r
+{\r
+       \r
+       /// <summary>\r
+       /// This class allows us to retrieve a specified amount of bits from\r
+       /// the input buffer, as well as copy big byte blocks.\r
+       ///\r
+       /// It uses an int buffer to store up to 31 bits for direct\r
+       /// manipulation.  This guarantees that we can get at least 16 bits,\r
+       /// but we only need at most 15, so this is all safe.\r
+       ///\r
+       /// There are some optimizations in this class, for example, you must\r
+       /// never peek more then 8 bits more than needed, and you must first\r
+       /// peek bits before you may drop them.  This is not a general purpose\r
+       /// class but optimized for the behaviour of the Inflater.\r
+       ///\r
+       /// authors of the original java version : John Leuner, Jochen Hoenicke\r
+       /// </summary>\r
+       public class StreamManipulator\r
+       {\r
+               private byte[] window;\r
+               private int window_start = 0;\r
+               private int window_end = 0;\r
+               \r
+               private uint buffer = 0;\r
+               private int bits_in_buffer = 0;\r
+               \r
+               /// <summary>\r
+               /// Get the next n bits but don't increase input pointer.  n must be\r
+               /// less or equal 16 and if you if this call succeeds, you must drop\r
+               /// at least n-8 bits in the next call.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the value of the bits, or -1 if not enough bits available.  */\r
+               /// </returns>\r
+               public int PeekBits(int n)\r
+               {\r
+                       if (bits_in_buffer < n) {\r
+                               if (window_start == window_end) {\r
+                                       return -1; // ok\r
+                               }\r
+                               buffer |= (uint)((window[window_start++] & 0xff |\r
+                                                (window[window_start++] & 0xff) << 8) << bits_in_buffer);\r
+                               bits_in_buffer += 16;\r
+                       }\r
+                       return (int)(buffer & ((1 << n) - 1));\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Drops the next n bits from the input.  You should have called peekBits\r
+               /// with a bigger or equal n before, to make sure that enough bits are in\r
+               /// the bit buffer.\r
+               /// </summary>\r
+               public void DropBits(int n)\r
+               {\r
+                       buffer >>= n;\r
+                       bits_in_buffer -= n;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the next n bits and increases input pointer.  This is equivalent\r
+               /// to peekBits followed by dropBits, except for correct error handling.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the value of the bits, or -1 if not enough bits available.\r
+               /// </returns>\r
+               public int GetBits(int n)\r
+               {\r
+                       int bits = PeekBits(n);\r
+                       if (bits >= 0) {\r
+                               DropBits(n);\r
+                       }\r
+                       return bits;\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the number of bits available in the bit buffer.  This must be\r
+               /// only called when a previous peekBits() returned -1.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the number of bits available.\r
+               /// </returns>\r
+               public int AvailableBits {\r
+                       get {\r
+                               return bits_in_buffer;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Gets the number of bytes available.\r
+               /// </summary>\r
+               /// <returns>\r
+               /// the number of bytes available.\r
+               /// </returns>\r
+               public int AvailableBytes {\r
+                       get {\r
+                               return window_end - window_start + (bits_in_buffer >> 3);\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Skips to the next byte boundary.\r
+               /// </summary>\r
+               public void SkipToByteBoundary()\r
+               {\r
+                       buffer >>= (bits_in_buffer & 7);\r
+                       bits_in_buffer &= ~7;\r
+               }\r
+               \r
+               public bool IsNeedingInput {\r
+                       get {\r
+                               return window_start == window_end;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Copies length bytes from input buffer to output buffer starting\r
+               /// at output[offset].  You have to make sure, that the buffer is\r
+               /// byte aligned.  If not enough bytes are available, copies fewer\r
+               /// bytes.\r
+               /// </summary>\r
+               /// <param name="output">\r
+               /// the buffer.\r
+               /// </param>\r
+               /// <param name="offset">\r
+               /// the offset in the buffer.\r
+               /// </param>\r
+               /// <param name="length">\r
+               /// the length to copy, 0 is allowed.\r
+               /// </param>\r
+               /// <returns>\r
+               /// the number of bytes copied, 0 if no byte is available.\r
+               /// </returns>\r
+               public int CopyBytes(byte[] output, int offset, int length)\r
+               {\r
+                       if (length < 0) {\r
+                               throw new ArgumentOutOfRangeException("length negative");\r
+                       }\r
+                       if ((bits_in_buffer & 7) != 0) {\r
+                               /* bits_in_buffer may only be 0 or 8 */\r
+                               throw new InvalidOperationException("Bit buffer is not aligned!");\r
+                       }\r
+                       \r
+                       int count = 0;\r
+                       while (bits_in_buffer > 0 && length > 0) {\r
+                               output[offset++] = (byte) buffer;\r
+                               buffer >>= 8;\r
+                               bits_in_buffer -= 8;\r
+                               length--;\r
+                               count++;\r
+                       }\r
+                       if (length == 0) {\r
+                               return count;\r
+                       }\r
+                       \r
+                       int avail = window_end - window_start;\r
+                       if (length > avail) {\r
+                               length = avail;\r
+                       }\r
+                       System.Array.Copy(window, window_start, output, offset, length);\r
+                       window_start += length;\r
+                       \r
+                       if (((window_start - window_end) & 1) != 0) {\r
+                               /* We always want an even number of bytes in input, see peekBits */\r
+                               buffer = (uint)(window[window_start++] & 0xff);\r
+                               bits_in_buffer = 8;\r
+                       }\r
+                       return count + length;\r
+               }\r
+               \r
+               public StreamManipulator()\r
+               {\r
+               }\r
+               \r
+               public void Reset()\r
+               {\r
+                       buffer = (uint)(window_start = window_end = bits_in_buffer = 0);\r
+               }\r
+               \r
+               public void SetInput(byte[] buf, int off, int len)\r
+               {\r
+                       if (window_start < window_end) {\r
+                               throw new InvalidOperationException("Old input was not completely processed");\r
+                       }\r
+                       \r
+                       int end = off + len;\r
+                       \r
+                       /* We want to throw an ArrayIndexOutOfBoundsException early.  The\r
+                       * check is very tricky: it also handles integer wrap around.\r
+                       */\r
+                       if (0 > off || off > end || end > buf.Length) {\r
+                               throw new ArgumentOutOfRangeException();\r
+                       }\r
+                       \r
+                       if ((len & 1) != 0) {\r
+                               /* We always want an even number of bytes in input, see peekBits */\r
+                               buffer |= (uint)((buf[off++] & 0xff) << bits_in_buffer);\r
+                               bits_in_buffer += 8;\r
+                       }\r
+                       \r
+                       window = buf;\r
+                       window_start = off;\r
+                       window_end = end;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Compression/ZipException.cs b/irc/TechBot/Compression/ZipException.cs
new file mode 100644 (file)
index 0000000..b342592
--- /dev/null
@@ -0,0 +1,62 @@
+// ZipException.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library.  Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module.  An independent module is a module which is not derived from\r
+// or based on this library.  If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so.  If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+\r
+namespace ICSharpCode.SharpZipLib \r
+{\r
+       \r
+       /// <summary>\r
+       /// Is thrown during the creation or input of a zip file.\r
+       /// </summary>\r
+       public class ZipException : Exception\r
+       {\r
+               /// <summary>\r
+               /// Initializes a new instance of the ZipException class with default properties.\r
+               /// </summary>\r
+               public ZipException()\r
+               {\r
+               }\r
+               \r
+               /// <summary>\r
+               /// Initializes a new instance of the ZipException class with a specified error message.\r
+               /// </summary>\r
+               public ZipException(string msg) : base(msg)\r
+               {\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/Default.build b/irc/TechBot/Default.build
new file mode 100644 (file)
index 0000000..62738cf
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>\r
+<project name="TechBot" default="build">\r
+\r
+       <target name="build" description="Build components">\r
+               <delete dir="bin" failonerror="false" />\r
+               <nant buildfile="Compression/Default.build" />\r
+               <nant buildfile="CHMLibrary/Default.build" />\r
+               <nant buildfile="TechBot.IRCLibrary/Default.build" />\r
+               <nant buildfile="TechBot.Library/Default.build" />\r
+               <nant buildfile="TechBot.Console/Default.build" />\r
+               <nant buildfile="TechBot/Default.build" />\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/TechBot.Console/App.config b/irc/TechBot/TechBot.Console/App.config
new file mode 100644 (file)
index 0000000..bdb4e73
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>\r
+<configuration>\r
+       <appSettings>\r
+               <add key="IRCServerHostName" value="irc.eu.freenode.net" />\r
+               <add key="IRCServerHostPort" value="6667" />\r
+               <add key="IRCChannelName" value="channel" />\r
+               <add key="IRCBotName" value="MyBot" />\r
+               <add key="ChmPath" value="C:\IRC\TechBot\CHM" />\r
+               <add key="MainChm" value="kmarch.chm" />\r
+               <add key="NtstatusXml" value="C:\IRC\TechBot\ntstatus.xml" />\r
+               <add key="WinerrorXml" value="C:\IRC\TechBot\winerror.xml" />\r
+               <add key="HresultXml" value="C:\IRC\TechBot\hresult.xml" />\r
+               <add key="SvnCommand" value="svn co svn://svn.reactos.com/trunk/reactos" />\r
+       </appSettings>\r
+</configuration>\r
diff --git a/irc/TechBot/TechBot.Console/AssemblyInfo.cs b/irc/TechBot/TechBot.Console/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..dbacda1
--- /dev/null
@@ -0,0 +1,32 @@
+using System.Reflection;\r
+using System.Runtime.CompilerServices;\r
+\r
+// Information about this assembly is defined by the following\r
+// attributes.\r
+//\r
+// change them to the information which is associated with the assembly\r
+// you compile.\r
+\r
+[assembly: AssemblyTitle("")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("")]\r
+[assembly: AssemblyProduct("")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+\r
+// The assembly version has following format :\r
+//\r
+// Major.Minor.Build.Revision\r
+//\r
+// You can specify all values by your own or you can build default build and revision\r
+// numbers with the '*' character (the default):\r
+\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+// The following attributes specify the key for the sign of your assembly. See the\r
+// .NET Framework documentation for more information about signing.\r
+// This is not required, if you don't want signing let these attributes like they're.\r
+[assembly: AssemblyDelaySign(false)]\r
+[assembly: AssemblyKeyFile("")]\r
diff --git a/irc/TechBot/TechBot.Console/Default.build b/irc/TechBot/TechBot.Console/Default.build
new file mode 100644 (file)
index 0000000..706547f
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>\r
+<project name="TechBot.Console" default="build">\r
+\r
+       <property name="output.dir" value="..\bin" />\r
+\r
+       <target name="build" description="Build component">\r
+               <mkdir dir="${output.dir}" />\r
+               <csc target="exe"\r
+                       output="${output.dir}\TechBot.Console.exe"\r
+                       optimize="true"\r
+                       debug="true"\r
+                       doc="${output.dir}\TechBot.Console.xml"\r
+                       warninglevel="0">\r
+                       <sources>\r
+                               <include name="*.cs" />\r
+                       </sources>\r
+                       <references>\r
+                               <include name="${output.dir}\TechBot.Library.dll" />\r
+                       </references>\r
+               </csc>\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/TechBot.Console/Main.cs b/irc/TechBot/TechBot.Console/Main.cs
new file mode 100644 (file)
index 0000000..9705e67
--- /dev/null
@@ -0,0 +1,187 @@
+using System;\r
+using System.Configuration;\r
+using TechBot.Library;\r
+\r
+namespace TechBot.Console\r
+{\r
+       public class ConsoleServiceOutput : IServiceOutput\r
+       {\r
+               public void WriteLine(string message)\r
+               {\r
+                       System.Console.WriteLine(message);\r
+               }\r
+       }\r
+\r
+       \r
+       class MainClass\r
+       {\r
+               private static void VerifyRequiredOption(string optionName,\r
+                                                        string optionValue)\r
+               {\r
+                       if (optionValue == null)\r
+                       {\r
+                               throw new Exception(String.Format("Option '{0}' not set.",\r
+                                                                 optionName));\r
+                       }\r
+               }\r
+               \r
+               private static string IRCServerHostName\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "IRCServerHostName";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static int IRCServerHostPort\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "IRCServerHostPort";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return Int32.Parse(s);\r
+                       }\r
+               }\r
+\r
+               private static string IRCChannelName\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "IRCChannelName";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static string IRCBotName\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "IRCBotName";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static string ChmPath\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "ChmPath";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+               \r
+               private static string MainChm\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "MainChm";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static string NtstatusXml\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "NtstatusXml";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static string WinerrorXml\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "WinerrorXml";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static string HresultXml\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "HresultXml";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static string SvnCommand\r
+               {\r
+                       get\r
+                       {\r
+                               string optionName = "SvnCommand";\r
+                               string s = ConfigurationSettings.AppSettings[optionName];\r
+                               VerifyRequiredOption(optionName,\r
+                                                    s);\r
+                               return s;\r
+                       }\r
+               }\r
+\r
+               private static void RunIrcService()\r
+               {\r
+                       IrcService ircService = new IrcService(IRCServerHostName,\r
+                                                              IRCServerHostPort,\r
+                                                              IRCChannelName,\r
+                                                              IRCBotName,\r
+                                                              ChmPath,\r
+                                                              MainChm,\r
+                                                              NtstatusXml,\r
+                                                              WinerrorXml,\r
+                                                              HresultXml,\r
+                                                              SvnCommand);\r
+                       ircService.Run();\r
+               }\r
+               \r
+               public static void Main(string[] args)\r
+               {\r
+                       if (args.Length > 0 && args[0].ToLower().Equals("irc"))\r
+                       {\r
+                               RunIrcService();\r
+                               return;\r
+                       }\r
+                       \r
+                       System.Console.WriteLine("TechBot running console service...");\r
+                       TechBotService service = new TechBotService(new ConsoleServiceOutput(),\r
+                                                                   ChmPath,\r
+                                                                   MainChm,\r
+                                                                   NtstatusXml,\r
+                                                                   WinerrorXml,\r
+                                                                   HresultXml,\r
+                                                                   SvnCommand);\r
+                       service.Run();\r
+                       while (true)\r
+                       {\r
+                               string s = System.Console.ReadLine();\r
+                               service.InjectMessage(s);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Console/TechBot.Console.cmbx b/irc/TechBot/TechBot.Console/TechBot.Console.cmbx
new file mode 100644 (file)
index 0000000..0e37e76
--- /dev/null
@@ -0,0 +1,16 @@
+<Combine fileversion="1.0" name="TechBot.Console" description="">\r
+  <StartMode startupentry="TechBot.Console" single="True">\r
+    <Execute entry="TechBot.Console" type="None" />\r
+  </StartMode>\r
+  <Entries>\r
+    <Entry filename=".\.\TechBot.Console.prjx" />\r
+  </Entries>\r
+  <Configurations active="Debug">\r
+    <Configuration name="Release">\r
+      <Entry name="TechBot.Console" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+    <Configuration name="Debug">\r
+      <Entry name="TechBot.Console" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot.Console/TechBot.Console.prjx b/irc/TechBot/TechBot.Console/TechBot.Console.prjx
new file mode 100644 (file)
index 0000000..6a08d66
--- /dev/null
@@ -0,0 +1,29 @@
+<Project name="TechBot.Console" standardNamespace="TechBot.Console" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">\r
+  <Contents>\r
+    <File name=".\Main.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+    <File name=".\App.config" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+  </Contents>\r
+  <References>\r
+    <Reference type="Project" refto="TechBot.Library" localcopy="True" />\r
+  </References>\r
+  <DeploymentInformation target="" script="" strategy="File" />\r
+  <Configuration runwithwarnings="True" name="Debug">\r
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Exe" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+    <Execution commandlineparameters="" consolepause="True" />\r
+    <Output directory="..\bin\Debug" assembly="TechBot.Console" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+  </Configuration>\r
+  <Configurations active="Debug">\r
+    <Configuration runwithwarnings="True" name="Debug">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Exe" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="True" />\r
+      <Output directory="..\bin\Debug" assembly="TechBot.Console" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+    <Configuration runwithwarnings="True" name="Release">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="Exe" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="True" />\r
+      <Output directory="..\bin\Release" assembly="TechBot.Console" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Project>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot.IRCLibrary/AssemblyInfo.cs b/irc/TechBot/TechBot.IRCLibrary/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..dbacda1
--- /dev/null
@@ -0,0 +1,32 @@
+using System.Reflection;\r
+using System.Runtime.CompilerServices;\r
+\r
+// Information about this assembly is defined by the following\r
+// attributes.\r
+//\r
+// change them to the information which is associated with the assembly\r
+// you compile.\r
+\r
+[assembly: AssemblyTitle("")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("")]\r
+[assembly: AssemblyProduct("")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+\r
+// The assembly version has following format :\r
+//\r
+// Major.Minor.Build.Revision\r
+//\r
+// You can specify all values by your own or you can build default build and revision\r
+// numbers with the '*' character (the default):\r
+\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+// The following attributes specify the key for the sign of your assembly. See the\r
+// .NET Framework documentation for more information about signing.\r
+// This is not required, if you don't want signing let these attributes like they're.\r
+[assembly: AssemblyDelaySign(false)]\r
+[assembly: AssemblyKeyFile("")]\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/Default.build b/irc/TechBot/TechBot.IRCLibrary/Default.build
new file mode 100644 (file)
index 0000000..1c468dd
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>\r
+<project name="TechBot.IRCLibrary" default="build">\r
+\r
+       <property name="output.dir" value="..\bin" />\r
+\r
+       <target name="build" description="Build component">\r
+               <mkdir dir="${output.dir}" />\r
+               <csc target="library"\r
+                       output="${output.dir}\TechBot.IRCLibrary.dll"\r
+                       optimize="true"\r
+                       debug="true"\r
+                       doc="${output.dir}\TechBot.IRCLibrary.xml"\r
+                       warninglevel="0">\r
+                       <sources>\r
+                               <include name="*.cs" />\r
+                       </sources>\r
+               </csc>\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/IRC.cs b/irc/TechBot/TechBot.IRCLibrary/IRC.cs
new file mode 100644 (file)
index 0000000..38cb448
--- /dev/null
@@ -0,0 +1,26 @@
+using System;\r
+\r
+namespace TechBot.IRCLibrary\r
+{\r
+       /// <summary>\r
+       /// IRC constants and helper methods.\r
+       /// </summary>\r
+       public class IRC\r
+       {\r
+               #region IRC commands\r
+\r
+               public const string JOIN = "JOIN";\r
+               public const string NICK = "NICK";\r
+               public const string PART = "PART";\r
+               public const string PING = "PING";\r
+               public const string PONG = "PONG";\r
+               public const string PRIVMSG = "PRIVMSG";\r
+               public const string USER = "USER";\r
+\r
+               public const string RPL_NAMREPLY = "353";\r
+               public const string RPL_ENDOFNAMES = "366";\r
+\r
+               #endregion\r
+\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/IrcChannel.cs b/irc/TechBot/TechBot.IRCLibrary/IrcChannel.cs
new file mode 100644 (file)
index 0000000..2f26abb
--- /dev/null
@@ -0,0 +1,134 @@
+/*\r
+   Channels names are strings (beginning with a '&' or '#' character) of\r
+   length up to 200 characters.  Apart from the the requirement that the\r
+   first character being either '&' or '#'; the only restriction on a\r
+   channel name is that it may not contain any spaces (' '), a control G\r
+   (^G or ASCII 7), or a comma (',' which is used as a list item\r
+   separator by the protocol).\r
+ */\r
+using System;\r
+using System.Collections;\r
+\r
+namespace TechBot.IRCLibrary\r
+{\r
+       /// <summary>\r
+       /// IRC channel type.\r
+       /// </summary>\r
+       public enum IrcChannelType\r
+       {\r
+               Public,\r
+               Private,\r
+               Secret\r
+       }\r
+\r
+\r
+\r
+       /// <summary>\r
+       /// IRC channel.\r
+       /// </summary>\r
+       public class IrcChannel\r
+       {\r
+               #region Private fields\r
+\r
+               private IrcClient owner;\r
+               private string name;\r
+               private IrcChannelType type = IrcChannelType.Public;\r
+               private ArrayList users = new ArrayList();\r
+\r
+               #endregion\r
+\r
+               #region Public properties\r
+\r
+               /// <summary>\r
+               /// Owner of this channel.\r
+               /// </summary>\r
+               public IrcClient Owner\r
+               {\r
+                       get\r
+                       {\r
+                               return owner;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Name of channel (no leading #).\r
+               /// </summary>\r
+               public string Name\r
+               {\r
+                       get\r
+                       {\r
+                               return name;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Type of channel.\r
+               /// </summary>\r
+               public IrcChannelType Type\r
+               {\r
+                       get\r
+                       {\r
+                               return type;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Users in this channel.\r
+               /// </summary>\r
+               public ArrayList Users\r
+               {\r
+                       get\r
+                       {\r
+                               return users;\r
+                       }\r
+               }\r
+\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Constructor.\r
+               /// </summary>\r
+               /// <param name="owner">Owner of this channel.</param>\r
+               /// <param name="name">Name of channel.</param>\r
+               public IrcChannel(IrcClient owner, string name)\r
+               {\r
+                       if (owner == null)\r
+                       {\r
+                               throw new ArgumentNullException("owner", "Owner cannot be null.");\r
+                       }\r
+                       if (name == null)\r
+                       {\r
+                               throw new ArgumentNullException("name", "Name cannot be null.");\r
+                       }\r
+                       this.owner = owner;\r
+                       this.name = name;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Locate a user.\r
+               /// </summary>\r
+               /// <param name="nickname">Nickname of user (no decorations).</param>\r
+               /// <returns>User or null if not found.</returns>\r
+               public IrcUser LocateUser(string nickname)\r
+               {\r
+                       foreach (IrcUser user in Users)\r
+                       {\r
+                               /* FIXME: There are special cases for nickname comparison */\r
+                               if (nickname.ToLower().Equals(user.Nickname.ToLower()))\r
+                               {\r
+                                       return user;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Talk to the channel.\r
+               /// </summary>\r
+               /// <param name="text">Text to send to the channel.</param>\r
+               public void Talk(string text)\r
+               {\r
+                       owner.SendMessage(new IrcMessage(IRC.PRIVMSG, String.Format("#{0} :{1}", name, text)));\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/IrcClient.cs b/irc/TechBot/TechBot.IRCLibrary/IrcClient.cs
new file mode 100644 (file)
index 0000000..b5c1d4b
--- /dev/null
@@ -0,0 +1,654 @@
+using System;\r
+using System.IO;\r
+using System.Text;\r
+using System.Collections;\r
+using System.Net.Sockets;\r
+\r
+namespace TechBot.IRCLibrary\r
+{\r
+       /// <summary>\r
+       /// Delegate that delivers an IRC message.\r
+       /// </summary>\r
+       public delegate void MessageReceivedHandler(IrcMessage message);\r
+\r
+       /// <summary>\r
+       /// Delegate that notifies if the user database for a channel has changed.\r
+       /// </summary>\r
+       public delegate void ChannelUserDatabaseChangedHandler(IrcChannel channel);\r
+\r
+       /// <summary>\r
+       /// An IRC client.\r
+       /// </summary>\r
+       public class IrcClient\r
+       {\r
+               /// <summary>\r
+               /// Monitor when an IRC command is received.\r
+               /// </summary>\r
+               private class IrcCommandEventRegistration\r
+               {\r
+                       /// <summary>\r
+                       /// IRC command to monitor.\r
+                       /// </summary>\r
+                       private string command;\r
+                       public string Command\r
+                       {\r
+                               get\r
+                               {\r
+                                       return command;\r
+                               }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Handler to call when command is received.\r
+                       /// </summary>\r
+                       private MessageReceivedHandler handler;\r
+                       public MessageReceivedHandler Handler\r
+                       {\r
+                               get\r
+                               {\r
+                                       return handler;\r
+                               }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Constructor.\r
+                       /// </summary>\r
+                       /// <param name="command">IRC command to monitor.</param>\r
+                       /// <param name="handler">Handler to call when command is received.</param>\r
+                       public IrcCommandEventRegistration(string command,\r
+                               MessageReceivedHandler handler)\r
+                       {\r
+                               this.command = command;\r
+                               this.handler = handler;\r
+                       }\r
+               }\r
+\r
+\r
+\r
+               /// <summary>\r
+               /// A buffer to store lines of text.\r
+               /// </summary>\r
+               private class LineBuffer\r
+               {\r
+                       /// <summary>\r
+                       /// Full lines of text in buffer.\r
+                       /// </summary>\r
+                       private ArrayList strings;\r
+\r
+                       /// <summary>\r
+                       /// Part of the last line of text in buffer.\r
+                       /// </summary>\r
+                       private string left = "";\r
+\r
+                       /// <summary>\r
+                       /// Standard constructor.\r
+                       /// </summary>\r
+                       public LineBuffer()\r
+                       {\r
+                               strings = new ArrayList();\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Return true if there is a complete line in the buffer or false if there is not.\r
+                       /// </summary>\r
+                       public bool DataAvailable\r
+                       {\r
+                               get\r
+                               {\r
+                                       return (strings.Count > 0);\r
+                               }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Return next complete line in buffer or null if none exists.\r
+                       /// </summary>\r
+                       /// <returns>Next complete line in buffer or null if none exists.</returns>\r
+                       public string Read()\r
+                       {\r
+                               if (DataAvailable)\r
+                               {\r
+                                       string line = strings[0] as string;\r
+                                       strings.RemoveAt(0);\r
+                                       return line;\r
+                               }\r
+                               else\r
+                               {\r
+                                       return null;\r
+                               }\r
+                       }\r
+\r
+                       /// <summary>\r
+                       /// Write a string to buffer splitting it into lines.\r
+                       /// </summary>\r
+                       /// <param name="data"></param>\r
+                       public void Write(string data)\r
+                       {\r
+                               data = left + data;\r
+                               left = "";\r
+                               string[] sa = data.Split(new char[] { '\n' });\r
+                               if (sa.Length <= 0)\r
+                               {\r
+                                       left = data;\r
+                                       return;\r
+                               }\r
+                               else\r
+                               {\r
+                                       left = "";\r
+                               }\r
+                               for (int i = 0; i < sa.Length; i++)\r
+                               {\r
+                                       if (i < sa.Length - 1)\r
+                                       {\r
+                                               /* This is a complete line. Remove any \r characters at the end of the line. */\r
+                                               string line = sa[i].TrimEnd(new char[] { '\r', '\n'});\r
+                                               /* Silently ignore empty lines */\r
+                                               if (!line.Equals(String.Empty))\r
+                                               {\r
+                                                       strings.Add(line);\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               /* This may be a partial line. */\r
+                                               left = sa[i];\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+\r
+               /// <summary>\r
+               /// State for asynchronous reads.\r
+               /// </summary>\r
+               private class StateObject\r
+               {\r
+                       /// <summary>\r
+                       /// Network stream where data is read from.\r
+                       /// </summary>\r
+                       public NetworkStream Stream;\r
+\r
+                       /// <summary>\r
+                       /// Buffer where data is put.\r
+                       /// </summary>\r
+                       public byte[] Buffer;\r
+\r
+                       /// <summary>\r
+                       /// Constructor.\r
+                       /// </summary>\r
+                       /// <param name="stream">Network stream where data is read from.</param>\r
+                       /// <param name="buffer">Buffer where data is put.</param>\r
+                       public StateObject(NetworkStream stream, byte[] buffer)\r
+                       {\r
+                               this.Stream = stream;\r
+                               this.Buffer = buffer;\r
+                       }\r
+               }\r
+\r
+\r
+               #region Private fields\r
+               private bool firstPingReceived = false;\r
+               private System.Text.Encoding encoding = System.Text.Encoding.UTF8;\r
+               private TcpClient tcpClient;\r
+               private NetworkStream networkStream;\r
+               private bool connected = false;\r
+               private LineBuffer messageStream;\r
+               private ArrayList ircCommandEventRegistrations = new ArrayList();\r
+               private ArrayList channels = new ArrayList();\r
+               #endregion\r
+\r
+               #region Public events\r
+\r
+               public event MessageReceivedHandler MessageReceived;\r
+\r
+               public event ChannelUserDatabaseChangedHandler ChannelUserDatabaseChanged;\r
+\r
+               #endregion\r
+\r
+               #region Public properties\r
+\r
+               /// <summary>\r
+               /// Encoding used.\r
+               /// </summary>\r
+               public System.Text.Encoding Encoding\r
+               {\r
+                       get\r
+                       {\r
+                               return encoding;\r
+                       }\r
+                       set\r
+                       {\r
+                               encoding = value;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// List of joined channels.\r
+               /// </summary>\r
+               public ArrayList Channels\r
+               {\r
+                       get\r
+                       {\r
+                               return channels;\r
+                       }\r
+               }\r
+\r
+               #endregion\r
+\r
+               #region Private methods\r
+\r
+               /// <summary>\r
+               /// Signal MessageReceived event.\r
+               /// </summary>\r
+               /// <param name="message">Message that was received.</param>\r
+               private void OnMessageReceived(IrcMessage message)\r
+               {\r
+                       foreach (IrcCommandEventRegistration icre in ircCommandEventRegistrations)\r
+                       {\r
+                               if (message.Command.ToLower().Equals(icre.Command.ToLower()))\r
+                               {\r
+                                       icre.Handler(message);\r
+                               }\r
+                       }\r
+                       if (MessageReceived != null)\r
+                       {\r
+                               MessageReceived(message);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Signal ChannelUserDatabaseChanged event.\r
+               /// </summary>\r
+               /// <param name="channel">Message that was received.</param>\r
+               private void OnChannelUserDatabaseChanged(IrcChannel channel)\r
+               {\r
+                       if (ChannelUserDatabaseChanged != null)\r
+                       {\r
+                               ChannelUserDatabaseChanged(channel);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Start an asynchronous read.\r
+               /// </summary>\r
+               private void Receive()\r
+               {\r
+                       if ((networkStream != null) && (networkStream.CanRead))\r
+                       {\r
+                               byte[] buffer = new byte[1024];\r
+                               networkStream.BeginRead(buffer, 0, buffer.Length, \r
+                                       new AsyncCallback(ReadComplete),\r
+                                       new StateObject(networkStream, buffer));\r
+                       }\r
+                       else\r
+                       {\r
+                               throw new Exception("Socket is closed.");\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Asynchronous read has completed.\r
+               /// </summary>\r
+               /// <param name="ar">IAsyncResult object.</param>\r
+               private void ReadComplete(IAsyncResult ar)\r
+               {\r
+                       StateObject stateObject = (StateObject) ar.AsyncState;\r
+                       if (stateObject.Stream.CanRead)\r
+                       {\r
+                               int bytesReceived = stateObject.Stream.EndRead(ar);\r
+                               if (bytesReceived > 0)\r
+                               {\r
+                                       messageStream.Write(Encoding.GetString(stateObject.Buffer, 0, bytesReceived));\r
+                                       while (messageStream.DataAvailable)\r
+                                       {\r
+                                               OnMessageReceived(new IrcMessage(messageStream.Read()));\r
+                                       }\r
+                               }\r
+                       }\r
+                       Receive();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Locate channel.\r
+               /// </summary>\r
+               /// <param name="name">Channel name.</param>\r
+               /// <returns>Channel or null if none was found.</returns>\r
+               private IrcChannel LocateChannel(string name)\r
+               {\r
+                       foreach (IrcChannel channel in Channels)\r
+                       {\r
+                               if (name.ToLower().Equals(channel.Name.ToLower()))\r
+                               {\r
+                                       return channel;\r
+                               }\r
+                       }\r
+                       return null;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Send a PONG message when a PING message is received.\r
+               /// </summary>\r
+               /// <param name="message">Received IRC message.</param>\r
+               private void PingMessageReceived(IrcMessage message)\r
+               {\r
+                       SendMessage(new IrcMessage(IRC.PONG, message.Parameters));\r
+                       firstPingReceived = true;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Process RPL_NAMREPLY message.\r
+               /// </summary>\r
+               /// <param name="message">Received IRC message.</param>\r
+               private void RPL_NAMREPLYMessageReceived(IrcMessage message)\r
+               {\r
+                       try\r
+                       {\r
+                               // :Oslo2.NO.EU.undernet.org 353 E101 = #E101 :E101 KongFu_uK @Exception\r
+                               /* "( "=" / "*" / "@" ) <channel>\r
+                                  :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )\r
+                                       - "@" is used for secret channels, "*" for private\r
+                                       channels, and "=" for others (public channels). */\r
+                               if (message.Parameters == null)\r
+                               {\r
+                                       System.Diagnostics.Debug.WriteLine(String.Format("Message has no parameters."));\r
+                                       return;\r
+                               }\r
+                               string[] parameters = message.Parameters.Split(new char[] { ' '});\r
+                               if (parameters.Length < 5)\r
+                               {\r
+                                       System.Diagnostics.Debug.WriteLine(String.Format("{0} is two few parameters.", parameters.Length));\r
+                                       return;\r
+                               }\r
+                               IrcChannelType type;\r
+                               switch (parameters[1])\r
+                               {\r
+                                       case "=":\r
+                                               type = IrcChannelType.Public;\r
+                                               break;\r
+                                       case "*":\r
+                                               type = IrcChannelType.Private;\r
+                                               break;\r
+                                       case "@":\r
+                                               type = IrcChannelType.Secret;\r
+                                               break;\r
+                                       default:\r
+                                               type = IrcChannelType.Public;\r
+                                               break;\r
+                               }\r
+                               IrcChannel channel = LocateChannel(parameters[2].Substring(1));\r
+                               if (channel == null)\r
+                               {\r
+                                       System.Diagnostics.Debug.WriteLine(String.Format("Channel not found '{0}'.",\r
+                                               parameters[2].Substring(1)));\r
+                                       return;\r
+                               }\r
+                               string nickname = parameters[3];\r
+                               if (nickname[0] != ':')\r
+                               {\r
+                                       System.Diagnostics.Debug.WriteLine(String.Format("String should start with : and not {0}.", nickname[0]));\r
+                                       return;\r
+                               }\r
+                               /* Skip : */\r
+                               IrcUser user = channel.LocateUser(nickname.Substring(1));\r
+                               if (user == null)\r
+                               {\r
+                                       user = new IrcUser(nickname.Substring(1));\r
+                                       channel.Users.Add(user);\r
+                               }\r
+                               for (int i = 4; i < parameters.Length; i++)\r
+                               {\r
+                                       nickname = parameters[i];\r
+                                       user = channel.LocateUser(nickname);\r
+                                       if (user == null)\r
+                                       {\r
+                                               user = new IrcUser(nickname);\r
+                                               channel.Users.Add(user);\r
+                                       }\r
+                               }\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
+                               System.Diagnostics.Debug.WriteLine(String.Format("Ex. {0}", ex));\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Process RPL_ENDOFNAMES message.\r
+               /// </summary>\r
+               /// <param name="message">Received IRC message.</param>\r
+               private void RPL_ENDOFNAMESMessageReceived(IrcMessage message)\r
+               {\r
+                       try\r
+                       {\r
+                               /* <channel> :End of NAMES list */\r
+                               if (message.Parameters == null)\r
+                               {\r
+                                       System.Diagnostics.Debug.WriteLine(String.Format("Message has no parameters."));\r
+                                       return;\r
+                               }\r
+\r
+                               string[] parameters = message.Parameters.Split(new char[] { ' ' });\r
+                               IrcChannel channel = LocateChannel(parameters[1].Substring(1));\r
+                               if (channel == null)\r
+                               {\r
+                                       System.Diagnostics.Debug.WriteLine(String.Format("Channel not found '{0}'.",\r
+                                               parameters[0].Substring(1)));\r
+                                       return;\r
+                               }\r
+\r
+                               OnChannelUserDatabaseChanged(channel);\r
+                       }       \r
+                       catch (Exception ex)\r
+                       {\r
+                               System.Diagnostics.Debug.WriteLine(String.Format("Ex. {0}", ex));\r
+                       }\r
+               }\r
+\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Connect to the specified IRC server on the specified port.\r
+               /// </summary>\r
+               /// <param name="server">Address of IRC server.</param>\r
+               /// <param name="port">Port of IRC server.</param>\r
+               public void Connect(string server, int port)\r
+               {\r
+                       if (connected)\r
+                       {\r
+                               throw new AlreadyConnectedException();\r
+                       }\r
+                       else\r
+                       {\r
+                               messageStream = new LineBuffer();\r
+                               tcpClient = new TcpClient();\r
+                               tcpClient.Connect(server, port);\r
+                               tcpClient.NoDelay = true;\r
+                               tcpClient.LingerState = new LingerOption(false, 0);\r
+                               networkStream = tcpClient.GetStream();\r
+                               connected = networkStream.CanRead && networkStream.CanWrite;\r
+                               if (!connected)\r
+                               {\r
+                                       throw new Exception("Cannot read and write from socket.");\r
+                               }\r
+                               /* Install PING message handler */\r
+                               MonitorCommand(IRC.PING, new MessageReceivedHandler(PingMessageReceived));\r
+                               /* Install RPL_NAMREPLY message handler */\r
+                               MonitorCommand(IRC.RPL_NAMREPLY, new MessageReceivedHandler(RPL_NAMREPLYMessageReceived));\r
+                               /* Install RPL_ENDOFNAMES message handler */\r
+                               MonitorCommand(IRC.RPL_ENDOFNAMES, new MessageReceivedHandler(RPL_ENDOFNAMESMessageReceived));\r
+                               /* Start receiving data */\r
+                               Receive();\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Disconnect from IRC server.\r
+               /// </summary>\r
+               public void Diconnect()\r
+               {\r
+                       if (!connected)\r
+                       {\r
+                               throw new NotConnectedException();\r
+                       }\r
+                       else\r
+                       {\r
+                               \r
+\r
+                               connected = false;\r
+                               tcpClient.Close();\r
+                               tcpClient = null;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Send an IRC message.\r
+               /// </summary>\r
+               /// <param name="message">The message to be sent.</param>\r
+               public void SendMessage(IrcMessage message)\r
+               {\r
+                       if (!connected)\r
+                       {\r
+                               throw new NotConnectedException();\r
+                       }\r
+\r
+                       /* Serialize sending messages */\r
+                       lock (typeof(IrcClient))\r
+                       {\r
+                               NetworkStream networkStream = tcpClient.GetStream();\r
+                               byte[] bytes = Encoding.GetBytes(message.Line);\r
+                               networkStream.Write(bytes, 0, bytes.Length);\r
+                               networkStream.Flush();\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Monitor when a message with an IRC command is received.\r
+               /// </summary>\r
+               /// <param name="command">IRC command to monitor.</param>\r
+               /// <param name="handler">Handler to call when command is received.</param>\r
+               public void MonitorCommand(string command, MessageReceivedHandler handler)\r
+               {\r
+                       if (command == null)\r
+                       {\r
+                               throw new ArgumentNullException("command", "Command cannot be null.");\r
+                       }\r
+                       if (handler == null)\r
+                       {\r
+                               throw new ArgumentNullException("handler", "Handler cannot be null.");\r
+                       }\r
+                       ircCommandEventRegistrations.Add(new IrcCommandEventRegistration(command, handler));\r
+               }\r
+\r
+               /// <summary>\r
+               /// Talk to the channel.\r
+               /// </summary>\r
+               /// <param name="nickname">Nickname of user to talk to.</param>\r
+               /// <param name="text">Text to send to the channel.</param>\r
+               public void TalkTo(string nickname, string text)\r
+               {\r
+                       if (nickname == null)\r
+                       {\r
+                               throw new ArgumentNullException("nickname", "Nickname cannot be null.");\r
+                       }\r
+                       if (text == null)\r
+                       {\r
+                               throw new ArgumentNullException("text", "Text cannot be null.");\r
+                       }\r
+\r
+                       SendMessage(new IrcMessage(IRC.PRIVMSG, String.Format("{0} :{1}", nickname, text)));\r
+               }\r
+\r
+               /// <summary>\r
+               /// Change nickname.\r
+               /// </summary>\r
+               /// <param name="nickname">New nickname.</param>\r
+               public void ChangeNick(string nickname)\r
+               {\r
+                       if (nickname == null)\r
+                       {\r
+                               throw new ArgumentNullException("nickname", "Nickname cannot be null.");\r
+                       }\r
+\r
+                       /* NICK <nickname> [ <hopcount> ] */\r
+                       SendMessage(new IrcMessage(IRC.NICK, nickname));\r
+               }\r
+\r
+               /// <summary>\r
+               /// Register.\r
+               /// </summary>\r
+               /// <param name="nickname">New nickname.</param>\r
+               /// <param name="realname">Real name. Can be null.</param>\r
+               public void Register(string nickname, string realname)\r
+               {\r
+                       if (nickname == null)\r
+                       {\r
+                               throw new ArgumentNullException("nickname", "Nickname cannot be null.");\r
+                       }\r
+                       firstPingReceived = false;\r
+                       ChangeNick(nickname);\r
+                       /* OLD: USER <username> <hostname> <servername> <realname> */\r
+                       /* NEW: USER <user> <mode> <unused> <realname> */\r
+                       SendMessage(new IrcMessage(IRC.USER, String.Format("{0} 0 * :{1}",\r
+                               nickname, realname != null ? realname : "Anonymous")));\r
+\r
+                       /* Wait for PING for up til 10 seconds */\r
+                       int timer = 0;\r
+                       while (!firstPingReceived && timer < 200)\r
+                       {\r
+                               System.Threading.Thread.Sleep(50);\r
+                               timer++;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Join an IRC channel.\r
+               /// </summary>\r
+               /// <param name="name">Name of channel (without leading #).</param>\r
+               /// <returns>New channel.</returns>\r
+               public IrcChannel JoinChannel(string name)\r
+               {\r
+                       IrcChannel channel = new IrcChannel(this, name);\r
+                       channels.Add(channel);\r
+                       /* JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0" */\r
+                       SendMessage(new IrcMessage(IRC.JOIN, String.Format("#{0}", name)));\r
+                       return channel;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Part an IRC channel.\r
+               /// </summary>\r
+               /// <param name="channel">IRC channel. If null, the user parts from all channels.</param>\r
+               /// <param name="message">Part message. Can be null.</param>\r
+               public void PartChannel(IrcChannel channel, string message)\r
+               {\r
+                       /* PART <channel> *( "," <channel> ) [ <Part Message> ] */\r
+                       if (channel != null)\r
+                       {\r
+                               SendMessage(new IrcMessage(IRC.PART, String.Format("#{0}{1}",\r
+                                       channel.Name, message != null ? String.Format(" :{0}", message) : "")));\r
+                               channels.Remove(channel);\r
+                       }\r
+                       else\r
+                       {\r
+                               string channelList = null;\r
+                               foreach (IrcChannel myChannel in Channels)\r
+                               {\r
+                                       if (channelList == null)\r
+                                       {\r
+                                               channelList = "";\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               channelList += ",";\r
+                                       }\r
+                                       channelList += myChannel.Name;\r
+                               }\r
+                               if (channelList != null)\r
+                               {\r
+                                       SendMessage(new IrcMessage(IRC.PART, String.Format("#{0}{1}",\r
+                                               channelList, message != null ? String.Format(" :{0}", message) : "")));\r
+                                       Channels.Clear();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/IrcException.cs b/irc/TechBot/TechBot.IRCLibrary/IrcException.cs
new file mode 100644 (file)
index 0000000..a96b688
--- /dev/null
@@ -0,0 +1,50 @@
+using System;\r
+\r
+namespace TechBot.IRCLibrary\r
+{\r
+       /// <summary>\r
+       /// Base class for all IRC exceptions.\r
+       /// </summary>\r
+       public class IrcException : Exception\r
+       {\r
+               public IrcException() : base()\r
+               {\r
+               }\r
+\r
+               public IrcException(string message) : base(message)\r
+               {\r
+               }\r
+\r
+               public IrcException(string message, Exception innerException) : base(message, innerException)\r
+               {\r
+               }\r
+       }\r
+\r
+       /// <summary>\r
+       /// Thrown when there is no connection to an IRC server.\r
+       /// </summary>\r
+       public class NotConnectedException : IrcException\r
+       {\r
+       }\r
+\r
+       /// <summary>\r
+       /// Thrown when there is an attempt to connect to an IRC server and there is already a connection.\r
+       /// </summary>\r
+       public class AlreadyConnectedException : IrcException\r
+       {\r
+       }\r
+\r
+       /// <summary>\r
+       /// Thrown when there is attempted to parse a malformed or invalid IRC message.\r
+       /// </summary>\r
+       public class MalformedMessageException : IrcException\r
+       {\r
+               public MalformedMessageException(string message) : base(message)\r
+               {\r
+               }\r
+\r
+               public MalformedMessageException(string message, Exception innerException) : base(message, innerException)\r
+               {\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs b/irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs
new file mode 100644 (file)
index 0000000..bab64de
--- /dev/null
@@ -0,0 +1,503 @@
+using System;\r
+\r
+namespace TechBot.IRCLibrary\r
+{\r
+       /*\r
+       <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>\r
+       <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]\r
+       <command>  ::= <letter> { <letter> } | <number> <number> <number>\r
+       <SPACE>    ::= ' ' { ' ' }\r
+       <params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]\r
+\r
+       <middle>   ::= <Any *non-empty* sequence of octets not including SPACE\r
+                                  or NUL or CR or LF, the first of which may not be ':'>\r
+       <trailing> ::= <Any, possibly *empty*, sequence of octets not including\r
+                                        NUL or CR or LF>\r
+\r
+       <crlf>     ::= CR LF\r
+\r
+       NOTES:\r
+\r
+         1)    <SPACE> is consists only of SPACE character(s) (0x20).\r
+                       Specially notice that TABULATION, and all other control\r
+                       characters are considered NON-WHITE-SPACE.\r
+\r
+         2)    After extracting the parameter list, all parameters are equal,\r
+                       whether matched by <middle> or <trailing>. <Trailing> is just\r
+                       a syntactic trick to allow SPACE within parameter.\r
+\r
+         3)    The fact that CR and LF cannot appear in parameter strings is\r
+                       just artifact of the message framing. This might change later.\r
+\r
+         4)    The NUL character is not special in message framing, and\r
+                       basically could end up inside a parameter, but as it would\r
+                       cause extra complexities in normal C string handling. Therefore\r
+                       NUL is not allowed within messages.\r
+\r
+         5)    The last parameter may be an empty string.\r
+\r
+         6)    Use of the extended prefix (['!' <user> ] ['@' <host> ]) must\r
+                       not be used in server to server communications and is only\r
+                       intended for server to client messages in order to provide\r
+                       clients with more useful information about who a message is\r
+                       from without the need for additional queries.\r
+        */\r
+       /*\r
+   NOTICE AUTH :*** Looking up your hostname\r
+   NOTICE AUTH :*** Checking Ident\r
+   NOTICE AUTH :*** Found your hostname\r
+   NOTICE AUTH :*** No ident response\r
+        */\r
+\r
+       /// <summary>\r
+       /// IRC message.\r
+       /// </summary>\r
+       public class IrcMessage\r
+       {\r
+               #region Private fields\r
+               private string line;\r
+               private string prefix;\r
+               private string prefixServername;\r
+               private string prefixNickname;\r
+               private string prefixUser;\r
+               private string prefixHost;\r
+               private string command;\r
+               private string parameters;\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Line of text that is to be parsed as an IRC message.\r
+               /// </summary>\r
+               public string Line\r
+               {\r
+                       get\r
+                       {\r
+                               return line;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Does the message have a prefix?\r
+               /// </summary>\r
+               public bool HasPrefix\r
+               {\r
+                       get\r
+                       {\r
+                               return prefix != null;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Prefix or null if none exists.\r
+               /// </summary>\r
+               public string Prefix\r
+               {\r
+                       get\r
+                       {\r
+                               return prefix;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Servername part of prefix or null if no prefix or servername exists.\r
+               /// </summary>\r
+               public string PrefixServername\r
+               {\r
+                       get\r
+                       {\r
+                               return prefixServername;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Nickname part of prefix or null if no prefix or nick exists.\r
+               /// </summary>\r
+               public string PrefixNickname\r
+               {\r
+                       get\r
+                       {\r
+                               return prefixNickname;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// User part of (extended) prefix or null if no (extended) prefix exists.\r
+               /// </summary>\r
+               public string PrefixUser\r
+               {\r
+                       get\r
+                       {\r
+                               return prefixUser;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Host part of (extended) prefix or null if no (extended) prefix exists.\r
+               /// </summary>\r
+               public string PrefixHost\r
+               {\r
+                       get\r
+                       {\r
+                               return prefixHost;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Command part of message.\r
+               /// </summary>\r
+               public string Command\r
+               {\r
+                       get\r
+                       {\r
+                               return command;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Is command numeric?\r
+               /// </summary>\r
+               public bool IsCommandNumeric\r
+               {\r
+                       get\r
+                       {\r
+                               if (command == null || command.Length != 3)\r
+                               {\r
+                                       return false;\r
+                               }\r
+                               try\r
+                               {\r
+                                       Int32.Parse(command);\r
+                                       return true;\r
+                               }\r
+                               catch (Exception)\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Command part of message as text.\r
+               /// </summary>\r
+               public string CommandText\r
+               {\r
+                       get\r
+                       {\r
+                               return command;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Command part of message as a number.\r
+               /// </summary>\r
+               /// <exception cref="InvalidOperationException">Thrown if IsCommandNumeric returns false.</exception>\r
+               public int CommandNumber\r
+               {\r
+                       get\r
+                       {\r
+                               if (IsCommandNumeric)\r
+                               {\r
+                                       return Int32.Parse(command);\r
+                               }\r
+                               else\r
+                               {\r
+                                       throw new InvalidOperationException();\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Parameters part of message.\r
+               /// </summary>\r
+               public string Parameters\r
+               {\r
+                       get\r
+                       {\r
+                               return parameters;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor.\r
+               /// </summary>\r
+               /// <param name="line">Line of text that is to be parsed as an IRC message.</param>\r
+               public IrcMessage(string line)\r
+               {\r
+                       /*\r
+                        * <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>\r
+                        * <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]\r
+                        * :Oslo1.NO.EU.undernet.org 461 MYNICK USER :Not enough parameters\r
+                        */\r
+                       try\r
+                       {\r
+                               this.line = line;\r
+                               int i = 0;\r
+\r
+                               #region Prefix\r
+                               if (line[i].Equals(':'))\r
+                               {\r
+                                       i++;\r
+                                       prefix = "";\r
+                                       /* This message has a prefix */\r
+                                       string s = "";\r
+                                       while (i < line.Length && line[i] != ' ' && line[i] != '!' && line[i] != '@')\r
+                                       {\r
+                                               s += line[i++];\r
+                                       }\r
+                                       if (IsValidIrcNickname(s))\r
+                                       {\r
+                                               prefixNickname = s;\r
+                                               prefix += prefixNickname;\r
+                                               if (line[i] == '!')\r
+                                               {\r
+                                                       /* This message has an extended prefix */\r
+                                                       i++;\r
+                                                       s = "";\r
+                                                       while (i < line.Length && line[i] != ' ' && line[i] != '@')\r
+                                                       {\r
+                                                               s += line[i];\r
+                                                               i++;\r
+                                                       }\r
+                                                       prefixUser = s;\r
+                                                       prefix += "!" + prefixUser;\r
+                                               }\r
+                                               if (line[i] == '@')\r
+                                               {\r
+                                                       /* This message has a host prefix */\r
+                                                       s = "";\r
+                                                       do\r
+                                                       {\r
+                                                               s += line[++i];\r
+                                                       }\r
+                                                       while (i < line.Length && line[i] != ' ');\r
+                                                       prefixHost = s;\r
+                                                       prefix += "@" + prefixHost;\r
+                                               }\r
+                                       }\r
+                                       else /* Assume it is a servername */\r
+                                       {\r
+                                               prefixServername = s;\r
+                                               prefix += prefixServername;\r
+                                       }\r
+\r
+                                       /* Skip spaces */\r
+                                       while (i < line.Length && line[i] == ' ')\r
+                                       {\r
+                                               i++;\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       prefix = null;\r
+                               }\r
+                               #endregion\r
+\r
+                               #region Command\r
+                               if (Char.IsDigit(line[i]))\r
+                               {\r
+                                       if (!Char.IsDigit(line, i + 1) || !Char.IsDigit(line, i + 2))\r
+                                       {\r
+                                               throw new Exception();\r
+                                       }\r
+                                       command = String.Format("{0}{1}{2}", line[i++], line[i++], line[i++]);\r
+                               }\r
+                               else\r
+                               {\r
+                                       command = "";\r
+                                       while (i < line.Length && Char.IsLetter(line[i]))\r
+                                       {\r
+                                               command += line[i];\r
+                                               i++;\r
+                                       }\r
+                               }\r
+                               #endregion\r
+\r
+                               #region Parameters\r
+                               while (true)\r
+                               {\r
+                                       /* Skip spaces */\r
+                                       while (i < line.Length && line[i] == ' ')\r
+                                       {\r
+                                               i++;\r
+                                       }\r
+                                       if (i < line.Length && line[i].Equals(':'))\r
+                                       {\r
+                                               i++;\r
+\r
+                                               /* Trailing */\r
+                                               while (i < line.Length && line[i] != ' ' && line[i] != '\r' && line[i] != '\n' && line[i] != 0)\r
+                                               {\r
+                                                       if (parameters == null)\r
+                                                       {\r
+                                                               parameters = "";\r
+                                                       }\r
+                                                       parameters += line[i];\r
+                                                       i++;\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               /* Middle */\r
+                                               while (i < line.Length && line[i] != '\r' && line[i] != '\n' && line[i] != 0)\r
+                                               {\r
+                                                       if (parameters == null)\r
+                                                       {\r
+                                                               parameters = "";\r
+                                                       }\r
+                                                       parameters += line[i];\r
+                                                       i++;\r
+                                               }\r
+                                       }\r
+                                       if (i >= line.Length)\r
+                                       {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               #endregion\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
+                               throw new MalformedMessageException("The message is malformed.", ex);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor.\r
+               /// </summary>\r
+               /// <param name="prefixServername"></param>\r
+               /// <param name="prefixNickname"></param>\r
+               /// <param name="prefixUser"></param>\r
+               /// <param name="prefixHost"></param>\r
+               /// <param name="command"></param>\r
+               /// <param name="parameters"></param>\r
+               public IrcMessage(string prefixServername,\r
+                       string prefixNickname,\r
+                       string prefixUser,\r
+                       string prefixHost,\r
+                       string command,\r
+                       string parameters)\r
+               {\r
+                       throw new NotImplementedException();\r
+               }\r
+\r
+               /// <summary>\r
+               /// Constructor.\r
+               /// </summary>\r
+               /// <param name="command">IRC command.</param>\r
+               /// <param name="parameters">IRC command parameters. May be null if there are no parameters.</param>\r
+               public IrcMessage(string command,\r
+                       string parameters)\r
+               {\r
+                       if (command == null || !IsValidIrcCommand(command))\r
+                       {\r
+                               throw new ArgumentException("Command is not a valid IRC command.", "command");\r
+                       }\r
+                       /* An IRC message must not be longer than 512 characters (including terminating CRLF) */\r
+                       int parametersLength = (parameters != null) ? 1 + parameters.Length : 0;\r
+                       if (command.Length + parametersLength > 510)\r
+                       {\r
+                               throw new MalformedMessageException("IRC message cannot be longer than 512 characters.");\r
+                       }\r
+                       this.command = command;\r
+                       this.parameters = parameters;\r
+                       if (parameters != null)\r
+                       {\r
+                               this.line = String.Format("{0} {1}\r\n", command, parameters);\r
+                       }\r
+                       else\r
+                       {\r
+                               this.line = String.Format("{0}\r\n", command);\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Returns wether a string of text is a valid IRC command.\r
+               /// </summary>\r
+               /// <param name="command">The IRC command.</param>\r
+               /// <returns>True, if <c ref="command">command</c> is a valid IRC command, false if not.</returns>\r
+               private static bool IsValidIrcCommand(string command)\r
+               {\r
+                       foreach (char c in command)\r
+                       {\r
+                               if (!Char.IsLetter(c))\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               private const string IrcSpecial = @"-[]\`^{}";\r
+               private const string IrcSpecialNonSpecs = @"_";\r
+\r
+               /// <summary>\r
+               /// Returns wether a character is an IRC special character.\r
+               /// </summary>\r
+               /// <param name="c">Character to test.</param>\r
+               /// <returns>True if the character is an IRC special character, false if not.</returns>\r
+               private static bool IsSpecial(char c)\r
+               {\r
+                       foreach (char validCharacter in IrcSpecial)\r
+                       {\r
+                               if (c.Equals(validCharacter))\r
+                               {\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       foreach (char validCharacter in IrcSpecialNonSpecs)\r
+                       {\r
+                               if (c.Equals(validCharacter))\r
+                               {\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       return false;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Returns wether a string of text is a valid IRC nickname.\r
+               /// </summary>\r
+               /// <param name="nickname">The IRC nickname.</param>\r
+               /// <returns>True, if <c ref="nickname">nickname</c> is a valid IRC nickname, false if not.</returns>\r
+               private static bool IsValidIrcNickname(string nickname)\r
+               {\r
+                       /*\r
+                        * <nick>       ::= <letter> { <letter> | <number> | <special> }\r
+                        * <letter>     ::= 'a' ... 'z' | 'A' ... 'Z'\r
+                        * <number>     ::= '0' ... '9'\r
+                        * <special>    ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'\r
+                        */\r
+                       /* An IRC nicknmame must be 1 - 9 characters in length. We don't care so much if it is larger */\r
+                       if ((nickname.Length < 1) || (nickname.Length > 30))\r
+                       {\r
+                               return false;\r
+                       }\r
+                       /* First character must be a letter. */\r
+                       if (!Char.IsLetter(nickname[0]))\r
+                       {\r
+                               return false;\r
+                       }\r
+                       /* Check the other valid characters for validity. */\r
+                       foreach (char c in nickname)\r
+                       {\r
+                               if (!Char.IsLetter(c) && !Char.IsDigit(c) && !IsSpecial(c))\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               /// <summary>\r
+               /// Write contents to a string.\r
+               /// </summary>\r
+               /// <returns>Contents as a string.</returns>\r
+               public override string ToString()\r
+               {\r
+                       return String.Format("Line({0})Prefix({1})Command({2})Parameters({3})",\r
+                               line, prefix != null ? prefix : "(null)",\r
+                               command != null ? command : "(null)",\r
+                               parameters != null ? parameters : "(null)");\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/IrcUser.cs b/irc/TechBot/TechBot.IRCLibrary/IrcUser.cs
new file mode 100644 (file)
index 0000000..901d74c
--- /dev/null
@@ -0,0 +1,96 @@
+using System;\r
+\r
+namespace TechBot.IRCLibrary\r
+{\r
+       /// <summary>\r
+       /// IRC user.\r
+       /// </summary>\r
+       public class IrcUser\r
+       {\r
+               #region Private fields\r
+\r
+               private string nickname;\r
+               private string decoratedNickname;\r
+\r
+               #endregion\r
+\r
+               #region Public properties\r
+\r
+               /// <summary>\r
+               /// Nickname of user.\r
+               /// </summary>\r
+               public string Nickname\r
+               {\r
+                       get\r
+                       {\r
+                               return nickname;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Decorated nickname of user.\r
+               /// </summary>\r
+               public string DecoratedNickname\r
+               {\r
+                       get\r
+                       {\r
+                               return decoratedNickname;\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Wether user is channel operator.\r
+               /// </summary>\r
+               public bool Operator\r
+               {\r
+                       get\r
+                       {\r
+                               return decoratedNickname.StartsWith("@");\r
+                       }\r
+               }\r
+\r
+               /// <summary>\r
+               /// Wether user has voice.\r
+               /// </summary>\r
+               public bool Voice\r
+               {\r
+                       get\r
+                       {\r
+                               return decoratedNickname.StartsWith("+");\r
+                       }\r
+               }\r
+\r
+               #endregion\r
+\r
+               /// <summary>\r
+               /// Constructor.\r
+               /// </summary>\r
+               /// <param name="nickname">Nickname (possibly decorated) of user.</param>\r
+               public IrcUser(string nickname)\r
+               {\r
+                       this.decoratedNickname = nickname.Trim();\r
+                       this.nickname = StripDecoration(decoratedNickname);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Strip docoration of nickname.\r
+               /// </summary>\r
+               /// <param name="nickname">Possible decorated nickname.</param>\r
+               /// <returns>Undecorated nickname.</returns>\r
+               public static string StripDecoration(string decoratedNickname)\r
+               {\r
+                       if (decoratedNickname.StartsWith("@"))\r
+                       {\r
+                               return decoratedNickname.Substring(1);\r
+                       }\r
+                       else if (decoratedNickname.StartsWith("+"))\r
+                       {\r
+                               return decoratedNickname.Substring(1);\r
+                       }\r
+                       else\r
+                       {\r
+                               return decoratedNickname;\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.cmbx b/irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.cmbx
new file mode 100644 (file)
index 0000000..67b1e60
--- /dev/null
@@ -0,0 +1,16 @@
+<Combine fileversion="1.0" name="TechBot.IRCLibrary" description="">\r
+  <StartMode startupentry="TechBot.IRCLibrary" single="True">\r
+    <Execute entry="TechBot.IRCLibrary" type="None" />\r
+  </StartMode>\r
+  <Entries>\r
+    <Entry filename=".\.\TechBot.IRCLibrary.prjx" />\r
+  </Entries>\r
+  <Configurations active="Debug">\r
+    <Configuration name="Release">\r
+      <Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+    <Configuration name="Debug">\r
+      <Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.prjx b/irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.prjx
new file mode 100644 (file)
index 0000000..457a420
--- /dev/null
@@ -0,0 +1,31 @@
+<Project name="TechBot.IRCLibrary" standardNamespace="TechBot.IRCLibrary" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">\r
+  <Contents>\r
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IrcException.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IRC.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IrcChannel.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IrcMessage.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IrcUser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IrcClient.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+  </Contents>\r
+  <References />\r
+  <DeploymentInformation target="" script="" strategy="File" />\r
+  <Configuration runwithwarnings="True" name="Debug">\r
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+    <Execution commandlineparameters="" consolepause="False" />\r
+    <Output directory="..\bin\Debug" assembly="TechBot.IRCLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+  </Configuration>\r
+  <Configurations active="Debug">\r
+    <Configuration runwithwarnings="True" name="Debug">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Debug" assembly="TechBot.IRCLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+    <Configuration runwithwarnings="True" name="Release">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Release" assembly="TechBot.IRCLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Project>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot.Library/ApiCommand.cs b/irc/TechBot/TechBot.Library/ApiCommand.cs
new file mode 100644 (file)
index 0000000..767a3b2
--- /dev/null
@@ -0,0 +1,319 @@
+using System;\r
+using System.IO;\r
+using System.Data;\r
+using System.Text.RegularExpressions;\r
+using HtmlHelp;\r
+using HtmlHelp.ChmDecoding;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class ApiCommand : BaseCommand, ICommand\r
+       {\r
+               private const bool IsVerbose = false;\r
+\r
+               private HtmlHelpSystem chm;\r
+               private IServiceOutput serviceOutput;\r
+               private string chmPath;\r
+               private string mainChm;\r
+               \r
+               public ApiCommand(IServiceOutput serviceOutput,\r
+                                 string chmPath,\r
+                                 string mainChm)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.chmPath = chmPath;\r
+                       this.mainChm = mainChm;\r
+                       Run();\r
+               }\r
+               \r
+               private void WriteIfVerbose(string message)\r
+               {\r
+                       if (IsVerbose)\r
+                               serviceOutput.WriteLine(message);\r
+               }\r
+\r
+               private void Run()\r
+               {\r
+                       string CHMFilename = Path.Combine(chmPath, mainChm);\r
+                       chm = new HtmlHelpSystem();\r
+                       chm.OpenFile(CHMFilename, null);\r
+                       \r
+                       Console.WriteLine(String.Format("Loaded main CHM: {0}",\r
+                                                       Path.GetFileName(CHMFilename)));\r
+                       foreach (string filename in Directory.GetFiles(chmPath))\r
+                       {\r
+                               if (!Path.GetExtension(filename).ToLower().Equals(".chm"))\r
+                                       continue;\r
+                               if (Path.GetFileName(filename).ToLower().Equals(mainChm))\r
+                                       continue;\r
+\r
+                               Console.WriteLine(String.Format("Loading CHM: {0}",\r
+                                                               Path.GetFileName(filename)));\r
+                               try\r
+                               {\r
+                                       chm.MergeFile(filename);\r
+                               }\r
+                               catch (Exception ex)\r
+                               {\r
+                                       Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",\r
+                                                                       Path.GetFileName(filename),\r
+                                                                       ex));\r
+                               }\r
+                       }\r
+                       Console.WriteLine(String.Format("Loaded {0} CHMs",\r
+                                                       chm.FileList.Length));\r
+               }\r
+               \r
+               public bool CanHandle(string commandName)\r
+               {\r
+                       return CanHandle(commandName,\r
+                                        new string[] { "api" });\r
+               }\r
+               \r
+               public void Handle(string commandName,\r
+                                  string parameters)\r
+               {\r
+                       if (parameters.Trim().Equals(String.Empty))\r
+                               DisplayNoKeyword();\r
+                       else\r
+                               Search(parameters);\r
+               }\r
+               \r
+               public string Help()\r
+               {\r
+                       return "!api <apiname>";\r
+               }\r
+               \r
+               private bool SearchIndex(string keyword)\r
+               {\r
+                       if (chm.HasIndex)\r
+                       {\r
+                               IndexItem item = chm.Index.SearchIndex(keyword,\r
+                                                                      IndexType.KeywordLinks);\r
+                               if (item != null && item.Topics.Count > 0)\r
+                               {\r
+                                       WriteIfVerbose(String.Format("Keyword {0} found in index",\r
+                                                                    item.KeyWord));\r
+                                       IndexTopic indexTopic = item.Topics[0] as IndexTopic;\r
+                                       return DisplayResult(keyword, indexTopic);\r
+                               }\r
+                               else\r
+                               {\r
+                                       WriteIfVerbose(String.Format("Keyword {0} not found in index",\r
+                                                                    keyword));\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       else\r
+                               return false;\r
+               }\r
+\r
+               private void SearchFullText(string keyword)\r
+               {\r
+                       string sort = "Rating ASC";\r
+/*\r
+                       sort = "Location ASC");\r
+                       sort = "Title ASC");\r
+*/\r
+                       WriteIfVerbose(String.Format("Searching fulltext database for {0}",\r
+                                         keyword));\r
+\r
+                       bool partialMatches = false;\r
+                       bool titlesOnly = true;\r
+                       int maxResults = 100;\r
+                       DataTable results = chm.PerformSearch(keyword,\r
+                                                             maxResults,\r
+                                                             partialMatches,\r
+                                                             titlesOnly);\r
+                       WriteIfVerbose(String.Format("results.Rows.Count = {0}",\r
+                                                    results != null ?\r
+                                                    results.Rows.Count.ToString() : "(none)"));\r
+                       if (results != null && results.Rows.Count > 0)\r
+                       {\r
+                               results.DefaultView.Sort = sort;\r
+                               if (!DisplayResult(keyword, results))\r
+                               {\r
+                                       DisplayNoResult(keyword);\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               DisplayNoResult(keyword);\r
+                       }\r
+               }\r
+\r
+               private void Search(string keyword)\r
+               {\r
+                       if (!SearchIndex(keyword))\r
+                       {\r
+                               SearchFullText(keyword);\r
+                       }\r
+               }\r
+               \r
+               private bool DisplayResult(string keyword,\r
+                                          IndexTopic indexTopic)\r
+               {\r
+                       keyword = keyword.Trim().ToLower();\r
+                       string url = indexTopic.URL;\r
+                       WriteIfVerbose(String.Format("URL from index search {0}",\r
+                                         url));\r
+                       string prototype = ExtractPrototype(url);\r
+                       if (prototype == null || prototype.Trim().Equals(String.Empty))\r
+                               return false;\r
+                       string formattedPrototype = FormatPrototype(prototype);\r
+                       serviceOutput.WriteLine(formattedPrototype);\r
+                       return true;\r
+               }\r
+               \r
+               private bool DisplayResult(string keyword,\r
+                                          DataTable results)\r
+               {\r
+                       keyword = keyword.Trim().ToLower();\r
+                       for (int i = 0; i < results.DefaultView.Count; i++)\r
+                       {\r
+                               DataRowView row = results.DefaultView[i];\r
+                               string title = row["Title"].ToString();\r
+                               WriteIfVerbose(String.Format("Examining {0}", title));\r
+                               if (title.Trim().ToLower().Equals(keyword))\r
+                               {\r
+                                       string location = row["Location"].ToString();\r
+                                       string rating = row["Rating"].ToString();\r
+                                       string url = row["Url"].ToString();\r
+                                       string prototype = ExtractPrototype(url);\r
+                                       if (prototype == null || prototype.Trim().Equals(String.Empty))\r
+                                               continue;\r
+                                       string formattedPrototype = FormatPrototype(prototype);\r
+                                       serviceOutput.WriteLine(formattedPrototype);\r
+                                       return true;\r
+                               }\r
+                       }\r
+                       return false;\r
+               }\r
+\r
+               private void DisplayNoResult(string keyword)\r
+               {\r
+                       serviceOutput.WriteLine(String.Format("I don't know about keyword {0}", keyword));\r
+               }\r
+\r
+               private void DisplayNoKeyword()\r
+               {\r
+                       serviceOutput.WriteLine("Please give me a keyword.");\r
+               }\r
+\r
+               private string ReplaceComments(string s)\r
+               {\r
+                       return Regex.Replace(s, "//(.+)\r\n", "");\r
+               }\r
+\r
+               private string ReplaceLineEndings(string s)\r
+               {\r
+                       return Regex.Replace(s, "(\r\n)+", " ");\r
+               }\r
+\r
+               private string ReplaceSpaces(string s)\r
+               {\r
+                       return Regex.Replace(s, @" +", " ");\r
+               }\r
+               \r
+               private string ReplaceSpacesBeforeLeftParenthesis(string s)\r
+               {\r
+                       return Regex.Replace(s, @"\( ", @"(");\r
+               }\r
+\r
+               private string ReplaceSpacesBeforeRightParenthesis(string s)\r
+               {\r
+                       return Regex.Replace(s, @" \)", @")");\r
+               }\r
+\r
+               private string ReplaceSemicolon(string s)\r
+               {\r
+                       return Regex.Replace(s, @";", @"");\r
+               }\r
+\r
+               private string FormatPrototype(string prototype)\r
+               {\r
+                       string s = ReplaceComments(prototype);\r
+                       s = ReplaceLineEndings(s);\r
+                       s = ReplaceSpaces(s);\r
+                       s = ReplaceSpacesBeforeLeftParenthesis(s);\r
+                       s = ReplaceSpacesBeforeRightParenthesis(s);\r
+                       s = ReplaceSemicolon(s);\r
+                       return s;\r
+               }\r
+               \r
+               private string ExtractPrototype(string url)\r
+               {\r
+                       string page = GetPage(url);\r
+                       Match match = Regex.Match(page,\r
+                                                 "<PRE class=\"?syntax\"?>(.+)</PRE>",\r
+                                                 RegexOptions.Multiline |\r
+                                                 RegexOptions.Singleline);\r
+                       if (match.Groups.Count > 1)\r
+                       {\r
+                               string prototype = match.Groups[1].ToString();\r
+                               return StripHtml(StripAfterSlashPre(prototype));\r
+                       }\r
+                       \r
+                       return "";\r
+               }\r
+               \r
+               private string StripAfterSlashPre(string html)\r
+               {\r
+                       int index = html.IndexOf("</PRE>");\r
+                       if (index != -1)\r
+                       {\r
+                               return html.Substring(0, index);\r
+                       }\r
+                       else\r
+                               return html;\r
+               }\r
+               \r
+               private string StripHtml(string html)\r
+               {\r
+                       return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);\r
+               }\r
+\r
+               private string GetPage(string url)\r
+               {\r
+                       string CHMFileName = "";\r
+                       string topicName = "";\r
+                       string anchor = "";\r
+                       CHMStream.CHMStream baseStream;\r
+                       if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))\r
+                       {\r
+                               baseStream = chm.BaseStream;\r
+                               CHMFileName = baseStream.CHMFileName;\r
+                               topicName = url;\r
+                               anchor = "";\r
+                       }\r
+                       else\r
+                       {\r
+                               baseStream = GetBaseStreamFromCHMFileName(CHMFileName);\r
+                       }\r
+\r
+                       if ((topicName == "") || (CHMFileName == "") || (baseStream == null))\r
+                       {\r
+                               return "";\r
+                       }\r
+\r
+                       return baseStream.ExtractTextFile(topicName);\r
+               }\r
+\r
+               private CHMStream.CHMStream GetBaseStreamFromCHMFileName(string CHMFileName)\r
+               {\r
+                       foreach (CHMFile file in chm.FileList)\r
+                       {\r
+                               WriteIfVerbose(String.Format("Compare: {0} <> {1}",\r
+                                                            file.ChmFilePath,\r
+                                                            CHMFileName));\r
+                               if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))\r
+                               {\r
+                                       return file.BaseStream;\r
+                               }\r
+                       }\r
+                       WriteIfVerbose(String.Format("Could not find loaded CHM file in list: {0}",\r
+                                                    CHMFileName));\r
+                       return null;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/AssemblyInfo.cs b/irc/TechBot/TechBot.Library/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..dbacda1
--- /dev/null
@@ -0,0 +1,32 @@
+using System.Reflection;\r
+using System.Runtime.CompilerServices;\r
+\r
+// Information about this assembly is defined by the following\r
+// attributes.\r
+//\r
+// change them to the information which is associated with the assembly\r
+// you compile.\r
+\r
+[assembly: AssemblyTitle("")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("")]\r
+[assembly: AssemblyProduct("")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+\r
+// The assembly version has following format :\r
+//\r
+// Major.Minor.Build.Revision\r
+//\r
+// You can specify all values by your own or you can build default build and revision\r
+// numbers with the '*' character (the default):\r
+\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+// The following attributes specify the key for the sign of your assembly. See the\r
+// .NET Framework documentation for more information about signing.\r
+// This is not required, if you don't want signing let these attributes like they're.\r
+[assembly: AssemblyDelaySign(false)]\r
+[assembly: AssemblyKeyFile("")]\r
diff --git a/irc/TechBot/TechBot.Library/Default.build b/irc/TechBot/TechBot.Library/Default.build
new file mode 100644 (file)
index 0000000..0b20ec3
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>\r
+<project name="TechBot.Library" default="build">\r
+\r
+       <property name="output.dir" value="..\bin" />\r
+\r
+       <target name="build" description="Build component">\r
+               <mkdir dir="${output.dir}" />\r
+               <csc target="library"\r
+                       output="${output.dir}\TechBot.Library.dll"\r
+                       optimize="true"\r
+                       debug="true"\r
+                       doc="${output.dir}\TechBot.Library.xml"\r
+                       warninglevel="0">\r
+                       <sources>\r
+                               <include name="*.cs" />\r
+                       </sources>\r
+                       <references>\r
+                               <include name="${output.dir}\CHMLibrary.dll" />\r
+                               <include name="${output.dir}\TechBot.IRCLibrary.dll" />\r
+                       </references>\r
+               </csc>\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/TechBot.Library/HelpCommand.cs b/irc/TechBot/TechBot.Library/HelpCommand.cs
new file mode 100644 (file)
index 0000000..b38cffb
--- /dev/null
@@ -0,0 +1,37 @@
+using System;\r
+using System.Collections;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class HelpCommand : BaseCommand, ICommand\r
+       {\r
+               private IServiceOutput serviceOutput;\r
+               private ArrayList commands;\r
+               \r
+               public HelpCommand(IServiceOutput serviceOutput,\r
+                                  ArrayList commands)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.commands = commands;\r
+               }\r
+\r
+               public bool CanHandle(string commandName)\r
+               {\r
+                       return CanHandle(commandName,\r
+                                        new string[] { "help" });\r
+               }\r
+               \r
+               public void Handle(string commandName,\r
+                                  string parameters)\r
+               {\r
+                       serviceOutput.WriteLine("I support the following commands:");\r
+                       foreach (ICommand command in commands)\r
+                               serviceOutput.WriteLine(command.Help());\r
+               }\r
+               \r
+               public string Help()\r
+               {\r
+                       return "!help";\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/HresultCommand.cs b/irc/TechBot/TechBot.Library/HresultCommand.cs
new file mode 100644 (file)
index 0000000..a81567e
--- /dev/null
@@ -0,0 +1,81 @@
+using System;\r
+using System.Xml;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class HresultCommand : BaseCommand, ICommand\r
+       {\r
+               private IServiceOutput serviceOutput;\r
+               private string hresultXml;\r
+               private XmlDocument hresultXmlDocument;\r
+\r
+               public HresultCommand(IServiceOutput serviceOutput,\r
+                                     string hresultXml)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.hresultXml = hresultXml;\r
+                       hresultXmlDocument = new XmlDocument();\r
+                       hresultXmlDocument.Load(hresultXml);\r
+               }\r
+               \r
+               public bool CanHandle(string commandName)\r
+               {\r
+                       return CanHandle(commandName,\r
+                                        new string[] { "hresult" });\r
+               }\r
+\r
+               public void Handle(string commandName,\r
+                                  string parameters)\r
+               {\r
+                       string hresultText = parameters;\r
+                       if (hresultText.Equals(String.Empty))\r
+                       {\r
+                               serviceOutput.WriteLine("Please provide a valid HRESULT value.");\r
+                               return;\r
+                       }\r
+\r
+                       NumberParser np = new NumberParser();\r
+                       long hresult = np.Parse(hresultText);\r
+                       if (np.Error)\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("{0} is not a valid HRESULT value.",\r
+                                                                     hresultText));\r
+                               return;\r
+                       }\r
+                       \r
+                       string description = GetHresultDescription(hresult);\r
+                       if (description != null)\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("{0} is {1}.",\r
+                                                                     hresultText,\r
+                                                                     description));\r
+                       }\r
+                       else\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("I don't know about HRESULT {0}.",\r
+                                                                     hresultText));\r
+                       }\r
+               }\r
+               \r
+               public string Help()\r
+               {\r
+                       return "!hresult <value>";\r
+               }\r
+               \r
+               private string GetHresultDescription(long hresult)\r
+               {\r
+                       XmlElement root = hresultXmlDocument.DocumentElement;\r
+                       XmlNode node = root.SelectSingleNode(String.Format("Hresult[@value='{0}']",\r
+                                                                          hresult.ToString("X8")));\r
+                       if (node != null)\r
+                       {\r
+                               XmlAttribute text = node.Attributes["text"];\r
+                               if (text == null)\r
+                                       throw new Exception("Node has no text attribute.");\r
+                               return text.Value;\r
+                       }\r
+                       else\r
+                               return null;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/ICommand.cs b/irc/TechBot/TechBot.Library/ICommand.cs
new file mode 100644 (file)
index 0000000..3c9edea
--- /dev/null
@@ -0,0 +1,28 @@
+using System;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public interface ICommand\r
+       {\r
+               bool CanHandle(string commandName);\r
+               void Handle(string commandName,\r
+                           string parameters);\r
+               string Help();\r
+       }\r
+\r
+       \r
+       \r
+       public class BaseCommand\r
+       {\r
+               protected bool CanHandle(string commandName,\r
+                                        string[] availableCommands)\r
+               {\r
+                       foreach (string availableCommand in availableCommands)\r
+                       {\r
+                               if (String.Compare(availableCommand, commandName, true) == 0)\r
+                                       return true;\r
+                       }\r
+                       return false;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/IrcService.cs b/irc/TechBot/TechBot.Library/IrcService.cs
new file mode 100644 (file)
index 0000000..42abdec
--- /dev/null
@@ -0,0 +1,142 @@
+using System;\r
+using System.Threading;\r
+using TechBot.IRCLibrary;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class IrcService : IServiceOutput\r
+       {\r
+               private string hostname;\r
+               private int port;\r
+               private string channelname;\r
+               private string botname;\r
+               private string chmPath;\r
+               private string mainChm;\r
+               private string ntstatusXml;\r
+               private string winerrorXml;\r
+               private string hresultXml;\r
+               private string svnCommand;\r
+               private IrcClient client;\r
+               private IrcChannel channel1;\r
+               private TechBotService service;\r
+               private bool isStopped = false;\r
+\r
+               public IrcService(string hostname,\r
+                                 int port,\r
+                                 string channelname,\r
+                                 string botname,\r
+                                 string chmPath,\r
+                                 string mainChm,\r
+                                 string ntstatusXml,\r
+                                 string winerrorXml,\r
+                                 string hresultXml,\r
+                                 string svnCommand)\r
+               {\r
+                       this.hostname = hostname;\r
+                       this.port = port;\r
+                       this.channelname = channelname;\r
+                   this.botname = botname;\r
+                   this.chmPath = chmPath;\r
+                   this.mainChm = mainChm;\r
+                   this.ntstatusXml = ntstatusXml;\r
+                   this.winerrorXml = winerrorXml;\r
+                   this.hresultXml = hresultXml;\r
+                   this.svnCommand = svnCommand;\r
+               }\r
+\r
+               public void Run()\r
+               {\r
+                       service = new TechBotService(this,\r
+                                                    chmPath,\r
+                                                    mainChm,\r
+                                                    ntstatusXml,\r
+                                                    winerrorXml,\r
+                                                    hresultXml,\r
+                                                    svnCommand);\r
+                       service.Run();\r
+\r
+                       client = new IrcClient();\r
+                       client.Encoding = System.Text.Encoding.GetEncoding("iso-8859-1");\r
+                       client.MessageReceived += new MessageReceivedHandler(client_MessageReceived);\r
+                       client.ChannelUserDatabaseChanged += new ChannelUserDatabaseChangedHandler(client_ChannelUserDatabaseChanged);\r
+                       System.Console.WriteLine(String.Format("Connecting to {0} port {1}",\r
+                                                              hostname, port));\r
+                       client.Connect(hostname, port);\r
+                       System.Console.WriteLine("Connected...");\r
+                       client.Register(botname, null);\r
+                       System.Console.WriteLine(String.Format("Registered as {0}...", botname));\r
+                       channel1 = client.JoinChannel(channelname);\r
+                       System.Console.WriteLine(String.Format("Joined channel {0}...", channelname));\r
+                       \r
+                       while (!isStopped)\r
+                       {\r
+                               Thread.Sleep(1000);\r
+                       }\r
+\r
+                       client.PartChannel(channel1, "Caught in the bitstream...");\r
+                       client.Diconnect();\r
+                       System.Console.WriteLine("Disconnected...");\r
+               }\r
+               \r
+               public void Stop()\r
+               {\r
+                       isStopped = true;\r
+               }\r
+       \r
+               public void WriteLine(string message)\r
+               {\r
+                       Console.WriteLine(String.Format("Sending: {0}", message));\r
+                       channel1.Talk(message);\r
+               }\r
+\r
+               private void ExtractMessage(string parameters,\r
+                                           out string message)\r
+               {\r
+                       int startIndex = parameters.IndexOf(':');\r
+                       if (startIndex != -1)\r
+                       {\r
+                               message = parameters.Substring(startIndex + 1);\r
+                       }\r
+                       else\r
+                       {\r
+                               message = parameters;\r
+                       }\r
+               }\r
+               \r
+               private void client_MessageReceived(IrcMessage message)\r
+               {\r
+                       try\r
+                       {\r
+                               if (channel1 != null &&\r
+                                   channel1.Name != null &&\r
+                                   message.Parameters != null)\r
+                               {\r
+                                       string injectMessage;\r
+                                       ExtractMessage(message.Parameters, out injectMessage);\r
+                                       if ((message.Command.ToUpper().Equals("PRIVMSG")) &&\r
+                                           (message.Parameters.ToLower().StartsWith("#" + channel1.Name.ToLower() + " ")))\r
+                                       {\r
+                                               Console.WriteLine("Injecting: " + injectMessage);\r
+                                               service.InjectMessage(injectMessage);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               Console.WriteLine("Received: " + message.Line);\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       Console.WriteLine("Received: " + message.Line);\r
+                               }\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
+                               Console.WriteLine(String.Format("Exception: {0}", ex));\r
+                       }\r
+               }\r
+               \r
+               private void client_ChannelUserDatabaseChanged(IrcChannel channel)\r
+               {\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/NtStatusCommand.cs b/irc/TechBot/TechBot.Library/NtStatusCommand.cs
new file mode 100644 (file)
index 0000000..081d60a
--- /dev/null
@@ -0,0 +1,81 @@
+using System;\r
+using System.Xml;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class NtStatusCommand : BaseCommand, ICommand\r
+       {\r
+               private IServiceOutput serviceOutput;\r
+               private string ntstatusXml;\r
+               private XmlDocument ntstatusXmlDocument;\r
+\r
+               public NtStatusCommand(IServiceOutput serviceOutput,\r
+                                      string ntstatusXml)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.ntstatusXml = ntstatusXml;\r
+                       ntstatusXmlDocument = new XmlDocument();\r
+                       ntstatusXmlDocument.Load(ntstatusXml);\r
+               }\r
+               \r
+               public bool CanHandle(string commandName)\r
+               {\r
+                       return CanHandle(commandName,\r
+                                        new string[] { "ntstatus" });\r
+               }\r
+\r
+               public void Handle(string commandName,\r
+                                  string parameters)\r
+               {\r
+                       string ntstatusText = parameters;\r
+                       if (ntstatusText.Equals(String.Empty))\r
+                       {\r
+                               serviceOutput.WriteLine("Please provide a valid NTSTATUS value.");\r
+                               return;\r
+                       }\r
+\r
+                       NumberParser np = new NumberParser();\r
+                       long ntstatus = np.Parse(ntstatusText);\r
+                       if (np.Error)\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("{0} is not a valid NTSTATUS value.",\r
+                                                                     ntstatusText));\r
+                               return;\r
+                       }\r
+                       \r
+                       string description = GetNtstatusDescription(ntstatus);\r
+                       if (description != null)\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("{0} is {1}.",\r
+                                                                     ntstatusText,\r
+                                                                     description));\r
+                       }\r
+                       else\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("I don't know about NTSTATUS {0}.",\r
+                                                                     ntstatusText));\r
+                       }\r
+               }\r
+               \r
+               public string Help()\r
+               {\r
+                       return "!ntstatus <value>";\r
+               }\r
+               \r
+               private string GetNtstatusDescription(long ntstatus)\r
+               {\r
+                       XmlElement root = ntstatusXmlDocument.DocumentElement;\r
+                       XmlNode node = root.SelectSingleNode(String.Format("Ntstatus[@value='{0}']",\r
+                                                                          ntstatus.ToString("X8")));\r
+                       if (node != null)\r
+                       {\r
+                               XmlAttribute text = node.Attributes["text"];\r
+                               if (text == null)\r
+                                       throw new Exception("Node has no text attribute.");\r
+                               return text.Value;\r
+                       }\r
+                       else\r
+                               return null;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/NumberParser.cs b/irc/TechBot/TechBot.Library/NumberParser.cs
new file mode 100644 (file)
index 0000000..9d4eab5
--- /dev/null
@@ -0,0 +1,32 @@
+using System;\r
+using System.Globalization;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class NumberParser\r
+       {\r
+               public bool Error = false;\r
+               \r
+               public long Parse(string s)\r
+               {\r
+                       try\r
+                       {\r
+                               Error = false;\r
+                               if (s.StartsWith("0x"))\r
+                                       return Int64.Parse(s.Substring(2),\r
+                                                          NumberStyles.HexNumber);\r
+                               else\r
+                                       return Int64.Parse(s);\r
+                       }\r
+                       catch (FormatException)\r
+                       {\r
+                               Error = true;\r
+                       }\r
+                       catch (OverflowException)\r
+                       {\r
+                               Error = true;\r
+                       }\r
+                       return -1;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/ServiceOutput.cs b/irc/TechBot/TechBot.Library/ServiceOutput.cs
new file mode 100644 (file)
index 0000000..c0d4474
--- /dev/null
@@ -0,0 +1,9 @@
+using System;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public interface IServiceOutput\r
+       {\r
+               void WriteLine(string message);\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/SvnCommand.cs b/irc/TechBot/TechBot.Library/SvnCommand.cs
new file mode 100644 (file)
index 0000000..32c77cc
--- /dev/null
@@ -0,0 +1,34 @@
+using System;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class SvnCommand : BaseCommand, ICommand\r
+       {\r
+               private IServiceOutput serviceOutput;\r
+               private string svnCommand;\r
+\r
+               public SvnCommand(IServiceOutput serviceOutput,\r
+                                      string svnCommand)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.svnCommand = svnCommand;\r
+               }\r
+               \r
+               public bool CanHandle(string commandName)\r
+               {\r
+                       return CanHandle(commandName,\r
+                                        new string[] { "svn" });\r
+               }\r
+\r
+               public void Handle(string commandName,\r
+                                  string parameters)\r
+               {\r
+                       serviceOutput.WriteLine(svnCommand);\r
+               }\r
+               \r
+               public string Help()\r
+               {\r
+                       return "!svn";\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/TechBot.Library.cmbx b/irc/TechBot/TechBot.Library/TechBot.Library.cmbx
new file mode 100644 (file)
index 0000000..6b132a5
--- /dev/null
@@ -0,0 +1,16 @@
+<Combine fileversion="1.0" name="TechBot.Library" description="">\r
+  <StartMode startupentry="TechBot.Library" single="True">\r
+    <Execute entry="TechBot.Library" type="None" />\r
+  </StartMode>\r
+  <Entries>\r
+    <Entry filename=".\.\TechBot.Library.prjx" />\r
+  </Entries>\r
+  <Configurations active="Debug">\r
+    <Configuration name="Release">\r
+      <Entry name="TechBot.Library" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+    <Configuration name="Debug">\r
+      <Entry name="TechBot.Library" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot.Library/TechBot.Library.prjx b/irc/TechBot/TechBot.Library/TechBot.Library.prjx
new file mode 100644 (file)
index 0000000..1f6fd18
--- /dev/null
@@ -0,0 +1,39 @@
+<Project name="TechBot.Library" standardNamespace="TechBot.Library" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">\r
+  <Contents>\r
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+    <File name=".\TechBotService.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\ServiceOutput.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\IrcService.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\ApiCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\ICommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\HelpCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\NtStatusCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\NumberParser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\HresultCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\WinerrorCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\SvnCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+  </Contents>\r
+  <References>\r
+    <Reference type="Project" refto="CHMLibrary" localcopy="True" />\r
+    <Reference type="Project" refto="TechBot.IRCLibrary" localcopy="True" />\r
+  </References>\r
+  <DeploymentInformation target="" script="" strategy="File" />\r
+  <Configuration runwithwarnings="True" name="Debug">\r
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+    <Execution commandlineparameters="" consolepause="False" />\r
+    <Output directory="..\bin\Debug" assembly="TechBot.Library" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+  </Configuration>\r
+  <Configurations active="Debug">\r
+    <Configuration runwithwarnings="True" name="Debug">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Debug" assembly="TechBot.Library" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+    <Configuration runwithwarnings="True" name="Release">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Release" assembly="TechBot.Library" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Project>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot.Library/TechBotService.cs b/irc/TechBot/TechBot.Library/TechBotService.cs
new file mode 100644 (file)
index 0000000..32a8ec3
--- /dev/null
@@ -0,0 +1,103 @@
+using System;\r
+using System.Collections;\r
+using System.IO;\r
+using System.Data;\r
+using System.Threading;\r
+using TechBot.IRCLibrary;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class TechBotService\r
+       {\r
+               private const bool IsVerbose = false;\r
+               \r
+               private IServiceOutput serviceOutput;\r
+               private string chmPath;\r
+               private string mainChm;\r
+               private string ntstatusXml;\r
+               private string winerrorXml;\r
+               private string hresultXml;\r
+               private string svnCommand;\r
+               private ArrayList commands = new ArrayList();\r
+               \r
+               public TechBotService(IServiceOutput serviceOutput,\r
+                                     string chmPath,\r
+                                         string mainChm,\r
+                                         string ntstatusXml,\r
+                                         string winerrorXml,\r
+                                         string hresultXml,\r
+                                         string svnCommand)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.chmPath = chmPath;\r
+                       this.mainChm = mainChm;\r
+                       this.ntstatusXml = ntstatusXml;\r
+                       this.winerrorXml = winerrorXml;\r
+                       this.hresultXml = hresultXml;\r
+                       this.svnCommand = svnCommand;\r
+               }\r
+               \r
+               private void WriteIfVerbose(string message)\r
+               {\r
+                       if (IsVerbose)\r
+                               serviceOutput.WriteLine(message);\r
+               }\r
+               \r
+               public void Run()\r
+               {\r
+                       commands.Add(new HelpCommand(serviceOutput,\r
+                                                    commands));\r
+                       commands.Add(new ApiCommand(serviceOutput,\r
+                                                   chmPath,\r
+                                                   mainChm));\r
+                       commands.Add(new NtStatusCommand(serviceOutput,\r
+                                                        ntstatusXml));\r
+                       commands.Add(new WinerrorCommand(serviceOutput,\r
+                                                        winerrorXml));\r
+                       commands.Add(new HresultCommand(serviceOutput,\r
+                                                       hresultXml));\r
+                       commands.Add(new SvnCommand(serviceOutput,\r
+                                                   svnCommand));\r
+               }\r
+               \r
+               public void InjectMessage(string message)\r
+               {\r
+                       if (message.StartsWith("!"))\r
+                       {\r
+                               ParseCommandMessage(message);\r
+                       }\r
+               }\r
+               \r
+               private bool IsCommandMessage(string message)\r
+               {\r
+                       return message.StartsWith("!");\r
+               }\r
+\r
+               public void ParseCommandMessage(string message)\r
+               {\r
+                       if (!IsCommandMessage(message))\r
+                               return;\r
+\r
+                       message = message.Substring(1).Trim();\r
+                       int index = message.IndexOf(' ');\r
+                       string commandName;\r
+                       string parameters = "";\r
+                       if (index != -1)\r
+                       {\r
+                               commandName = message.Substring(0, index).Trim();\r
+                               parameters = message.Substring(index).Trim();\r
+                       }\r
+                       else\r
+                               commandName = message.Trim();\r
+\r
+                       foreach (ICommand command in commands)\r
+                       {\r
+                               if (command.CanHandle(commandName))\r
+                               {\r
+                                       command.Handle(commandName, parameters);\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.Library/WinerrorCommand.cs b/irc/TechBot/TechBot.Library/WinerrorCommand.cs
new file mode 100644 (file)
index 0000000..5e92b81
--- /dev/null
@@ -0,0 +1,81 @@
+using System;\r
+using System.Xml;\r
+\r
+namespace TechBot.Library\r
+{\r
+       public class WinerrorCommand : BaseCommand, ICommand\r
+       {\r
+               private IServiceOutput serviceOutput;\r
+               private string winerrorXml;\r
+               private XmlDocument winerrorXmlDocument;\r
+\r
+               public WinerrorCommand(IServiceOutput serviceOutput,\r
+                                      string winerrorXml)\r
+               {\r
+                       this.serviceOutput = serviceOutput;\r
+                       this.winerrorXml = winerrorXml;\r
+                       winerrorXmlDocument = new XmlDocument();\r
+                       winerrorXmlDocument.Load(winerrorXml);\r
+               }\r
+               \r
+               public bool CanHandle(string commandName)\r
+               {\r
+                       return CanHandle(commandName,\r
+                                        new string[] { "winerror" });\r
+               }\r
+\r
+               public void Handle(string commandName,\r
+                                  string parameters)\r
+               {\r
+                       string winerrorText = parameters;\r
+                       if (winerrorText.Equals(String.Empty))\r
+                       {\r
+                               serviceOutput.WriteLine("Please provide a valid System Error Code value.");\r
+                               return;\r
+                       }\r
+\r
+                       NumberParser np = new NumberParser();\r
+                       long winerror = np.Parse(winerrorText);\r
+                       if (np.Error)\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("{0} is not a valid System Error Code value.",\r
+                                                                     winerrorText));\r
+                               return;\r
+                       }\r
+                       \r
+                       string description = GetWinerrorDescription(winerror);\r
+                       if (description != null)\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("{0} is {1}.",\r
+                                                                     winerrorText,\r
+                                                                     description));\r
+                       }\r
+                       else\r
+                       {\r
+                               serviceOutput.WriteLine(String.Format("I don't know about System Error Code {0}.",\r
+                                                                     winerrorText));\r
+                       }\r
+               }\r
+               \r
+               public string Help()\r
+               {\r
+                       return "!winerror <value>";\r
+               }\r
+               \r
+               private string GetWinerrorDescription(long winerror)\r
+               {\r
+                       XmlElement root = winerrorXmlDocument.DocumentElement;\r
+                       XmlNode node = root.SelectSingleNode(String.Format("Winerror[@value='{0}']",\r
+                                                                          winerror));\r
+                       if (node != null)\r
+                       {\r
+                               XmlAttribute text = node.Attributes["text"];\r
+                               if (text == null)\r
+                                       throw new Exception("Node has no text attribute.");\r
+                               return text.Value;\r
+                       }\r
+                       else\r
+                               return null;\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot.cmbx b/irc/TechBot/TechBot.cmbx
new file mode 100644 (file)
index 0000000..3e8915e
--- /dev/null
@@ -0,0 +1,36 @@
+<Combine fileversion="1.0" name="TechBot" description="">\r
+  <StartMode startupentry="TechBot" single="True">\r
+    <Execute entry="TechBot" type="None" />\r
+    <Execute entry="TechBot.Library" type="None" />\r
+    <Execute entry="CHMLibrary" type="None" />\r
+    <Execute entry="Compression" type="None" />\r
+    <Execute entry="TechBot.Console" type="None" />\r
+    <Execute entry="TechBot.IRCLibrary" type="None" />\r
+  </StartMode>\r
+  <Entries>\r
+    <Entry filename=".\TechBot\TechBot.prjx" />\r
+    <Entry filename=".\TechBot.Library\TechBot.Library.prjx" />\r
+    <Entry filename=".\CHMLibrary\CHMLibrary.prjx" />\r
+    <Entry filename=".\Compression\Compression.prjx" />\r
+    <Entry filename=".\TechBot.Console\TechBot.Console.prjx" />\r
+    <Entry filename=".\TechBot.IRCLibrary\TechBot.IRCLibrary.prjx" />\r
+  </Entries>\r
+  <Configurations active="Debug">\r
+    <Configuration name="Release">\r
+      <Entry name="TechBot" configurationname="Debug" build="False" />\r
+      <Entry name="TechBot.Library" configurationname="Debug" build="False" />\r
+      <Entry name="CHMLibrary" configurationname="Debug" build="False" />\r
+      <Entry name="Compression" configurationname="Debug" build="False" />\r
+      <Entry name="TechBot.Console" configurationname="Debug" build="False" />\r
+      <Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+    <Configuration name="Debug">\r
+      <Entry name="TechBot" configurationname="Debug" build="False" />\r
+      <Entry name="TechBot.Library" configurationname="Debug" build="False" />\r
+      <Entry name="CHMLibrary" configurationname="Debug" build="False" />\r
+      <Entry name="Compression" configurationname="Debug" build="False" />\r
+      <Entry name="TechBot.Console" configurationname="Debug" build="False" />\r
+      <Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Combine>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot/App.config b/irc/TechBot/TechBot/App.config
new file mode 100644 (file)
index 0000000..bdb4e73
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>\r
+<configuration>\r
+       <appSettings>\r
+               <add key="IRCServerHostName" value="irc.eu.freenode.net" />\r
+               <add key="IRCServerHostPort" value="6667" />\r
+               <add key="IRCChannelName" value="channel" />\r
+               <add key="IRCBotName" value="MyBot" />\r
+               <add key="ChmPath" value="C:\IRC\TechBot\CHM" />\r
+               <add key="MainChm" value="kmarch.chm" />\r
+               <add key="NtstatusXml" value="C:\IRC\TechBot\ntstatus.xml" />\r
+               <add key="WinerrorXml" value="C:\IRC\TechBot\winerror.xml" />\r
+               <add key="HresultXml" value="C:\IRC\TechBot\hresult.xml" />\r
+               <add key="SvnCommand" value="svn co svn://svn.reactos.com/trunk/reactos" />\r
+       </appSettings>\r
+</configuration>\r
diff --git a/irc/TechBot/TechBot/AssemblyInfo.cs b/irc/TechBot/TechBot/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..dbacda1
--- /dev/null
@@ -0,0 +1,32 @@
+using System.Reflection;\r
+using System.Runtime.CompilerServices;\r
+\r
+// Information about this assembly is defined by the following\r
+// attributes.\r
+//\r
+// change them to the information which is associated with the assembly\r
+// you compile.\r
+\r
+[assembly: AssemblyTitle("")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("")]\r
+[assembly: AssemblyProduct("")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+\r
+// The assembly version has following format :\r
+//\r
+// Major.Minor.Build.Revision\r
+//\r
+// You can specify all values by your own or you can build default build and revision\r
+// numbers with the '*' character (the default):\r
+\r
+[assembly: AssemblyVersion("1.0.*")]\r
+\r
+// The following attributes specify the key for the sign of your assembly. See the\r
+// .NET Framework documentation for more information about signing.\r
+// This is not required, if you don't want signing let these attributes like they're.\r
+[assembly: AssemblyDelaySign(false)]\r
+[assembly: AssemblyKeyFile("")]\r
diff --git a/irc/TechBot/TechBot/Default.build b/irc/TechBot/TechBot/Default.build
new file mode 100644 (file)
index 0000000..8d0f4bb
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>\r
+<project name="TechBot" default="build">\r
+\r
+       <property name="output.dir" value="..\bin" />\r
+\r
+       <target name="build" description="Build component">\r
+               <mkdir dir="${output.dir}" />\r
+               <csc target="winexe"\r
+                       output="${output.dir}\TechBot.exe"\r
+                       optimize="true"\r
+                       debug="true"\r
+                       doc="${output.dir}\TechBot.xml"\r
+                       warninglevel="0">\r
+                       <sources>\r
+                               <include name="*.cs" />\r
+                       </sources>\r
+                       <references>\r
+                               <include name="${output.dir}\TechBot.Library.dll" />\r
+                       </references>\r
+               </csc>\r
+       </target>\r
+\r
+</project>\r
diff --git a/irc/TechBot/TechBot/ServiceThread.cs b/irc/TechBot/TechBot/ServiceThread.cs
new file mode 100644 (file)
index 0000000..0fd8145
--- /dev/null
@@ -0,0 +1,71 @@
+using System;\r
+using System.Configuration;\r
+using System.Diagnostics;\r
+using TechBot.Library;\r
+\r
+namespace TechBot\r
+{\r
+       public class ServiceThread\r
+       {\r
+               private string IRCServerHostName;\r
+               private int IRCServerHostPort;\r
+               private string IRCChannelName;\r
+               private string IRCBotName;\r
+               private string ChmPath;\r
+               private string MainChm;\r
+               private string NtstatusXml;\r
+               private string HresultXml;\r
+               private string WinerrorXml;\r
+               private string SvnCommand;\r
+               private EventLog eventLog;\r
+               \r
+               public ServiceThread(EventLog eventLog)\r
+               {\r
+                       this.eventLog = eventLog;\r
+               }\r
+               \r
+               private void SetupConfiguration()\r
+               {\r
+                       IRCServerHostName = ConfigurationSettings.AppSettings["IRCServerHostName"];\r
+                       IRCServerHostPort = Int32.Parse(ConfigurationSettings.AppSettings["IRCServerHostPort"]);\r
+                       IRCChannelName = ConfigurationSettings.AppSettings["IRCChannelName"];\r
+                       IRCBotName = ConfigurationSettings.AppSettings["IRCBotName"];\r
+                       ChmPath = ConfigurationSettings.AppSettings["ChmPath"];\r
+                       MainChm = ConfigurationSettings.AppSettings["MainChm"];\r
+                       NtstatusXml = ConfigurationSettings.AppSettings["NtstatusXml"];\r
+                       HresultXml = ConfigurationSettings.AppSettings["HresultXml"];\r
+                       WinerrorXml = ConfigurationSettings.AppSettings["WinerrorXml"];\r
+                       SvnCommand = ConfigurationSettings.AppSettings["SvnCommand"];\r
+               }\r
+               \r
+               public void Run()\r
+               {\r
+                       SetupConfiguration();\r
+                       System.Console.WriteLine("TechBot irc service...");\r
+                       \r
+                       IrcService ircService = new IrcService(IRCServerHostName,\r
+                                                              IRCServerHostPort,\r
+                                                              IRCChannelName,\r
+                                                              IRCBotName,\r
+                                                              ChmPath,\r
+                                                              MainChm,\r
+                                                              NtstatusXml,\r
+                                                              WinerrorXml,\r
+                                                              HresultXml,\r
+                                                              SvnCommand);\r
+                       ircService.Run();\r
+               }\r
+               \r
+               public void Start()\r
+               {\r
+                       try\r
+                       {\r
+                               Run();\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
+                               eventLog.WriteEntry(String.Format("Ex. {0}", ex));\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/irc/TechBot/TechBot/TechBot.prjx b/irc/TechBot/TechBot/TechBot.prjx
new file mode 100644 (file)
index 0000000..7af6ca1
--- /dev/null
@@ -0,0 +1,30 @@
+<Project name="TechBot" standardNamespace="TechBot" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">\r
+  <Contents>\r
+    <File name=".\TechBotService.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+    <File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+    <File name=".\App.config" subtype="Code" buildaction="Nothing" dependson="" data="" />\r
+    <File name=".\ServiceThread.cs" subtype="Code" buildaction="Compile" dependson="" data="" />\r
+  </Contents>\r
+  <References>\r
+    <Reference type="Project" refto="TechBot.Library" localcopy="True" />\r
+  </References>\r
+  <DeploymentInformation target="" script="" strategy="File" />\r
+  <Configuration runwithwarnings="True" name="Debug">\r
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+    <Execution commandlineparameters="" consolepause="False" />\r
+    <Output directory="..\bin\Debug" assembly="TechBot" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+  </Configuration>\r
+  <Configurations active="Debug">\r
+    <Configuration runwithwarnings="True" name="Debug">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Debug" assembly="TechBot" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+    <Configuration runwithwarnings="True" name="Release">\r
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />\r
+      <Execution commandlineparameters="" consolepause="False" />\r
+      <Output directory="..\bin\Release" assembly="TechBot" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />\r
+    </Configuration>\r
+  </Configurations>\r
+</Project>
\ No newline at end of file
diff --git a/irc/TechBot/TechBot/TechBotService.cs b/irc/TechBot/TechBot/TechBotService.cs
new file mode 100644 (file)
index 0000000..79fca2d
--- /dev/null
@@ -0,0 +1,97 @@
+using System;\r
+using System.Threading;\r
+using System.Collections;\r
+using System.ComponentModel;\r
+using System.Data;\r
+using System.Diagnostics;\r
+using System.ServiceProcess;\r
+using System.Configuration.Install;\r
+\r
+namespace TechBot\r
+{\r
+       public class TechBotService : System.ServiceProcess.ServiceBase\r
+       {\r
+               private Thread thread;\r
+               private ServiceThread threadWorker;\r
+               \r
+               public TechBotService()\r
+               {\r
+                       InitializeComponents();\r
+               }\r
+\r
+               private void InitializeComponents()\r
+               {\r
+                       this.ServiceName = "TechBot";\r
+               }\r
+               \r
+               /// <summary>\r
+               /// This method starts the service.\r
+               /// </summary>\r
+               public static void Main()\r
+               {\r
+                       System.ServiceProcess.ServiceBase.Run(new System.ServiceProcess.ServiceBase[] {\r
+                               new TechBotService() // To run more than one service you have to add them here\r
+                       });\r
+               }\r
+\r
+               /// <summary>\r
+               /// Clean up any resources being used.\r
+               /// </summary>\r
+               protected override void Dispose(bool disposing)\r
+               {\r
+                       base.Dispose(disposing);\r
+               }\r
+\r
+               /// <summary>\r
+               /// Start this service.\r
+               /// </summary>\r
+               protected override void OnStart(string[] args)\r
+               {\r
+                       try\r
+                       {\r
+                               threadWorker = new ServiceThread(EventLog);\r
+                               thread = new Thread(new ThreadStart(threadWorker.Start));\r
+                               thread.Start();\r
+                               EventLog.WriteEntry(String.Format("TechBot service is running."));\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
+                               EventLog.WriteEntry(String.Format("Ex. {0}", ex));\r
+                       }\r
+               }\r
\r
+               /// <summary>\r
+               /// Stop this service.\r
+               /// </summary>\r
+               protected override void OnStop()\r
+               {\r
+                       try\r
+                       {\r
+                               thread.Abort();\r
+                               thread.Join();\r
+                               thread = null;\r
+                               threadWorker = null;\r
+                               EventLog.WriteEntry(String.Format("TechBot service is stopped."));\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
+                               EventLog.WriteEntry(String.Format("Ex. {0}", ex));\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+[RunInstaller(true)]\r
+public class ProjectInstaller : Installer\r
+{\r
+       public ProjectInstaller()\r
+       {\r
+               ServiceProcessInstaller spi = new ServiceProcessInstaller();\r
+               spi.Account = ServiceAccount.LocalSystem;\r
+               \r
+               ServiceInstaller si = new ServiceInstaller();\r
+               si.ServiceName = "TechBot";\r
+               si.StartType = ServiceStartMode.Automatic;\r
+               Installers.AddRange(new Installer[] {spi, si});\r
+       }\r
+}\r