--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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("(", "(");\r
+ innerText = innerText.Replace(")", ")");\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 <OBJECT> 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
--- /dev/null
+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
--- /dev/null
+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("(", "(");\r
+ innerText = innerText.Replace(")", ")");\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 <OBJECT> 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+// \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 ("&");\r
+ break;\r
+ case '"' :\r
+ output.Append (""");\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 ("&");\r
+ break;\r
+ case '>' : \r
+ output.Append (">");\r
+ break;\r
+ case '<' :\r
+ output.Append ("<");\r
+ break;\r
+ case '"' :\r
+ output.Append (""");\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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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 <= 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+/*\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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+using System;\r
+\r
+namespace TechBot.Library\r
+{\r
+ public interface IServiceOutput\r
+ {\r
+ void WriteLine(string message);\r
+ }\r
+}\r
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+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