mirror of
https://github.com/reactos/reactos.git
synced 2026-06-02 01:11:03 +08:00
Import TechBot
svn path=/trunk/; revision=13064
This commit is contained in:
32
irc/TechBot/CHMLibrary/AssemblyInfo.cs
Normal file
32
irc/TechBot/CHMLibrary/AssemblyInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
274
irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs
Normal file
274
irc/TechBot/CHMLibrary/CHMDecoding/BinaryReaderHelp.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>BinaryReaderHelp</c> implements static helper methods for extracting binary data
|
||||
/// from a binary reader object.
|
||||
/// </summary>
|
||||
internal class BinaryReaderHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal helper method to extract null-terminated strings from a binary reader
|
||||
/// </summary>
|
||||
/// <param name="binReader">reference to the binary reader</param>
|
||||
/// <param name="offset">offset in the stream</param>
|
||||
/// <param name="noOffset">true if the offset value should be used</param>
|
||||
/// <param name="encoder">encoder used for text encoding</param>
|
||||
/// <returns>An extracted string value</returns>
|
||||
internal static string ExtractString(ref BinaryReader binReader, int offset, bool noOffset, Encoding encoder)
|
||||
{
|
||||
string strReturn = "";
|
||||
|
||||
if(encoder == null)
|
||||
encoder = Encoding.ASCII;
|
||||
|
||||
ArrayList nameBytes = new ArrayList();
|
||||
byte curByte;
|
||||
|
||||
if(!noOffset)
|
||||
binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
|
||||
return "";
|
||||
|
||||
curByte = binReader.ReadByte();
|
||||
while( (curByte != (byte)0) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
|
||||
{
|
||||
nameBytes.Add( curByte );
|
||||
curByte = binReader.ReadByte();
|
||||
}
|
||||
|
||||
byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
|
||||
strReturn = encoder.GetString(name,0,name.Length);
|
||||
|
||||
return strReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper method to extract a string with a specific length from the binary reader
|
||||
/// </summary>
|
||||
/// <param name="binReader">reference to the binary reader</param>
|
||||
/// <param name="length">length of the string (number of bytes)</param>
|
||||
/// <param name="offset">offset in the stream</param>
|
||||
/// <param name="noOffset">true if the offset value should be used</param>
|
||||
/// <param name="encoder">encoder used for text encoding</param>
|
||||
/// <returns>An extracted string value</returns>
|
||||
internal static string ExtractString(ref BinaryReader binReader, int length, int offset, bool noOffset, Encoding encoder)
|
||||
{
|
||||
string strReturn = "";
|
||||
|
||||
if(length == 0)
|
||||
return "";
|
||||
|
||||
if(encoder == null)
|
||||
encoder = Encoding.ASCII;
|
||||
|
||||
ArrayList nameBytes = new ArrayList();
|
||||
byte curByte;
|
||||
|
||||
if(!noOffset)
|
||||
binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
|
||||
return "";
|
||||
|
||||
curByte = binReader.ReadByte();
|
||||
while( (curByte != (byte)0) && (nameBytes.Count < length) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
|
||||
{
|
||||
nameBytes.Add( curByte );
|
||||
|
||||
if(nameBytes.Count < length)
|
||||
curByte = binReader.ReadByte();
|
||||
}
|
||||
|
||||
byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
|
||||
strReturn = encoder.GetString(name,0,name.Length);
|
||||
|
||||
return strReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper method to extract a string with a specific length from the binary reader
|
||||
/// </summary>
|
||||
/// <param name="binReader">reference to the binary reader</param>
|
||||
/// <param name="bFoundTerminator">reference to a bool vairable which will receive true if the
|
||||
/// string terminator \0 was found. false indicates that the end of the stream was reached.</param>
|
||||
/// <param name="offset">offset in the stream</param>
|
||||
/// <param name="noOffset">true if the offset value should be used</param>
|
||||
/// <param name="encoder">encoder used for text encoding</param>
|
||||
/// <returns>An extracted string value</returns>
|
||||
internal static string ExtractString(ref BinaryReader binReader, ref bool bFoundTerminator, int offset, bool noOffset, Encoding encoder)
|
||||
{
|
||||
string strReturn = "";
|
||||
|
||||
ArrayList nameBytes = new ArrayList();
|
||||
byte curByte;
|
||||
|
||||
if(encoder == null)
|
||||
encoder = Encoding.ASCII;
|
||||
|
||||
if(!noOffset)
|
||||
binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
|
||||
return "";
|
||||
|
||||
curByte = binReader.ReadByte();
|
||||
while( (curByte != (byte)0) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
|
||||
{
|
||||
nameBytes.Add( curByte );
|
||||
curByte = binReader.ReadByte();
|
||||
|
||||
if( curByte == (byte)0 )
|
||||
{
|
||||
bFoundTerminator = true;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
|
||||
strReturn = encoder.GetString(name,0,name.Length);
|
||||
|
||||
return strReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper method to extract a null-terminated UTF-16/UCS-2 strings from a binary reader
|
||||
/// </summary>
|
||||
/// <param name="binReader">reference to the binary reader</param>
|
||||
/// <param name="offset">offset in the stream</param>
|
||||
/// <param name="noOffset">true if the offset value should be used</param>
|
||||
/// <param name="encoder">encoder used for text encoding</param>
|
||||
/// <returns>An extracted string value</returns>
|
||||
internal static string ExtractUTF16String(ref BinaryReader binReader, int offset, bool noOffset, Encoding encoder)
|
||||
{
|
||||
string strReturn = "";
|
||||
|
||||
ArrayList nameBytes = new ArrayList();
|
||||
byte curByte;
|
||||
int lastByte=-1;
|
||||
|
||||
if(!noOffset)
|
||||
binReader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
|
||||
return "";
|
||||
|
||||
if(encoder == null)
|
||||
encoder = Encoding.Unicode;
|
||||
|
||||
curByte = binReader.ReadByte();
|
||||
int nCnt = 0;
|
||||
while( ((curByte != (byte)0) || (lastByte != 0) ) && (binReader.BaseStream.Position < binReader.BaseStream.Length) )
|
||||
{
|
||||
nameBytes.Add( curByte );
|
||||
|
||||
if(nCnt%2 == 0)
|
||||
lastByte = (int)curByte;
|
||||
|
||||
curByte = binReader.ReadByte();
|
||||
|
||||
nCnt++;
|
||||
}
|
||||
|
||||
byte[] name = (byte[]) (nameBytes.ToArray(System.Type.GetType("System.Byte")));
|
||||
strReturn = Encoding.Unicode.GetString(name,0,name.Length);
|
||||
|
||||
// apply text encoding
|
||||
name = Encoding.Default.GetBytes(strReturn);
|
||||
strReturn = encoder.GetString(name,0,name.Length);
|
||||
|
||||
return strReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper for reading ENCINT encoded integer values
|
||||
/// </summary>
|
||||
/// <param name="binReader">reference to the reader</param>
|
||||
/// <returns>a long value</returns>
|
||||
internal static long ReadENCINT(ref BinaryReader binReader)
|
||||
{
|
||||
long nRet = 0;
|
||||
byte buffer = 0;
|
||||
int shift = 0;
|
||||
|
||||
if(binReader.BaseStream.Position >= binReader.BaseStream.Length)
|
||||
return nRet;
|
||||
|
||||
do
|
||||
{
|
||||
buffer = binReader.ReadByte();
|
||||
nRet |= ((long)((buffer & (byte)0x7F))) << shift;
|
||||
shift += 7;
|
||||
|
||||
}while ( (buffer & (byte)0x80) != 0);
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an s/r encoded value from the byte array and decodes it into an integer
|
||||
/// </summary>
|
||||
/// <param name="wclBits">a byte array containing all bits (contains only 0 or 1 elements)</param>
|
||||
/// <param name="s">scale param for encoding</param>
|
||||
/// <param name="r">root param for encoding</param>
|
||||
/// <param name="nBitIndex">current index in the wclBits array</param>
|
||||
/// <returns>Returns an decoded integer value.</returns>
|
||||
internal static int ReadSRItem(byte[] wclBits, int s, int r, ref int nBitIndex)
|
||||
{
|
||||
int nRet = 0;
|
||||
int q = r;
|
||||
|
||||
int nPref1Cnt = 0;
|
||||
|
||||
while( wclBits[nBitIndex++] == 1)
|
||||
{
|
||||
nPref1Cnt++;
|
||||
}
|
||||
|
||||
if(nPref1Cnt == 0)
|
||||
{
|
||||
int nMask = 0;
|
||||
|
||||
for(int nbits=0; nbits<q;nbits++)
|
||||
{
|
||||
nMask |= ( 0x01 & (int)wclBits[nBitIndex]) << (q-nbits-1);
|
||||
nBitIndex++;
|
||||
}
|
||||
|
||||
nRet = nMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
q += (nPref1Cnt-1);
|
||||
|
||||
int nMask = 0;
|
||||
int nRMaxValue = 0;
|
||||
|
||||
for(int nbits=0; nbits<q;nbits++)
|
||||
{
|
||||
nMask |= ( 0x01 & (int)wclBits[nBitIndex]) << (q-nbits-1);
|
||||
nBitIndex++;
|
||||
}
|
||||
|
||||
for(int nsv=0; nsv<r; nsv++)
|
||||
{
|
||||
nRMaxValue = nRMaxValue << 1;
|
||||
nRMaxValue |= 0x1;
|
||||
}
|
||||
|
||||
nRMaxValue++; // startvalue of s/r encoding with 1 prefixing '1'
|
||||
|
||||
nRMaxValue *= (int) Math.Pow((double)2, (double)(nPref1Cnt-1));
|
||||
|
||||
nRet = nRMaxValue + nMask;
|
||||
}
|
||||
|
||||
return nRet;
|
||||
}
|
||||
}
|
||||
}
|
||||
325
irc/TechBot/CHMLibrary/CHMDecoding/CHMBtree.cs
Normal file
325
irc/TechBot/CHMLibrary/CHMDecoding/CHMBtree.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMBtree</c> implements methods/properties to decode the binary help index.
|
||||
/// This class automatically creates an index arraylist for the current CHMFile instance.
|
||||
/// It does not store the index internally !
|
||||
/// </summary>
|
||||
/// <remarks>The binary index can be found in the storage file $WWKeywordLinks/BTree</remarks>
|
||||
internal sealed class CHMBtree : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant specifying the size of the string blocks
|
||||
/// </summary>
|
||||
private const int BLOCK_SIZE = 2048;
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing flags
|
||||
/// </summary>
|
||||
private int _flags = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the data format
|
||||
/// </summary>
|
||||
private byte[] _dataFormat = new byte[16];
|
||||
/// <summary>
|
||||
/// Internal member storing the index of the last listing block
|
||||
/// </summary>
|
||||
private int _indexOfLastListingBlock = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the index of the root block
|
||||
/// </summary>
|
||||
private int _indexOfRootBlock = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the number of blocks
|
||||
/// </summary>
|
||||
private int _numberOfBlocks = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the tree depth.
|
||||
/// (1 if no index blocks, 2 one level of index blocks, ...)
|
||||
/// </summary>
|
||||
private int _treeDepth = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the number of keywords in the file
|
||||
/// </summary>
|
||||
private int _numberOfKeywords = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the codepage
|
||||
/// </summary>
|
||||
private int _codePage = 0;
|
||||
/// <summary>
|
||||
/// true if the index is from a CHI or CHM file, else CHW
|
||||
/// </summary>
|
||||
private bool _isCHI_CHM = true;
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
/// <summary>
|
||||
/// Internal flag specifying if we have to read listing or index blocks
|
||||
/// </summary>
|
||||
private bool _readListingBlocks = true;
|
||||
/// <summary>
|
||||
/// Internal member storing an indexlist of the current file.
|
||||
/// </summary>
|
||||
private ArrayList _indexList = new ArrayList();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the $WWKeywordLinks/BTree file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMBtree(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
if( associatedFile == null)
|
||||
{
|
||||
throw new ArgumentException("CHMBtree.ctor() - Associated CHMFile must not be null !", "associatedFile");
|
||||
}
|
||||
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
|
||||
// clear internal binary data after extraction
|
||||
_binaryFileData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int nCurOffset = 0;
|
||||
int nTemp = 0;
|
||||
|
||||
// decode header
|
||||
binReader.ReadChars(2); // 2chars signature (not important)
|
||||
|
||||
_flags = (int)binReader.ReadInt16(); // WORD flags
|
||||
|
||||
binReader.ReadInt16(); // size of blocks (always 2048)
|
||||
|
||||
_dataFormat = binReader.ReadBytes(16);
|
||||
|
||||
binReader.ReadInt32(); // unknown DWORD
|
||||
|
||||
_indexOfLastListingBlock = binReader.ReadInt32();
|
||||
_indexOfRootBlock = binReader.ReadInt32();
|
||||
|
||||
binReader.ReadInt32(); // unknown DWORD
|
||||
|
||||
_numberOfBlocks = binReader.ReadInt32();
|
||||
_treeDepth = binReader.ReadInt16();
|
||||
_numberOfKeywords = binReader.ReadInt32();
|
||||
_codePage = binReader.ReadInt32();
|
||||
|
||||
binReader.ReadInt32(); // lcid DWORD
|
||||
|
||||
nTemp = binReader.ReadInt32();
|
||||
_isCHI_CHM = (nTemp==1);
|
||||
|
||||
binReader.ReadInt32(); // unknown DWORD
|
||||
binReader.ReadInt32(); // unknown DWORD
|
||||
binReader.ReadInt32(); // unknown DWORD
|
||||
binReader.ReadInt32(); // unknown DWORD
|
||||
|
||||
// end of header decode
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
nCurOffset = (int)memStream.Position;
|
||||
byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);
|
||||
bRet &= DecodeBlock(dataBlock, ref nCurOffset, _treeDepth-1);
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a block of url-string data
|
||||
/// </summary>
|
||||
/// <param name="dataBlock">block of data</param>
|
||||
/// <param name="nOffset">current file offset</param>
|
||||
/// <param name="indexBlocks">number of index blocks</param>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeBlock( byte[] dataBlock, ref int nOffset, int indexBlocks )
|
||||
{
|
||||
bool bRet = true;
|
||||
int nblockOffset = nOffset;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(dataBlock);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int freeSpace = binReader.ReadInt16(); // length of freespace
|
||||
int nrOfEntries = binReader.ReadInt16(); // number of entries
|
||||
|
||||
bool bListingEndReached = false;
|
||||
|
||||
//while( (memStream.Position < (memStream.Length-freeSpace)) && (bRet) )
|
||||
//{
|
||||
int nIndexOfPrevBlock = -1;
|
||||
int nIndexOfNextBlock = -1;
|
||||
int nIndexOfChildBlock = 0;
|
||||
|
||||
if(_readListingBlocks)
|
||||
{
|
||||
nIndexOfPrevBlock = binReader.ReadInt32(); // -1 if this is the header
|
||||
nIndexOfNextBlock = binReader.ReadInt32(); // -1 if this is the last block
|
||||
}
|
||||
else
|
||||
{
|
||||
nIndexOfChildBlock = binReader.ReadInt32();
|
||||
}
|
||||
|
||||
for(int nE = 0; nE < nrOfEntries; nE++)
|
||||
{
|
||||
if(_readListingBlocks)
|
||||
{
|
||||
bListingEndReached = (nIndexOfNextBlock==-1);
|
||||
|
||||
string keyWord = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
|
||||
|
||||
bool isSeeAlsoKeyword = (binReader.ReadInt16()!=0);
|
||||
|
||||
int indent = binReader.ReadInt16(); // indent of entry
|
||||
int nCharIndex = binReader.ReadInt32();
|
||||
|
||||
binReader.ReadInt32();
|
||||
|
||||
int numberOfPairs = binReader.ReadInt32();
|
||||
|
||||
int[] nTopics = new int[numberOfPairs];
|
||||
string[] seeAlso = new string[numberOfPairs];
|
||||
|
||||
for(int i=0; i < numberOfPairs; i++)
|
||||
{
|
||||
if(isSeeAlsoKeyword)
|
||||
{
|
||||
seeAlso[i] = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
nTopics[i] = binReader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
binReader.ReadInt32(); // unknown
|
||||
|
||||
int nIndexOfThisEntry = binReader.ReadInt32();
|
||||
|
||||
IndexItem newItem = new IndexItem(_associatedFile, keyWord, isSeeAlsoKeyword, indent, nCharIndex, nIndexOfThisEntry, seeAlso, nTopics);
|
||||
_indexList.Add(newItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
string keyWord = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
|
||||
|
||||
bool isSeeAlsoKeyword = (binReader.ReadInt16()!=0);
|
||||
|
||||
int indent = binReader.ReadInt16(); // indent of entry
|
||||
int nCharIndex = binReader.ReadInt32();
|
||||
|
||||
binReader.ReadInt32();
|
||||
|
||||
int numberOfPairs = binReader.ReadInt32();
|
||||
|
||||
int[] nTopics = new int[numberOfPairs];
|
||||
string[] seeAlso = new string[numberOfPairs];
|
||||
|
||||
for(int i=0; i < numberOfPairs; i++)
|
||||
{
|
||||
if(isSeeAlsoKeyword)
|
||||
{
|
||||
seeAlso[i] = BinaryReaderHelp.ExtractUTF16String(ref binReader, 0, true, _associatedFile.TextEncoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
nTopics[i] = binReader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
int nIndexChild = binReader.ReadInt32();
|
||||
int nIndexOfThisEntry=-1;
|
||||
|
||||
IndexItem newItem = new IndexItem(_associatedFile, keyWord, isSeeAlsoKeyword, indent, nCharIndex, nIndexOfThisEntry, seeAlso, nTopics);
|
||||
_indexList.Add(newItem);
|
||||
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
binReader.ReadBytes(freeSpace);
|
||||
|
||||
|
||||
if( bListingEndReached )
|
||||
_readListingBlocks = false;
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal generated index list
|
||||
/// </summary>
|
||||
internal ArrayList IndexList
|
||||
{
|
||||
get { return _indexList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
2061
irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs
Normal file
2061
irc/TechBot/CHMLibrary/CHMDecoding/CHMFile.cs
Normal file
File diff suppressed because it is too large
Load Diff
286
irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs
Normal file
286
irc/TechBot/CHMLibrary/CHMDecoding/CHMIdxhdr.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMIdxhdr</c> implements t properties which have been read from the #IDXHDR file.
|
||||
/// </summary>
|
||||
internal sealed class CHMIdxhdr : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the number of topic nodes including the contents and index files
|
||||
/// </summary>
|
||||
private int _numberOfTopicNodes = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset in the #STRINGS file of the ImageList param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
private int _imageListOffset = 0;
|
||||
/// <summary>
|
||||
/// True if the value of the ImageType param of the "text/site properties" object of the sitemap contents is "Folder".
|
||||
/// </summary>
|
||||
private bool _imageTypeFolder = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the background value
|
||||
/// </summary>
|
||||
private int _background = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the foreground value
|
||||
/// </summary>
|
||||
private int _foreground = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset in the #STRINGS file of the Font param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
private int _fontOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset in the #STRINGS file of the FrameName param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
private int _frameNameOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset in the #STRINGS file of the WindowName param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
private int _windowNameOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the number of merged files
|
||||
/// </summary>
|
||||
private int _numberOfMergedFiles = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset in the #STRINGS file of the merged file names
|
||||
/// </summary>
|
||||
private ArrayList _mergedFileOffsets = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #IDXHDR file</param>
|
||||
/// <param name="associatedFile">associated CHMFile instance</param>
|
||||
public CHMIdxhdr(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int nTemp = 0;
|
||||
|
||||
// 4 character T#SM
|
||||
binReader.ReadBytes(4);
|
||||
// unknown timestamp DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// unknown 1
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// number of topic nodes including the contents & index files
|
||||
_numberOfTopicNodes = binReader.ReadInt32();
|
||||
|
||||
// unknown DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// offset in the strings file
|
||||
_imageListOffset = binReader.ReadInt32();
|
||||
if( _imageListOffset == 0)
|
||||
_imageListOffset = -1; // 0/-1 = none
|
||||
|
||||
// unknown DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// 1 if the value of the ImageType param of the "text/site properties" object of the sitemap contents is "Folder".
|
||||
nTemp = binReader.ReadInt32();
|
||||
_imageTypeFolder = (nTemp == 1);
|
||||
|
||||
// offset in the strings file
|
||||
_background = binReader.ReadInt32();
|
||||
// offset in the strings file
|
||||
_foreground = binReader.ReadInt32();
|
||||
|
||||
// offset in the strings file
|
||||
_fontOffset = binReader.ReadInt32();
|
||||
|
||||
// window styles DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
// window styles DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// unknown DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// offset in the strings file
|
||||
_frameNameOffset = binReader.ReadInt32();
|
||||
if( _frameNameOffset == 0)
|
||||
_frameNameOffset = -1; // 0/-1 = none
|
||||
// offset in the strings file
|
||||
_windowNameOffset = binReader.ReadInt32();
|
||||
if( _windowNameOffset == 0)
|
||||
_windowNameOffset = -1; // 0/-1 = none
|
||||
|
||||
// informations types DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// unknown DWORD
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
// number of merged files in the merged file list DWORD
|
||||
_numberOfMergedFiles = binReader.ReadInt32();
|
||||
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
for(int i = 0; i < _numberOfMergedFiles; i++)
|
||||
{
|
||||
// DWORD offset value of merged file
|
||||
nTemp = binReader.ReadInt32();
|
||||
|
||||
if(nTemp > 0)
|
||||
_mergedFileOffsets.Add(nTemp);
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of topic nodes including the contents and index files
|
||||
/// </summary>
|
||||
public int NumberOfTopicNodes
|
||||
{
|
||||
get { return _numberOfTopicNodes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset in the #STRINGS file of the ImageList
|
||||
/// param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
public int ImageListOffset
|
||||
{
|
||||
get { return _imageListOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the value of the ImageType param of the
|
||||
/// "text/site properties" object of the sitemap contents is "Folder".
|
||||
/// </summary>
|
||||
/// <remarks>If this is set to true, the help will display folders instead of books</remarks>
|
||||
public bool ImageTypeFolder
|
||||
{
|
||||
get { return _imageTypeFolder; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the background setting
|
||||
/// </summary>
|
||||
public int Background
|
||||
{
|
||||
get { return _background; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the foreground setting
|
||||
/// </summary>
|
||||
public int Foreground
|
||||
{
|
||||
get { return _foreground; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset in the #STRINGS file of the Font
|
||||
/// param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
public int WindowNameOffset
|
||||
{
|
||||
get { return _fontOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset in the #STRINGS file of the FrameName
|
||||
/// param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
public int FrameNameOffset
|
||||
{
|
||||
get { return _frameNameOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset in the #STRINGS file of the WindowName
|
||||
/// param of the "text/site properties" object of the sitemap contents
|
||||
/// </summary>
|
||||
public int FontOffset
|
||||
{
|
||||
get { return _windowNameOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array list of offset numbers in the #STRINGS file of the
|
||||
/// merged file names.
|
||||
/// </summary>
|
||||
public ArrayList MergedFileOffsets
|
||||
{
|
||||
get { return _mergedFileOffsets; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
_mergedFileOffsets = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
256
irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs
Normal file
256
irc/TechBot/CHMLibrary/CHMDecoding/CHMStrings.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMStrings</c> implements a string collection read from the #STRINGS file
|
||||
/// </summary>
|
||||
internal sealed class CHMStrings : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant specifying the size of the string blocks
|
||||
/// </summary>
|
||||
private const int STRING_BLOCK_SIZE = 4096;
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the string dictionary
|
||||
/// </summary>
|
||||
private Hashtable _stringDictionary = new Hashtable();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #STRINGS file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMStrings(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
internal CHMStrings()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _stringDictionary.Count );
|
||||
|
||||
if (_stringDictionary.Count != 0)
|
||||
{
|
||||
IDictionaryEnumerator iDictionaryEnumerator = _stringDictionary.GetEnumerator();
|
||||
while (iDictionaryEnumerator.MoveNext())
|
||||
{
|
||||
DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;
|
||||
writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );
|
||||
writer.Write( dictionaryEntry.Value.ToString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(int i=0; i<nCnt;i++)
|
||||
{
|
||||
int nKey = reader.ReadInt32();
|
||||
string sValue = reader.ReadString();
|
||||
|
||||
_stringDictionary[nKey.ToString()] = sValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">instance to set</param>
|
||||
internal void SetCHMFile(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
//binReader.ReadByte(); // file starts with a NULL character for 0-based offset indexing
|
||||
|
||||
int nStringOffset = 0;
|
||||
int nSubsetOffset = 0;
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
nStringOffset = (int)memStream.Position;
|
||||
byte [] stringBlock = binReader.ReadBytes(STRING_BLOCK_SIZE);
|
||||
bRet &= DecodeBlock(stringBlock, ref nStringOffset, ref nSubsetOffset);
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a string block
|
||||
/// </summary>
|
||||
/// <param name="stringBlock">byte array which represents the string block</param>
|
||||
/// <param name="nStringOffset">current string offset number</param>
|
||||
/// <param name="nSubsetOffset">reference to a subset variable</param>
|
||||
/// <returns>true if succeeded</returns>
|
||||
/// <remarks>If a string crosses the end of a block then it will be cut off
|
||||
/// without a NT and repeated in full, with a NT, at the start of the next block.
|
||||
/// For eg "To customize the appearance of a contents file" might become
|
||||
/// "To customize the (block ending)To customize the appearance of a contents file"
|
||||
/// when there are 17 bytes left at the end of the block. </remarks>
|
||||
private bool DecodeBlock( byte[] stringBlock, ref int nStringOffset, ref int nSubsetOffset)
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(stringBlock);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
bool bFoundTerminator = false;
|
||||
|
||||
int nCurOffset = nStringOffset + (int)memStream.Position;
|
||||
|
||||
string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);
|
||||
|
||||
if(nSubsetOffset != 0)
|
||||
{
|
||||
_stringDictionary[nSubsetOffset.ToString()] = sTemp.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
_stringDictionary[nCurOffset.ToString()] = sTemp.ToString();
|
||||
}
|
||||
|
||||
if( bFoundTerminator )
|
||||
{
|
||||
nSubsetOffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nSubsetOffset = nCurOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indexer which returns the string at a given offset
|
||||
/// </summary>
|
||||
public string this[int offset]
|
||||
{
|
||||
get
|
||||
{
|
||||
if(offset == -1)
|
||||
return String.Empty;
|
||||
|
||||
string sTemp = (string)_stringDictionary[ offset.ToString() ];
|
||||
|
||||
if(sTemp == null)
|
||||
return String.Empty;
|
||||
|
||||
return sTemp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indexer which returns the string at a given offset
|
||||
/// </summary>
|
||||
public string this[string offset]
|
||||
{
|
||||
get
|
||||
{
|
||||
if(offset == "-1")
|
||||
return String.Empty;
|
||||
|
||||
string sTemp = (string)_stringDictionary[ offset ];
|
||||
|
||||
if(sTemp == null)
|
||||
return String.Empty;
|
||||
|
||||
return sTemp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
_stringDictionary = null;
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
821
irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs
Normal file
821
irc/TechBot/CHMLibrary/CHMDecoding/CHMSystem.cs
Normal file
@@ -0,0 +1,821 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMSystem</c> reads the #SYSTEM file of the chm and stores its settings
|
||||
/// </summary>
|
||||
internal sealed class CHMSystem : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the file version
|
||||
/// </summary>
|
||||
private int _fileVersion = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the contents file path
|
||||
/// </summary>
|
||||
private string _contentsFile = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the index file path
|
||||
/// </summary>
|
||||
private string _indexFile = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the default help topic
|
||||
/// </summary>
|
||||
private string _defaultTopic = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the help-window title
|
||||
/// </summary>
|
||||
private string _title = "";
|
||||
/// <summary>
|
||||
/// Internal flag if dbcs is on
|
||||
/// </summary>
|
||||
private bool _dbcs = false;
|
||||
/// <summary>
|
||||
/// Internal flag if fulltext search is enabled
|
||||
/// </summary>
|
||||
private bool _fullTextSearch = false;
|
||||
/// <summary>
|
||||
/// Internal flag if KLinks are in the file
|
||||
/// </summary>
|
||||
private bool _hasKLinks = false;
|
||||
/// <summary>
|
||||
/// Internal flag if ALinks are in the file
|
||||
/// </summary>
|
||||
private bool _hasALinks = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the name of the default window
|
||||
/// </summary>
|
||||
private string _defaultWindow = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the filename of the compiled file
|
||||
/// </summary>
|
||||
private string _compileFile = "";
|
||||
/// <summary>
|
||||
/// Internal flag storing the offset value of the binary index
|
||||
/// </summary>
|
||||
private uint _binaryIndexURLTableID = 0;
|
||||
/// <summary>
|
||||
/// Inernal member storing the compiler version this file was compiled
|
||||
/// </summary>
|
||||
private string _compilerVersion = "";
|
||||
/// <summary>
|
||||
/// Internal flag storing the offset value of the binary TOC
|
||||
/// </summary>
|
||||
private uint _binaryTOCURLTableID = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the default fontface, size, charset
|
||||
/// </summary>
|
||||
private string _defaultFont = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the culture info of the file
|
||||
/// </summary>
|
||||
private CultureInfo _culture;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #SYSTEM file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMSystem(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
|
||||
if(_culture == null)
|
||||
{
|
||||
// Set the text encoder of the chm file to the read charset/codepage
|
||||
_associatedFile.TextEncoding = Encoding.GetEncoding( this.CodePage );
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
// First entry = DWORD for version number
|
||||
_fileVersion = (int) binReader.ReadInt32();
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
bRet &= DecodeEntry(ref binReader);
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an #system file entry
|
||||
/// </summary>
|
||||
/// <param name="binReader">binary reader reference</param>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeEntry(ref BinaryReader binReader)
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
int code = (int) binReader.ReadInt16(); // entry code, WORD
|
||||
int length = (int) binReader.ReadInt16(); // length of entry
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
_contentsFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 1:
|
||||
{
|
||||
_indexFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 2:
|
||||
{
|
||||
_defaultTopic = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 3:
|
||||
{
|
||||
_title = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 4:
|
||||
{
|
||||
int nTemp = 0;
|
||||
nTemp = binReader.ReadInt32(); // read DWORD LCID
|
||||
_culture = new CultureInfo(nTemp);
|
||||
|
||||
if(_culture != null)
|
||||
_associatedFile.TextEncoding = Encoding.GetEncoding(_culture.TextInfo.ANSICodePage);
|
||||
|
||||
nTemp = binReader.ReadInt32(); // read DWORD DBCS
|
||||
_dbcs = (nTemp == 1);
|
||||
|
||||
nTemp = binReader.ReadInt32(); // read DWORD Fulltext search
|
||||
_fullTextSearch = (nTemp == 1);
|
||||
|
||||
nTemp = binReader.ReadInt32(); // read DWORD has klinks
|
||||
_hasKLinks = (nTemp != 0);
|
||||
|
||||
nTemp = binReader.ReadInt32(); // read DWORD has alinks
|
||||
_hasALinks = (nTemp != 0);
|
||||
|
||||
// read the rest of code 4 (not important for us)
|
||||
byte[] temp = new byte[length-(5*4)];
|
||||
temp = binReader.ReadBytes(length-(5*4));
|
||||
};break;
|
||||
case 5:
|
||||
{
|
||||
_defaultWindow = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 6:
|
||||
{
|
||||
_compileFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 7:
|
||||
{
|
||||
if(_fileVersion > 2)
|
||||
{
|
||||
_binaryIndexURLTableID = (uint) binReader.ReadInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
}
|
||||
};break;
|
||||
case 8:
|
||||
{
|
||||
// abbreviation (not interresting for us)
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
};break;
|
||||
case 9:
|
||||
{
|
||||
_compilerVersion = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
};break;
|
||||
case 10:
|
||||
{
|
||||
// timestamp of the file (not interresting for us)
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
};break;
|
||||
case 11:
|
||||
{
|
||||
if(_fileVersion > 2)
|
||||
{
|
||||
_binaryTOCURLTableID = (uint) binReader.ReadInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
}
|
||||
};break;
|
||||
case 12:
|
||||
{
|
||||
// number of information bytes
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
};break;
|
||||
case 13:
|
||||
{
|
||||
// copy of file #idxhdr
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
};break;
|
||||
case 14:
|
||||
{
|
||||
// custom tabs for HH viewer
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
};break;
|
||||
case 15:
|
||||
{
|
||||
// a checksum
|
||||
byte[] read = binReader.ReadBytes(length);
|
||||
int i=read.Length;
|
||||
};break;
|
||||
case 16:
|
||||
{
|
||||
// Default Font=string,number,number
|
||||
// The string is the name of the font, the first number is the
|
||||
// point size & the last number is the character set used by the font.
|
||||
// For acceptable values see *_CHARSET defines in wingdi.h from the
|
||||
// Windows SDK or the same file in MinGW or Wine.
|
||||
// Most of the time you will only want to use 0, which is the value for ANSI,
|
||||
// which is the subset of ASCII used by Windows.
|
||||
_defaultFont = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
|
||||
|
||||
};break;
|
||||
default:
|
||||
{
|
||||
byte[] temp = new byte[length];
|
||||
temp = binReader.ReadBytes(length);
|
||||
//bRet = false;
|
||||
int i=temp.Length;
|
||||
};break;
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all HHC files and checks which one has the global object tag.
|
||||
/// This hhc file will be returned as master hhc
|
||||
/// </summary>
|
||||
/// <param name="hhcTopics">list of topics containing the extension hhc</param>
|
||||
/// <param name="TopicItemArrayList">true if the arraylist contains topic items</param>
|
||||
/// <returns>the filename of the found master toc</returns>
|
||||
private string GetMasterHHC(ArrayList hhcTopics, bool TopicItemArrayList)
|
||||
{
|
||||
string sRet = "";
|
||||
|
||||
if( (hhcTopics!=null) && (hhcTopics.Count > 0) )
|
||||
{
|
||||
if( TopicItemArrayList )
|
||||
{
|
||||
if(hhcTopics.Count == 1)
|
||||
{
|
||||
sRet = ((TopicEntry)hhcTopics[0]).Locale;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(TopicEntry curEntry in hhcTopics)
|
||||
{
|
||||
CHMStream.CHMStream iw=null;
|
||||
MemoryStream fileObject=null;
|
||||
|
||||
if( _associatedFile.CurrentStorageWrapper == null)
|
||||
{
|
||||
iw=new CHMStream.CHMStream();
|
||||
iw.OpenCHM(_associatedFile.ChmFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
iw = _associatedFile.CurrentStorageWrapper;
|
||||
}
|
||||
|
||||
fileObject = iw.OpenStream(curEntry.Locale);
|
||||
if( fileObject != null)
|
||||
{
|
||||
string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
|
||||
fileObject.Close();
|
||||
|
||||
if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
|
||||
{
|
||||
sRet = curEntry.Locale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hhcTopics.Count == 1)
|
||||
{
|
||||
sRet = ((string)hhcTopics[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(string curEntry in hhcTopics)
|
||||
{
|
||||
CHMStream.CHMStream iw=null;
|
||||
MemoryStream fileObject=null;
|
||||
|
||||
if( _associatedFile.CurrentStorageWrapper == null)
|
||||
{
|
||||
iw=new CHMStream.CHMStream();
|
||||
iw.OpenCHM(_associatedFile.ChmFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
iw = _associatedFile.CurrentStorageWrapper;
|
||||
}
|
||||
|
||||
fileObject = iw.OpenStream(curEntry);
|
||||
if( fileObject != null)
|
||||
{
|
||||
string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
|
||||
fileObject.Close();
|
||||
|
||||
if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
|
||||
{
|
||||
sRet = curEntry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file version of the chm file.
|
||||
/// 2 for Compatibility=1.0, 3 for Compatibility=1.1
|
||||
/// </summary>
|
||||
public int FileVersion
|
||||
{
|
||||
get { return _fileVersion; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents file name
|
||||
/// </summary>
|
||||
public string ContentsFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if( BinaryTOC ) // if the file contains a binary TOC
|
||||
{
|
||||
// make sure the CHMFile instance exists and has loaded the file #URLTBL
|
||||
if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
|
||||
{
|
||||
// Get an url-table entry by its unique id
|
||||
UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryTOCURLTableID );
|
||||
if(entry != null)
|
||||
{
|
||||
// entry found, return the url ( = filename )
|
||||
return entry.URL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_contentsFile.Length <= 0)
|
||||
{
|
||||
string sCheck = "Table of Contents.hhc"; // default HHP contents filename
|
||||
|
||||
if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
|
||||
{
|
||||
TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
|
||||
|
||||
if( te == null)
|
||||
{
|
||||
sCheck = "toc.hhc"; // default HHP contents filename
|
||||
|
||||
te = _associatedFile.TopicsFile.GetByLocale( sCheck );
|
||||
|
||||
if( te == null)
|
||||
{
|
||||
sCheck = CompileFile + ".hhc";
|
||||
|
||||
te = _associatedFile.TopicsFile.GetByLocale( sCheck );
|
||||
|
||||
if( te == null)
|
||||
{
|
||||
ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhc");
|
||||
|
||||
if( arrExt == null )
|
||||
{
|
||||
arrExt = _associatedFile.EnumFilesByExtension("hhc");
|
||||
|
||||
if( arrExt == null )
|
||||
{
|
||||
Debug.WriteLine("CHMSystem.ContentsFile - Failed, contents file not found !");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(arrExt.Count > 1)
|
||||
{
|
||||
sCheck = GetMasterHHC(arrExt, false);
|
||||
_contentsFile = sCheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
_contentsFile = ((string)arrExt[0]);
|
||||
sCheck = _contentsFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(arrExt.Count > 1)
|
||||
{
|
||||
sCheck = GetMasterHHC(arrExt, true);
|
||||
_contentsFile = sCheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
_contentsFile = ((TopicEntry)arrExt[0]).Locale;
|
||||
sCheck = _contentsFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_contentsFile = sCheck;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_contentsFile = sCheck;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_contentsFile = sCheck;
|
||||
}
|
||||
}
|
||||
|
||||
return sCheck;
|
||||
}
|
||||
}
|
||||
|
||||
return _contentsFile;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index file name
|
||||
/// </summary>
|
||||
public string IndexFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if( BinaryIndex ) // if the file contains a binary index
|
||||
{
|
||||
// make sure the CHMFile instance exists and has loaded the file #URLTBL
|
||||
if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
|
||||
{
|
||||
// Get an url-table entry by its unique id
|
||||
UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryIndexURLTableID );
|
||||
if(entry != null)
|
||||
{
|
||||
// entry found, return the url ( = filename )
|
||||
return entry.URL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_indexFile.Length <= 0)
|
||||
{
|
||||
string sCheck = "Index.hhk"; // default HHP index filename
|
||||
|
||||
if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
|
||||
{
|
||||
TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
|
||||
|
||||
if( te == null)
|
||||
{
|
||||
sCheck = CompileFile + ".hhk";
|
||||
|
||||
te = _associatedFile.TopicsFile.GetByLocale( sCheck );
|
||||
|
||||
if( te == null)
|
||||
{
|
||||
ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhk");
|
||||
|
||||
if( arrExt == null )
|
||||
{
|
||||
Debug.WriteLine("CHMSystem.IndexFile - Failed, index file not found !");
|
||||
}
|
||||
else
|
||||
{
|
||||
_indexFile = ((TopicEntry)arrExt[0]).Locale;
|
||||
sCheck = _indexFile;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_indexFile = sCheck;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_indexFile = sCheck;
|
||||
}
|
||||
}
|
||||
|
||||
return sCheck;
|
||||
}
|
||||
}
|
||||
return _indexFile;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default topic of this file
|
||||
/// </summary>
|
||||
/// <param name="local">new local value of the topic</param>
|
||||
internal void SetDefaultTopic(string local)
|
||||
{
|
||||
_defaultTopic = local;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default help topic
|
||||
/// </summary>
|
||||
public string DefaultTopic
|
||||
{
|
||||
get { return _defaultTopic; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the help window
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get { return _title; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if DBCS is in use
|
||||
/// </summary>
|
||||
public bool DBCS
|
||||
{
|
||||
get { return _dbcs; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if full-text-search is available
|
||||
/// </summary>
|
||||
public bool FullTextSearch
|
||||
{
|
||||
get { return _fullTextSearch; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the file has ALinks
|
||||
/// </summary>
|
||||
public bool HasALinks
|
||||
{
|
||||
get { return _hasALinks; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the file has KLinks
|
||||
/// </summary>
|
||||
public bool HasKLinks
|
||||
{
|
||||
get { return _hasKLinks; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default window name
|
||||
/// </summary>
|
||||
public string DefaultWindow
|
||||
{
|
||||
get { return _defaultWindow; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name of the compile file
|
||||
/// </summary>
|
||||
public string CompileFile
|
||||
{
|
||||
get { return _compileFile; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the binary index in the url table
|
||||
/// </summary>
|
||||
public uint BinaryIndexURLTableID
|
||||
{
|
||||
get { return _binaryIndexURLTableID; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the chm has a binary index file
|
||||
/// </summary>
|
||||
public bool BinaryIndex
|
||||
{
|
||||
get { return (_binaryIndexURLTableID>0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the chm has a binary index file
|
||||
/// </summary>
|
||||
public string CompilerVersion
|
||||
{
|
||||
get { return _compilerVersion; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the binary toc in the url table
|
||||
/// </summary>
|
||||
public uint BinaryTOCURLTableID
|
||||
{
|
||||
get { return _binaryTOCURLTableID; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the chm has a binary toc file
|
||||
/// </summary>
|
||||
public bool BinaryTOC
|
||||
{
|
||||
get { return (_binaryTOCURLTableID>0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font face of the read font property.
|
||||
/// Empty string for default font.
|
||||
/// </summary>
|
||||
public string FontFace
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _defaultFont.Length > 0)
|
||||
{
|
||||
string [] fontSplit = _defaultFont.Split( new char[]{','});
|
||||
if(fontSplit.Length > 0)
|
||||
return fontSplit[0].Trim();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font size of the read font property.
|
||||
/// 0 for default font size
|
||||
/// </summary>
|
||||
public double FontSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _defaultFont.Length > 0)
|
||||
{
|
||||
string [] fontSplit = _defaultFont.Split( new char[]{','});
|
||||
if(fontSplit.Length > 1)
|
||||
return double.Parse(fontSplit[1].Trim());
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the character set of the read font property
|
||||
/// 1 for default
|
||||
/// </summary>
|
||||
public int CharacterSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _defaultFont.Length > 0)
|
||||
{
|
||||
string [] fontSplit = _defaultFont.Split( new char[]{','});
|
||||
if(fontSplit.Length > 2)
|
||||
return Int32.Parse(fontSplit[2].Trim());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the codepage depending on the read font property
|
||||
/// </summary>
|
||||
public int CodePage
|
||||
{
|
||||
get
|
||||
{
|
||||
// if we've read a LCID from the system file
|
||||
// ignore the font-property settings and return
|
||||
// the codepage generated from the culture info
|
||||
if(_culture != null)
|
||||
{
|
||||
return _culture.TextInfo.ANSICodePage;
|
||||
}
|
||||
|
||||
int nRet = 1252; // default codepage windows-1252
|
||||
|
||||
int nCSet = CharacterSet;
|
||||
|
||||
switch(nCSet)
|
||||
{
|
||||
case 0x00: nRet = 1252;break; // ANSI_CHARSET
|
||||
case 0xCC: nRet = 1251;break; // RUSSIAN_CHARSET
|
||||
case 0xEE: nRet = 1250;break; // EE_CHARSET
|
||||
case 0xA1: nRet = 1253;break; // GREEK_CHARSET
|
||||
case 0xA2: nRet = 1254;break; // TURKISH_CHARSET
|
||||
case 0xBA: nRet = 1257;break; // BALTIC_CHARSET
|
||||
case 0xB1: nRet = 1255;break; // HEBREW_CHARSET
|
||||
case 0xB2: nRet = 1256;break; // ARABIC_CHARSET
|
||||
case 0x80: nRet = 932;break; // SHIFTJIS_CHARSET
|
||||
case 0x81: nRet = 949;break; // HANGEUL_CHARSET
|
||||
case 0x86: nRet = 936;break; // GB2313_CHARSET
|
||||
case 0x88: nRet = 950;break; // CHINESEBIG5_CHARSET
|
||||
}
|
||||
|
||||
return nRet;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assiciated culture info
|
||||
/// </summary>
|
||||
public CultureInfo Culture
|
||||
{
|
||||
get { return _culture; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
288
irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs
Normal file
288
irc/TechBot/CHMLibrary/CHMDecoding/CHMTocidx.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMTocidx</c> implements functions to decode the #TOCIDX internal file.
|
||||
/// </summary>
|
||||
internal sealed class CHMTocidx : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant specifying the size of the data blocks
|
||||
/// </summary>
|
||||
private const int BLOCK_SIZE = 0x1000;
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal memebr storing the offset to the 20/28 byte structs
|
||||
/// </summary>
|
||||
private int _offset2028 = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset to the 16 byte structs
|
||||
/// </summary>
|
||||
private int _offset16structs = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the number of 16 byte structs
|
||||
/// </summary>
|
||||
private int _numberOf16structs = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset to the topic list
|
||||
/// </summary>
|
||||
private int _offsetOftopics = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the toc
|
||||
/// </summary>
|
||||
private ArrayList _toc = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member for offset seeking
|
||||
/// </summary>
|
||||
private Hashtable _offsetTable = new Hashtable();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #TOCIDX file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMTocidx(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
|
||||
// clear internal binary data after extraction
|
||||
_binaryFileData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
_toc = new ArrayList();
|
||||
_offsetTable = new Hashtable();
|
||||
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int nCurOffset = 0;
|
||||
|
||||
_offset2028 = binReader.ReadInt32();
|
||||
_offset16structs = binReader.ReadInt32();
|
||||
_numberOf16structs = binReader.ReadInt32();
|
||||
_offsetOftopics = binReader.ReadInt32();
|
||||
|
||||
binReader.BaseStream.Seek( _offset2028, SeekOrigin.Begin );
|
||||
|
||||
if( RecursivelyBuildTree(ref binReader, _offset2028, _toc, null) )
|
||||
{
|
||||
binReader.BaseStream.Seek( _offset16structs, SeekOrigin.Begin );
|
||||
nCurOffset = (int)binReader.BaseStream.Position;
|
||||
|
||||
for(int i=0; i < _numberOf16structs; i++)
|
||||
{
|
||||
int tocOffset = binReader.ReadInt32();
|
||||
int sqNr = binReader.ReadInt32();
|
||||
int topOffset = binReader.ReadInt32();
|
||||
int hhctopicIdx = binReader.ReadInt32();
|
||||
|
||||
nCurOffset = (int)binReader.BaseStream.Position;
|
||||
|
||||
int topicIdx = -1;
|
||||
// if the topic offset is within the range of the stream
|
||||
// and is >= the offset of the first topic dword
|
||||
if((topOffset < (binReader.BaseStream.Length - 4)) && (topOffset >= _offsetOftopics))
|
||||
{
|
||||
// read the index of the topic for this item
|
||||
binReader.BaseStream.Seek( topOffset, SeekOrigin.Begin);
|
||||
topicIdx = binReader.ReadInt32();
|
||||
binReader.BaseStream.Seek( nCurOffset, SeekOrigin.Begin);
|
||||
|
||||
|
||||
TOCItem item = (TOCItem)_offsetTable[tocOffset.ToString()];
|
||||
if( item != null)
|
||||
{
|
||||
if(( topicIdx < _associatedFile.TopicsFile.TopicTable.Count)&&(topicIdx>=0))
|
||||
{
|
||||
TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile.TopicTable[topicIdx]);
|
||||
if( (te != null) && (item.TopicOffset < 0) )
|
||||
{
|
||||
item.TopicOffset = te.EntryOffset;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively reads the binary toc tree from the file
|
||||
/// </summary>
|
||||
/// <param name="binReader">reference to binary reader</param>
|
||||
/// <param name="NodeOffset">offset of the first node in the current level</param>
|
||||
/// <param name="level">arraylist of TOCItems for the current level</param>
|
||||
/// <param name="parentItem">parent item for the item</param>
|
||||
/// <returns>Returns true if succeeded</returns>
|
||||
private bool RecursivelyBuildTree(ref BinaryReader binReader, int NodeOffset, ArrayList level, TOCItem parentItem)
|
||||
{
|
||||
bool bRet = true;
|
||||
int nextOffset=0;
|
||||
|
||||
int nReadOffset = (int)binReader.BaseStream.Position;
|
||||
|
||||
binReader.BaseStream.Seek(NodeOffset, SeekOrigin.Begin);
|
||||
|
||||
do
|
||||
{
|
||||
int nCurOffset = (int)binReader.BaseStream.Position;
|
||||
|
||||
int unkn1 = binReader.ReadInt16(); // unknown
|
||||
int unkn2 = binReader.ReadInt16(); // unknown
|
||||
|
||||
int flag = binReader.ReadInt32();
|
||||
|
||||
int nFolderAdd = 0;
|
||||
|
||||
if((_associatedFile != null) && (_associatedFile.ImageTypeFolder))
|
||||
{
|
||||
// get the value which should be added, to display folders instead of books
|
||||
if(HtmlHelpSystem.UseHH2TreePics)
|
||||
nFolderAdd = 8;
|
||||
else
|
||||
nFolderAdd = 4;
|
||||
}
|
||||
|
||||
int nFolderImgIdx = (HtmlHelpSystem.UseHH2TreePics ? (TOCItem.STD_FOLDER_HH2+nFolderAdd) : (TOCItem.STD_FOLDER_HH1+nFolderAdd));
|
||||
int nFileImgIdx = (HtmlHelpSystem.UseHH2TreePics ? TOCItem.STD_FILE_HH2 : TOCItem.STD_FILE_HH1);
|
||||
|
||||
int stdImage = ((flag & 0x4)!=0) ? nFolderImgIdx : nFileImgIdx;
|
||||
|
||||
int stringOffset = binReader.ReadInt32();
|
||||
|
||||
int ParentOffset = binReader.ReadInt32();
|
||||
nextOffset = binReader.ReadInt32();
|
||||
|
||||
int firstChildOffset = 0;
|
||||
int unkn3=0;
|
||||
|
||||
if( (flag&0x4)!=0 )
|
||||
{
|
||||
firstChildOffset = binReader.ReadInt32();
|
||||
unkn3 = binReader.ReadInt32(); // unknown
|
||||
}
|
||||
|
||||
TOCItem newItem = new TOCItem();
|
||||
newItem.ImageIndex = stdImage;
|
||||
newItem.Offset = nCurOffset;
|
||||
newItem.OffsetNext = nextOffset;
|
||||
newItem.AssociatedFile = _associatedFile;
|
||||
newItem.TocMode = DataMode.Binary;
|
||||
newItem.Parent = parentItem;
|
||||
|
||||
if( (flag&0x08) == 0)
|
||||
{
|
||||
// toc item doesn't have a local value (=> stringOffset = offset of strings file)
|
||||
newItem.Name = _associatedFile.StringsFile[stringOffset];
|
||||
}
|
||||
else
|
||||
{
|
||||
// this item has a topic entry (=> stringOffset = index of topic entry)
|
||||
if((stringOffset < _associatedFile.TopicsFile.TopicTable.Count) && (stringOffset >= 0))
|
||||
{
|
||||
TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile.TopicTable[stringOffset]);
|
||||
if(te != null)
|
||||
{
|
||||
newItem.TopicOffset = te.EntryOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_offsetTable[nCurOffset.ToString()] = newItem;
|
||||
|
||||
// if this item has children (firstChildOffset > 0)
|
||||
if( firstChildOffset > 0)
|
||||
{
|
||||
bRet &= RecursivelyBuildTree(ref binReader, firstChildOffset, newItem.Children, newItem);
|
||||
}
|
||||
|
||||
level.Add( newItem );
|
||||
|
||||
if(nCurOffset != nextOffset)
|
||||
binReader.BaseStream.Seek(nextOffset, SeekOrigin.Begin);
|
||||
|
||||
}while(nextOffset != 0);
|
||||
|
||||
binReader.BaseStream.Seek(nReadOffset, SeekOrigin.Begin);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal read toc
|
||||
/// </summary>
|
||||
internal ArrayList TOC
|
||||
{
|
||||
get { return _toc; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
_toc = null;
|
||||
_offsetTable = null;
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
235
irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs
Normal file
235
irc/TechBot/CHMLibrary/CHMDecoding/CHMTopics.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMTopics</c> implements functionality to decode the #TOPICS internal file
|
||||
/// </summary>
|
||||
internal sealed class CHMTopics : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the topic list
|
||||
/// </summary>
|
||||
private ArrayList _topicTable = new ArrayList();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #TOPICS file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMTopics(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
|
||||
// clear internal binary data after extraction
|
||||
_binaryFileData = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
internal CHMTopics()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _topicTable.Count );
|
||||
foreach(TopicEntry curItem in _topicTable)
|
||||
{
|
||||
curItem.Dump(ref writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
int i=0;
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt;i++)
|
||||
{
|
||||
TopicEntry newItem = new TopicEntry();
|
||||
newItem.SetCHMFile(_associatedFile);
|
||||
newItem.ReadDump(ref reader);
|
||||
_topicTable.Add(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">instance to set</param>
|
||||
internal void SetCHMFile(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
|
||||
foreach(TopicEntry curEntry in _topicTable)
|
||||
{
|
||||
curEntry.SetCHMFile(associatedFile);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int nCurOffset = 0;
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
int entryOffset = nCurOffset;
|
||||
int tocIdx = binReader.ReadInt32();
|
||||
int titleOffset = binReader.ReadInt32();
|
||||
int urltablOffset = binReader.ReadInt32();
|
||||
int visibilityMode = binReader.ReadInt16();
|
||||
int unknownMode = binReader.ReadInt16();
|
||||
|
||||
TopicEntry newEntry = new TopicEntry(entryOffset, tocIdx, titleOffset, urltablOffset, visibilityMode, unknownMode, _associatedFile);
|
||||
_topicTable.Add( newEntry );
|
||||
|
||||
nCurOffset = (int)memStream.Position;
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arraylist containing all topic entries.
|
||||
/// </summary>
|
||||
public ArrayList TopicTable
|
||||
{
|
||||
get
|
||||
{
|
||||
return _topicTable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the topic entry of a given offset
|
||||
/// </summary>
|
||||
public TopicEntry this[int offset]
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach(TopicEntry curEntry in _topicTable)
|
||||
if(curEntry.EntryOffset == offset)
|
||||
return curEntry;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches a topic by the locale name
|
||||
/// </summary>
|
||||
/// <param name="locale">locale name to search</param>
|
||||
/// <returns>The topicentry instance if found, otherwise null</returns>
|
||||
public TopicEntry GetByLocale(string locale)
|
||||
{
|
||||
foreach(TopicEntry curEntry in TopicTable)
|
||||
{
|
||||
if(curEntry.Locale.ToLower() == locale.ToLower())
|
||||
return curEntry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the topics for all files with a given file extension
|
||||
/// </summary>
|
||||
/// <param name="fileExtension">extension to search</param>
|
||||
/// <returns>An arraylist of TopicEntry instances or null if no topic was found</returns>
|
||||
public ArrayList GetByExtension(string fileExtension)
|
||||
{
|
||||
ArrayList arrRet = new ArrayList();
|
||||
|
||||
foreach(TopicEntry curEntry in TopicTable)
|
||||
{
|
||||
if(curEntry.Locale.ToLower().EndsWith(fileExtension.ToLower()))
|
||||
arrRet.Add(curEntry);
|
||||
}
|
||||
|
||||
if(arrRet.Count > 0)
|
||||
return arrRet;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
_topicTable=null;
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
308
irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs
Normal file
308
irc/TechBot/CHMLibrary/CHMDecoding/CHMUrlstr.cs
Normal file
@@ -0,0 +1,308 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMUrlstr</c> implements a string collection storing the URL strings of the help file
|
||||
/// </summary>
|
||||
internal sealed class CHMUrlstr : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant specifying the size of the string blocks
|
||||
/// </summary>
|
||||
private const int BLOCK_SIZE = 0x1000;
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the url dictionary
|
||||
/// </summary>
|
||||
private Hashtable _urlDictionary = new Hashtable();
|
||||
/// <summary>
|
||||
/// Internal member storing the framename dictionary
|
||||
/// </summary>
|
||||
private Hashtable _framenameDictionary = new Hashtable();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #URLSTR file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMUrlstr(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
|
||||
// clear internal binary data after extraction
|
||||
_binaryFileData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
internal CHMUrlstr()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _urlDictionary.Count );
|
||||
|
||||
if (_urlDictionary.Count != 0)
|
||||
{
|
||||
IDictionaryEnumerator iDictionaryEnumerator = _urlDictionary.GetEnumerator();
|
||||
while (iDictionaryEnumerator.MoveNext())
|
||||
{
|
||||
DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;
|
||||
writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );
|
||||
writer.Write( dictionaryEntry.Value.ToString() );
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write( _framenameDictionary.Count );
|
||||
|
||||
if (_framenameDictionary.Count != 0)
|
||||
{
|
||||
IDictionaryEnumerator iDictionaryEnumerator = _framenameDictionary.GetEnumerator();
|
||||
while (iDictionaryEnumerator.MoveNext())
|
||||
{
|
||||
DictionaryEntry dictionaryEntry = (DictionaryEntry)iDictionaryEnumerator.Current;
|
||||
writer.Write( Int32.Parse(dictionaryEntry.Key.ToString()) );
|
||||
writer.Write( dictionaryEntry.Value.ToString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
int i=0;
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt;i++)
|
||||
{
|
||||
int nKey = reader.ReadInt32();
|
||||
string sValue = reader.ReadString();
|
||||
|
||||
_urlDictionary[nKey.ToString()] = sValue;
|
||||
}
|
||||
|
||||
nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt;i++)
|
||||
{
|
||||
int nKey = reader.ReadInt32();
|
||||
string sValue = reader.ReadString();
|
||||
|
||||
_framenameDictionary[nKey.ToString()] = sValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">instance to set</param>
|
||||
internal void SetCHMFile(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int nCurOffset = 0;
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
nCurOffset = (int)memStream.Position;
|
||||
byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);
|
||||
bRet &= DecodeBlock(dataBlock, ref nCurOffset);
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a block of url-string data
|
||||
/// </summary>
|
||||
/// <param name="dataBlock">block of data</param>
|
||||
/// <param name="nOffset">current file offset</param>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeBlock( byte[] dataBlock, ref int nOffset )
|
||||
{
|
||||
bool bRet = true;
|
||||
int blockOffset = nOffset;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(dataBlock);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
if(nOffset==0)
|
||||
binReader.ReadByte(); // first block starts with an unknown byte
|
||||
|
||||
while( (memStream.Position < (memStream.Length-8)) && (bRet) )
|
||||
{
|
||||
int entryOffset = blockOffset + (int)memStream.Position;
|
||||
|
||||
int urlOffset = binReader.ReadInt32();
|
||||
int frameOffset = binReader.ReadInt32();
|
||||
|
||||
|
||||
// There is one way to tell where the end of the URL/FrameName
|
||||
// pairs occurs: Repeat the following: read 2 DWORDs and if both
|
||||
// are less than the current offset then this is the start of the Local
|
||||
// strings else skip two NT strings.
|
||||
// if(( (urlOffset < (entryOffset+8)) && (frameOffset < (entryOffset+8)) ))
|
||||
// {
|
||||
// //TODO: add correct string reading if an offset has been found
|
||||
// /*
|
||||
// int curOffset = (int)memStream.Position;
|
||||
//
|
||||
// memStream.Seek( (long)(blockOffset-urlOffset), SeekOrigin.Begin);
|
||||
// string sTemp = CHMReader.ExtractString(ref binReader, 0, true);
|
||||
//
|
||||
// memStream.Seek( (long)(blockOffset-frameOffset), SeekOrigin.Begin);
|
||||
// sTemp = CHMReader.ExtractString(ref binReader, 0, true);
|
||||
//
|
||||
// memStream.Seek((long)curOffset, SeekOrigin.Begin);
|
||||
// */
|
||||
//
|
||||
//
|
||||
// int curOffs = (int)memStream.Position;
|
||||
// BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding);
|
||||
// nOffset += (int)memStream.Position - curOffs;
|
||||
//
|
||||
// curOffs = (int)memStream.Position;
|
||||
// BinaryReaderHelp.ExtractString(ref binReader, 0, true, _associatedFile.TextEncoding);
|
||||
// nOffset += (int)memStream.Position - curOffs;
|
||||
// }
|
||||
// else
|
||||
{
|
||||
bool bFoundTerminator = false;
|
||||
|
||||
string sTemp = BinaryReaderHelp.ExtractString(ref binReader, ref bFoundTerminator, 0, true, _associatedFile.TextEncoding);
|
||||
|
||||
if(sTemp == "")
|
||||
{
|
||||
//nOffset = nOffset + 1 + (int)memStream.Length - (int)memStream.Position;
|
||||
memStream.Seek(memStream.Length-1, SeekOrigin.Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
_urlDictionary[entryOffset.ToString()] = sTemp.ToString();
|
||||
_framenameDictionary[ entryOffset.ToString() ] = sTemp.ToString() ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url at a given offset
|
||||
/// </summary>
|
||||
/// <param name="offset">offset of url</param>
|
||||
/// <returns>the url at the given offset</returns>
|
||||
public string GetURLatOffset(int offset)
|
||||
{
|
||||
if(offset == -1)
|
||||
return String.Empty;
|
||||
|
||||
string sTemp = (string)_urlDictionary[ offset.ToString() ];
|
||||
|
||||
if(sTemp == null)
|
||||
return String.Empty;
|
||||
|
||||
return sTemp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the framename at a given offset
|
||||
/// </summary>
|
||||
/// <param name="offset">offset of the framename</param>
|
||||
/// <returns>the frame name at the given offset</returns>
|
||||
public string GetFrameNameatOffset(int offset)
|
||||
{
|
||||
if(offset == -1)
|
||||
return String.Empty;
|
||||
|
||||
string sTemp = (string)_framenameDictionary[ offset.ToString() ];
|
||||
|
||||
if(sTemp == null)
|
||||
return String.Empty;
|
||||
|
||||
return sTemp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
_urlDictionary = null;
|
||||
_framenameDictionary = null;
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
245
irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs
Normal file
245
irc/TechBot/CHMLibrary/CHMDecoding/CHMUrltable.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>CHMUrltable</c> implements methods to decode the #URLTBL internal file.
|
||||
/// </summary>
|
||||
internal sealed class CHMUrltable : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant specifying the size of the data blocks
|
||||
/// </summary>
|
||||
private const int BLOCK_SIZE = 0x1000;
|
||||
/// <summary>
|
||||
/// Constant specifying the number of records per block
|
||||
/// </summary>
|
||||
private const int RECORDS_PER_BLOCK = 341;
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the object is going to be disposed
|
||||
/// </summary>
|
||||
private bool disposed = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the binary file data
|
||||
/// </summary>
|
||||
private byte[] _binaryFileData = null;
|
||||
/// <summary>
|
||||
/// Internal member storing the url table
|
||||
/// </summary>
|
||||
private ArrayList _urlTable = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="binaryFileData">binary file data of the #URLTBL file</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public CHMUrltable(byte[] binaryFileData, CHMFile associatedFile)
|
||||
{
|
||||
_binaryFileData = binaryFileData;
|
||||
_associatedFile = associatedFile;
|
||||
DecodeData();
|
||||
|
||||
// clear internal binary data after extraction
|
||||
_binaryFileData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
internal CHMUrltable()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _urlTable.Count );
|
||||
foreach(UrlTableEntry curItem in _urlTable)
|
||||
{
|
||||
curItem.Dump(ref writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
int i=0;
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt;i++)
|
||||
{
|
||||
UrlTableEntry newItem = new UrlTableEntry();
|
||||
newItem.SetCHMFile(_associatedFile);
|
||||
newItem.ReadDump(ref reader);
|
||||
_urlTable.Add(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">instance to set</param>
|
||||
internal void SetCHMFile(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
|
||||
foreach(UrlTableEntry curEntry in _urlTable)
|
||||
{
|
||||
curEntry.SetCHMFile(associatedFile);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the binary file data and fills the internal properties
|
||||
/// </summary>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeData()
|
||||
{
|
||||
bool bRet = true;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(_binaryFileData);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
int nCurOffset = 0;
|
||||
|
||||
while( (memStream.Position < memStream.Length) && (bRet) )
|
||||
{
|
||||
nCurOffset = (int)memStream.Position;
|
||||
byte [] dataBlock = binReader.ReadBytes(BLOCK_SIZE);
|
||||
bRet &= DecodeBlock(dataBlock, ref nCurOffset);
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a block of url-string data
|
||||
/// </summary>
|
||||
/// <param name="dataBlock">block of data</param>
|
||||
/// <param name="nOffset">current file offset</param>
|
||||
/// <returns>true if succeeded</returns>
|
||||
private bool DecodeBlock( byte[] dataBlock, ref int nOffset )
|
||||
{
|
||||
bool bRet = true;
|
||||
int blockOffset = nOffset;
|
||||
|
||||
MemoryStream memStream = new MemoryStream(dataBlock);
|
||||
BinaryReader binReader = new BinaryReader(memStream);
|
||||
|
||||
for(int i=0; i < RECORDS_PER_BLOCK; i++)
|
||||
{
|
||||
int recordOffset = blockOffset + (int)memStream.Position;
|
||||
|
||||
uint nuniqueID = (uint) binReader.ReadInt32(); // unknown dword
|
||||
int ntopicsIdx = binReader.ReadInt32();
|
||||
int urlstrOffset = binReader.ReadInt32();
|
||||
|
||||
UrlTableEntry newEntry = new UrlTableEntry(nuniqueID, recordOffset, ntopicsIdx, urlstrOffset, _associatedFile);
|
||||
_urlTable.Add(newEntry);
|
||||
|
||||
if( memStream.Position >= memStream.Length)
|
||||
break;
|
||||
}
|
||||
|
||||
if(dataBlock.Length == BLOCK_SIZE)
|
||||
binReader.ReadInt32();
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arraylist containing all urltable entries.
|
||||
/// </summary>
|
||||
public ArrayList UrlTable
|
||||
{
|
||||
get
|
||||
{
|
||||
return _urlTable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urltable entry of a given offset
|
||||
/// </summary>
|
||||
public UrlTableEntry this[int offset]
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach(UrlTableEntry curEntry in _urlTable)
|
||||
if(curEntry.EntryOffset == offset)
|
||||
return curEntry;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urltable entry of a given uniqueID
|
||||
/// </summary>
|
||||
public UrlTableEntry GetByUniqueID(uint uniqueID)
|
||||
{
|
||||
foreach(UrlTableEntry curEntry in UrlTable)
|
||||
{
|
||||
if(curEntry.UniqueID == uniqueID)
|
||||
return curEntry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// This object will be cleaned up by the Dispose method.
|
||||
// Therefore, you should call GC.SupressFinalize to
|
||||
// take this object off the finalization queue
|
||||
// and prevent finalization code for this object
|
||||
// from executing a second time.
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose(bool disposing) executes in two distinct scenarios.
|
||||
/// If disposing equals true, the method has been called directly
|
||||
/// or indirectly by a user's code. Managed and unmanaged resources
|
||||
/// can be disposed.
|
||||
/// If disposing equals false, the method has been called by the
|
||||
/// runtime from inside the finalizer and you should not reference
|
||||
/// other objects. Only unmanaged resources can be disposed.
|
||||
/// </summary>
|
||||
/// <param name="disposing">disposing flag</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if(!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if(disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
_binaryFileData = null;
|
||||
_urlTable = null;
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
395
irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs
Normal file
395
irc/TechBot/CHMLibrary/CHMDecoding/DumpingInfo.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
using ICSharpCode.SharpZipLib;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
|
||||
using HtmlHelp;
|
||||
// using HtmlHelp.Storage;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration for specifying the dumping compression
|
||||
/// </summary>
|
||||
public enum DumpCompression
|
||||
{
|
||||
/// <summary>
|
||||
/// None - no data compression will be used.
|
||||
/// Fastest but most memory intensive
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Minimum - a minimum data compression will be used.
|
||||
/// Fast but not much data reduction
|
||||
/// </summary>
|
||||
Minimum = 1,
|
||||
/// <summary>
|
||||
/// Medium - a medium data compression will be used.
|
||||
/// Slower but medium data reduction
|
||||
/// </summary>
|
||||
Medium = 2,
|
||||
/// <summary>
|
||||
/// Maximum - a maximum data compression will be used.
|
||||
/// Slowest but maximum data reduction
|
||||
/// </summary>
|
||||
Maximum = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags which specify which data should be dumped
|
||||
/// </summary>
|
||||
[FlagsAttribute()]
|
||||
public enum DumpingFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// DumpTextTOC - if this flag is set, text-based TOCs (sitemap format) will be dumped
|
||||
/// </summary>
|
||||
DumpTextTOC = 1,
|
||||
/// <summary>
|
||||
/// DumpBinaryTOC - if this flag is set, binary TOCs will be dumped
|
||||
/// </summary>
|
||||
DumpBinaryTOC = 2,
|
||||
/// <summary>
|
||||
/// DumpTextIndex - if this flag is set, the text-based index (sitemap format) will be dumped
|
||||
/// </summary>
|
||||
DumpTextIndex = 4,
|
||||
/// <summary>
|
||||
/// DumpBinaryIndex - if this flag is set, the binary index will be dumped
|
||||
/// </summary>
|
||||
DumpBinaryIndex = 8,
|
||||
/// <summary>
|
||||
/// DumpStrings - if this flag is set, the internal #STRINGS file will be dumped
|
||||
/// </summary>
|
||||
DumpStrings = 16,
|
||||
/// <summary>
|
||||
/// DumpUrlStr - if this flag is set, the internal #URLSTR file will be dumped
|
||||
/// </summary>
|
||||
DumpUrlStr = 32,
|
||||
/// <summary>
|
||||
/// DumpUrlTbl - if this flag is set, the internal #URLTBL file will be dumped
|
||||
/// </summary>
|
||||
DumpUrlTbl = 64,
|
||||
/// <summary>
|
||||
/// DumpTopics - if this flag is set, the internal #TOPICS file will be dumped
|
||||
/// </summary>
|
||||
DumpTopics = 128,
|
||||
/// <summary>
|
||||
/// DumpFullText - if this flag is set, the internal $FIftiMain file will be dumped
|
||||
/// </summary>
|
||||
DumpFullText = 256
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class <c>DumpingInfo</c> implements information properties for the CHMFile class
|
||||
/// if and how data dumping should be used.
|
||||
/// </summary>
|
||||
public sealed class DumpingInfo
|
||||
{
|
||||
public bool m_bAllowSaveDump=true;
|
||||
|
||||
private readonly static BitVector32.Section DumpFlags = BitVector32.CreateSection(512);
|
||||
|
||||
private const string _dumpHeader = "HtmlHelpSystem dump file 1.0";
|
||||
|
||||
private string _outputDir = ""; // emtpy string means, same directory as chm file
|
||||
private DumpCompression _compressionLevel = DumpCompression.Maximum;
|
||||
private CHMFile _chmFile = null;
|
||||
|
||||
private DeflaterOutputStream _outputStream = null;
|
||||
private InflaterInputStream _inputStream = null;
|
||||
|
||||
private BinaryWriter _writer = null;
|
||||
private BinaryReader _reader = null;
|
||||
|
||||
private BitVector32 _flags;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="flags">Combine flag values to specify which data should be dumped.</param>
|
||||
/// <param name="outputDir">output directory. emtpy string means,
|
||||
/// same directory as chm file (only if destination = ExternalFile)</param>
|
||||
/// <param name="compressionLevel">compression which should be used</param>
|
||||
public DumpingInfo(DumpingFlags flags, string outputDir, DumpCompression compressionLevel)
|
||||
{
|
||||
_flags = new BitVector32(0);
|
||||
int i = _flags[DumpFlags];
|
||||
_flags[DumpFlags] = i | (int)flags;
|
||||
|
||||
_outputDir = outputDir;
|
||||
_compressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if text-based TOCs will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpTextTOC
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTextTOC) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if binary TOCs will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpBinaryTOC
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpBinaryTOC) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the text-based index will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpTextIndex
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTextIndex) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the binary index will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpBinaryIndex
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpBinaryIndex) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the #STRINGS file will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpStrings
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpStrings) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the #URLSTR file will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpUrlStr
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpUrlStr) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the #URLTBL file will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpUrlTbl
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpUrlTbl) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the #TOPICS file will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpTopics
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpTopics) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the $FIftiMain file will be written to the dumping file
|
||||
/// </summary>
|
||||
public bool DumpFullText
|
||||
{
|
||||
get { return ((_flags[DumpFlags] & (int)DumpingFlags.DumpFullText) != 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dump output directory.
|
||||
/// </summary>
|
||||
/// <value>emtpy string means, same directory as chm file</value>
|
||||
/// <remarks>If Destination is set to DumpingOutput.InternalFile this property will be ignored</remarks>
|
||||
public string OutputDir
|
||||
{
|
||||
get { return _outputDir; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The compression level used.
|
||||
/// </summary>
|
||||
public DumpCompression CompressionLevel
|
||||
{
|
||||
get { return _compressionLevel; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the CHMFile instance associated with this object
|
||||
/// </summary>
|
||||
internal CHMFile ChmFile
|
||||
{
|
||||
get { return _chmFile; }
|
||||
set { _chmFile = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the compression level to the deflater constants
|
||||
/// </summary>
|
||||
private int CompLvl
|
||||
{
|
||||
get
|
||||
{
|
||||
switch(CompressionLevel)
|
||||
{
|
||||
case DumpCompression.None: return Deflater.NO_COMPRESSION;
|
||||
case DumpCompression.Minimum: return Deflater.BEST_SPEED;
|
||||
case DumpCompression.Medium: return Deflater.DEFAULT_COMPRESSION;
|
||||
case DumpCompression.Maximum: return Deflater.BEST_COMPRESSION;
|
||||
}
|
||||
|
||||
return Deflater.BEST_COMPRESSION;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a dump exists
|
||||
/// </summary>
|
||||
internal bool DumpExists
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_flags[DumpFlags] == 0)
|
||||
return false;
|
||||
|
||||
// we have a reader or writer to the dump so it must exist
|
||||
if( (_reader != null) || (_writer != null) )
|
||||
return true;
|
||||
|
||||
string sDmpFile = _chmFile.ChmFilePath;
|
||||
sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");
|
||||
|
||||
return File.Exists(sDmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a binary writer instance which allows you to write to the dump
|
||||
/// </summary>
|
||||
internal BinaryWriter Writer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_bAllowSaveDump==false)
|
||||
return null;
|
||||
|
||||
if(_flags[DumpFlags] == 0)
|
||||
throw new InvalidOperationException("Nothing to dump. No flags have been set !");
|
||||
|
||||
if(_reader != null)
|
||||
throw new InvalidOperationException("Can't write and read at the same time !");
|
||||
|
||||
if(_chmFile == null)
|
||||
throw new InvalidOperationException("Only usable with an associated CHMFile instance !");
|
||||
|
||||
if(_writer==null)
|
||||
{
|
||||
string sDmpFile = _chmFile.ChmFilePath;
|
||||
sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");
|
||||
StreamWriter stream = new StreamWriter(sDmpFile, false, _chmFile.TextEncoding);
|
||||
|
||||
// write header info uncompressed
|
||||
BinaryWriter _hwriter = new BinaryWriter(stream.BaseStream);
|
||||
_hwriter.Write(_dumpHeader);
|
||||
_hwriter.Write((int)CompressionLevel);
|
||||
|
||||
if(_compressionLevel == DumpCompression.None)
|
||||
{
|
||||
_writer = new BinaryWriter(stream.BaseStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
_outputStream = new DeflaterOutputStream(stream.BaseStream, new Deflater(CompLvl));
|
||||
|
||||
_writer = new BinaryWriter(_outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
return _writer;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a binary reader which allows you to read from the dump
|
||||
/// </summary>
|
||||
internal BinaryReader Reader
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_writer != null)
|
||||
throw new InvalidOperationException("Can't write and read at the same time !");
|
||||
|
||||
if(_chmFile == null)
|
||||
throw new InvalidOperationException("Only usable with an associated CHMFile instance !");
|
||||
|
||||
if(_reader==null)
|
||||
{
|
||||
string sDmpFile = _chmFile.ChmFilePath;
|
||||
sDmpFile=sDmpFile.ToLower().Replace(".chm",".CHB");
|
||||
StreamReader stream = new StreamReader(sDmpFile, _chmFile.TextEncoding);
|
||||
|
||||
BinaryReader _hReader = new BinaryReader(stream.BaseStream);
|
||||
string sH = _hReader.ReadString();
|
||||
|
||||
if(sH != _dumpHeader)
|
||||
{
|
||||
_hReader.Close();
|
||||
Debug.WriteLine("Unexpected dump-file header !");
|
||||
throw new FormatException("DumpingInfo.Reader - Unexpected dump-file header !");
|
||||
}
|
||||
|
||||
_compressionLevel = (DumpCompression)_hReader.ReadInt32();
|
||||
// if(_compressionLevel != (DumpCompression)_hReader.ReadInt32())
|
||||
// {
|
||||
// _hReader.Close();
|
||||
// return null;
|
||||
// }
|
||||
|
||||
if(_compressionLevel == DumpCompression.None)
|
||||
{
|
||||
_reader = new BinaryReader(stream.BaseStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputStream = new InflaterInputStream(stream.BaseStream, new Inflater());
|
||||
|
||||
_reader = new BinaryReader(_inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
return _reader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves data and closes the dump
|
||||
/// </summary>
|
||||
/// <returns>true if succeed</returns>
|
||||
internal bool SaveData()
|
||||
{
|
||||
if (m_bAllowSaveDump==false)
|
||||
return true;
|
||||
|
||||
if(_writer != null)
|
||||
{
|
||||
if(_writer!=null)
|
||||
_writer.Close();
|
||||
_outputStream = null;
|
||||
_writer = null;
|
||||
}
|
||||
|
||||
if(_reader != null)
|
||||
{
|
||||
if(_reader!=null)
|
||||
_reader.Close();
|
||||
_inputStream = null;
|
||||
_reader = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
1131
irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs
Normal file
1131
irc/TechBot/CHMLibrary/CHMDecoding/FullTextEngine.cs
Normal file
File diff suppressed because it is too large
Load Diff
593
irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs
Normal file
593
irc/TechBot/CHMLibrary/CHMDecoding/HHCParser.cs
Normal file
@@ -0,0 +1,593 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>HHCParser</c> implements a parser for HHC contents files.
|
||||
/// </summary>
|
||||
internal sealed class HHCParser
|
||||
{
|
||||
/// <summary>
|
||||
/// regular expressions for replacing the sitemap boundary tags
|
||||
/// </summary>
|
||||
private static string RE_ULOpening = @"\<ul\>"; // will be replaced by a '(' for nested parsing
|
||||
private static string RE_ULClosing = @"\</ul\>"; // will be replaced by a ')' for nested parsing
|
||||
|
||||
/// <summary>
|
||||
/// Matching ul-tags
|
||||
/// </summary>
|
||||
private static string RE_ULBoundaries = @"\<ul\>(?<innerText>.*)\</ul\>";
|
||||
/// <summary>
|
||||
/// Matching the nested tree structure.
|
||||
/// </summary>
|
||||
private static string RE_NestedBoundaries = @"\( (?> [^()]+ | \( (?<DEPTH>) | \) (?<-DEPTH>) )* (?(DEPTH)(?!)) \)";
|
||||
/// <summary>
|
||||
/// Matching object-tags
|
||||
/// </summary>
|
||||
private static string RE_ObjectBoundaries = @"\<object(?<innerText>.*?)\</object\>";
|
||||
/// <summary>
|
||||
/// Matching param tags
|
||||
/// </summary>
|
||||
private static string RE_ParamBoundaries = @"\<param(?<innerText>.*?)\>";
|
||||
/// <summary>
|
||||
/// Extracting tag attributes
|
||||
/// </summary>
|
||||
private const string RE_QuoteAttributes = @"( |\t)*(?<attributeName>[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?<attributeTD>[\""\'])?(?<attributeValue>.*?(?(attributeTD)\k<attributeTD>|([\s>]|.$)))";
|
||||
|
||||
/// <summary>
|
||||
/// private regular expressionobjects
|
||||
/// </summary>
|
||||
private static Regex ulRE;
|
||||
private static Regex NestedRE;
|
||||
private static Regex ObjectRE;
|
||||
private static Regex ParamRE;
|
||||
private static Regex AttributesRE;
|
||||
|
||||
/// <summary>
|
||||
/// Internal member storing the list of TOCItems which are holding merge links
|
||||
/// </summary>
|
||||
private static ArrayList _mergeItems = null;
|
||||
|
||||
/// <summary>
|
||||
/// Internal member storing the last read regular topic item.
|
||||
/// This is used to handle "Merge" entries and add them as child to this instance.
|
||||
/// </summary>
|
||||
private static TOCItem _lastTopicItem = null;
|
||||
|
||||
/// <summary>
|
||||
/// Parses a HHC file and returns an ArrayList with the table of contents (TOC) tree
|
||||
/// </summary>
|
||||
/// <param name="hhcFile">string content of the hhc file</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
/// <returns>Returns an ArrayList with the table of contents (TOC) tree</returns>
|
||||
public static ArrayList ParseHHC(string hhcFile, CHMFile chmFile)
|
||||
{
|
||||
_lastTopicItem = null;
|
||||
_mergeItems = null; // clear merged item list
|
||||
ArrayList tocList = new ArrayList();
|
||||
|
||||
ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
NestedRE = new Regex(RE_NestedBoundaries, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
int innerTextIdx = ulRE.GroupNumberFromName("innerText");
|
||||
|
||||
if( ulRE.IsMatch(hhcFile, 0) )
|
||||
{
|
||||
Match m = ulRE.Match(hhcFile, 0);
|
||||
|
||||
int nFirstUL = 0;
|
||||
|
||||
nFirstUL = hhcFile.ToLower().IndexOf("<ul>");
|
||||
|
||||
if(nFirstUL == -1)
|
||||
nFirstUL = hhcFile.ToLower().IndexOf("<il>");
|
||||
|
||||
if( ObjectRE.IsMatch(hhcFile, 0) ) // first object block contains information types and categories
|
||||
{
|
||||
Match mO = ObjectRE.Match(hhcFile, 0);
|
||||
int iOTxt = ObjectRE.GroupNumberFromName("innerText");
|
||||
|
||||
string globalText = mO.Groups[iOTxt].Value;
|
||||
|
||||
if( mO.Groups[iOTxt].Index <= nFirstUL)
|
||||
ParseGlobalSettings( globalText, chmFile );
|
||||
}
|
||||
|
||||
// parse toc tree
|
||||
string innerText = m.Groups["innerText"].Value;
|
||||
|
||||
innerText = innerText.Replace("(", "(");
|
||||
innerText = innerText.Replace(")", ")");
|
||||
innerText = Regex.Replace(innerText, RE_ULOpening, "(", RegexOptions.IgnoreCase);
|
||||
innerText = Regex.Replace(innerText, RE_ULClosing, ")", RegexOptions.IgnoreCase);
|
||||
|
||||
ParseTree( innerText, null, tocList, chmFile );
|
||||
|
||||
}
|
||||
|
||||
return tocList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the hhc file contains a global object tag.
|
||||
/// </summary>
|
||||
/// <param name="hhcFile">string content of the hhc file</param>
|
||||
/// <param name="chmFile">chm file</param>
|
||||
/// <returns>true if the hhc content contains a global object tag</returns>
|
||||
public static bool HasGlobalObjectTag(string hhcFile, CHMFile chmFile)
|
||||
{
|
||||
bool bRet = false;
|
||||
|
||||
ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
int innerTextIdx = ulRE.GroupNumberFromName("innerText");
|
||||
|
||||
if( ulRE.IsMatch(hhcFile, 0) )
|
||||
{
|
||||
Match m = ulRE.Match(hhcFile, 0);
|
||||
|
||||
int nFirstUL = 0;
|
||||
|
||||
nFirstUL = hhcFile.ToLower().IndexOf("<ul>");
|
||||
|
||||
if(nFirstUL == -1)
|
||||
nFirstUL = hhcFile.ToLower().IndexOf("<il>");
|
||||
|
||||
if( ObjectRE.IsMatch(hhcFile, 0) ) // first object block contains information types and categories
|
||||
{
|
||||
Match mO = ObjectRE.Match(hhcFile, 0);
|
||||
int iOTxt = ObjectRE.GroupNumberFromName("innerText");
|
||||
|
||||
string globalText = mO.Groups[iOTxt].Value;
|
||||
|
||||
if( mO.Groups[iOTxt].Index <= nFirstUL)
|
||||
bRet = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets true if the previously done parsing found merge-links
|
||||
/// </summary>
|
||||
public static bool HasMergeLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_mergeItems==null)
|
||||
return false;
|
||||
|
||||
return _mergeItems.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all TOCItem references which are holding merge-links
|
||||
/// </summary>
|
||||
public static ArrayList MergeItems
|
||||
{
|
||||
get { return _mergeItems; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively parses a sitemap tree
|
||||
/// </summary>
|
||||
/// <param name="text">content text</param>
|
||||
/// <param name="parent">Parent for all read items</param>
|
||||
/// <param name="arrNodes">arraylist which receives the extracted nodes</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static void ParseTree( string text, TOCItem parent, ArrayList arrNodes, CHMFile chmFile )
|
||||
{
|
||||
string strPreItems="", strPostItems="";
|
||||
string innerText = "";
|
||||
|
||||
int nIndex = 0;
|
||||
|
||||
while( NestedRE.IsMatch(text, nIndex) )
|
||||
{
|
||||
Match m = NestedRE.Match(text, nIndex);
|
||||
|
||||
innerText = m.Value.Substring( 1, m.Length-2);
|
||||
|
||||
strPreItems = text.Substring(nIndex,m.Index-nIndex);
|
||||
|
||||
ParseItems(strPreItems, parent, arrNodes, chmFile);
|
||||
|
||||
if((arrNodes.Count>0) && (innerText.Length > 0) )
|
||||
{
|
||||
TOCItem p = ((TOCItem)(arrNodes[arrNodes.Count-1]));
|
||||
ParseTree( innerText, p, p.Children, chmFile );
|
||||
}
|
||||
|
||||
nIndex = m.Index+m.Length;
|
||||
}
|
||||
|
||||
if( nIndex == 0)
|
||||
{
|
||||
strPostItems = text.Substring(nIndex, text.Length-nIndex);
|
||||
ParseItems(strPostItems, parent, arrNodes, chmFile);
|
||||
}
|
||||
else if( nIndex < text.Length-1)
|
||||
{
|
||||
strPostItems = text.Substring(nIndex, text.Length-nIndex);
|
||||
ParseTree(strPostItems, parent, arrNodes, chmFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses tree nodes from the text
|
||||
/// </summary>
|
||||
/// <param name="itemstext">text containing the items</param>
|
||||
/// <param name="parent">Parent for all read items</param>
|
||||
/// <param name="arrNodes">arraylist where the nodes should be added</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static void ParseItems( string itemstext, TOCItem parent, ArrayList arrNodes, CHMFile chmFile)
|
||||
{
|
||||
int innerTextIdx = ObjectRE.GroupNumberFromName("innerText");
|
||||
int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
|
||||
|
||||
// get group-name indexes
|
||||
int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
|
||||
int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
|
||||
int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
|
||||
|
||||
int nObjStartIndex = 0;
|
||||
|
||||
while( ObjectRE.IsMatch(itemstext, nObjStartIndex) )
|
||||
{
|
||||
Match m = ObjectRE.Match(itemstext, nObjStartIndex);
|
||||
|
||||
string innerText = m.Groups[innerTextIdx].Value;
|
||||
|
||||
TOCItem tocItem = new TOCItem();
|
||||
tocItem.TocMode = DataMode.TextBased;
|
||||
tocItem.AssociatedFile = chmFile;
|
||||
tocItem.Parent = parent;
|
||||
|
||||
// read parameters
|
||||
int nParamIndex = 0;
|
||||
|
||||
while( ParamRE.IsMatch(innerText, nParamIndex) )
|
||||
{
|
||||
Match mP = ParamRE.Match(innerText, nParamIndex);
|
||||
|
||||
string innerP = mP.Groups[innerPTextIdx].Value;
|
||||
|
||||
string paramName = "";
|
||||
string paramValue = "";
|
||||
|
||||
int nAttrIdx = 0;
|
||||
|
||||
while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
|
||||
{
|
||||
Match mA = AttributesRE.Match(innerP, nAttrIdx);
|
||||
|
||||
string attributeName = mA.Groups[nameIndex].Value;
|
||||
string attributeValue = mA.Groups[valueIndex].Value;
|
||||
string attributeTD = mA.Groups[tdIndex].Value;
|
||||
|
||||
if(attributeTD.Length > 0)
|
||||
{
|
||||
// delete the trailing textqualifier
|
||||
if( attributeValue.Length > 0)
|
||||
{
|
||||
int ltqi = attributeValue.LastIndexOf( attributeTD );
|
||||
|
||||
if(ltqi >= 0)
|
||||
{
|
||||
attributeValue = attributeValue.Substring(0,ltqi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "name")
|
||||
{
|
||||
paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "value")
|
||||
{
|
||||
paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
// delete trailing /
|
||||
while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))
|
||||
paramValue = paramValue.Substring(0,paramValue.Length-1);
|
||||
|
||||
}
|
||||
|
||||
nAttrIdx = mA.Index+mA.Length;
|
||||
}
|
||||
|
||||
tocItem.Params[paramName] = paramValue;
|
||||
switch(paramName.ToLower())
|
||||
{
|
||||
case "name":
|
||||
{
|
||||
tocItem.Name = paramValue;
|
||||
};break;
|
||||
case "local":
|
||||
{
|
||||
tocItem.Local = paramValue.Replace("../", "").Replace("./", "");
|
||||
};break;
|
||||
case "imagenumber":
|
||||
{
|
||||
tocItem.ImageIndex = Int32.Parse(paramValue);
|
||||
tocItem.ImageIndex-=1;
|
||||
|
||||
int nFolderAdd = 0;
|
||||
|
||||
if((chmFile != null) && (chmFile.ImageTypeFolder))
|
||||
{
|
||||
// get the value which should be added, to display folders instead of books
|
||||
if(HtmlHelpSystem.UseHH2TreePics)
|
||||
nFolderAdd = 8;
|
||||
else
|
||||
nFolderAdd = 4;
|
||||
}
|
||||
|
||||
if(tocItem.ImageIndex%2 != 0)
|
||||
{
|
||||
if(tocItem.ImageIndex==1)
|
||||
tocItem.ImageIndex=0;
|
||||
}
|
||||
if(HtmlHelpSystem.UseHH2TreePics)
|
||||
if( tocItem.ImageIndex == 0)
|
||||
tocItem.ImageIndex = TOCItem.STD_FOLDER_HH2+nFolderAdd;
|
||||
};break;
|
||||
case "merge": // this item contains topics or a full TOC from a merged CHM
|
||||
{
|
||||
tocItem.MergeLink = paramValue;
|
||||
|
||||
// "register" this item as merge-link
|
||||
if(_mergeItems==null)
|
||||
_mergeItems=new ArrayList();
|
||||
|
||||
_mergeItems.Add(tocItem);
|
||||
|
||||
};break;
|
||||
case "type": // information type assignment for item
|
||||
{
|
||||
tocItem.InfoTypeStrings.Add( paramValue );
|
||||
};break;
|
||||
}
|
||||
|
||||
nParamIndex = mP.Index+mP.Length;
|
||||
}
|
||||
|
||||
tocItem.ChmFile = chmFile.ChmFilePath;
|
||||
|
||||
if(tocItem.MergeLink.Length > 0)
|
||||
{
|
||||
if(_lastTopicItem != null)
|
||||
{
|
||||
tocItem.Parent = _lastTopicItem;
|
||||
_lastTopicItem.Children.Add(tocItem);
|
||||
}
|
||||
else
|
||||
arrNodes.Add( tocItem );
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastTopicItem = tocItem;
|
||||
arrNodes.Add( tocItem );
|
||||
}
|
||||
|
||||
nObjStartIndex = m.Index+m.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the very first <OBJECT> tag in the sitemap file and extracts
|
||||
/// information types and categories.
|
||||
/// </summary>
|
||||
/// <param name="sText">text of the object tag</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static void ParseGlobalSettings(string sText, CHMFile chmFile)
|
||||
{
|
||||
int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
|
||||
|
||||
// get group-name indexes
|
||||
int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
|
||||
int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
|
||||
int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
|
||||
|
||||
// read parameters
|
||||
int nParamIndex = 0;
|
||||
|
||||
// 0... unknown
|
||||
// 1... inclusinve info type name
|
||||
// 2... exclusive info type name
|
||||
// 3... hidden info type name
|
||||
// 4... category name
|
||||
// 5... incl infotype name for category
|
||||
// 6... excl infotype name for category
|
||||
// 7... hidden infotype name for category
|
||||
int prevItem = 0;
|
||||
|
||||
string sName = "";
|
||||
string sDescription = "";
|
||||
string curCategory = "";
|
||||
|
||||
while( ParamRE.IsMatch(sText, nParamIndex) )
|
||||
{
|
||||
Match mP = ParamRE.Match(sText, nParamIndex);
|
||||
|
||||
string innerP = mP.Groups[innerPTextIdx].Value;
|
||||
|
||||
string paramName = "";
|
||||
string paramValue = "";
|
||||
|
||||
int nAttrIdx = 0;
|
||||
|
||||
while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
|
||||
{
|
||||
Match mA = AttributesRE.Match(innerP, nAttrIdx);
|
||||
|
||||
string attributeName = mA.Groups[nameIndex].Value;
|
||||
string attributeValue = mA.Groups[valueIndex].Value;
|
||||
string attributeTD = mA.Groups[tdIndex].Value;
|
||||
|
||||
if(attributeTD.Length > 0)
|
||||
{
|
||||
// delete the trailing textqualifier
|
||||
if( attributeValue.Length > 0)
|
||||
{
|
||||
int ltqi = attributeValue.LastIndexOf( attributeTD );
|
||||
|
||||
if(ltqi >= 0)
|
||||
{
|
||||
attributeValue = attributeValue.Substring(0,ltqi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "name")
|
||||
{
|
||||
paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "value")
|
||||
{
|
||||
paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
// delete trailing /
|
||||
while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))
|
||||
paramValue = paramValue.Substring(0,paramValue.Length-1);
|
||||
|
||||
}
|
||||
|
||||
nAttrIdx = mA.Index+mA.Length;
|
||||
}
|
||||
|
||||
switch(paramName.ToLower())
|
||||
{
|
||||
case "savetype": // inclusive information type name
|
||||
{
|
||||
prevItem = 1;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "savetypedesc": // description of information type
|
||||
{
|
||||
InformationTypeMode mode = InformationTypeMode.Inclusive;
|
||||
sDescription = paramValue;
|
||||
|
||||
if( prevItem == 1)
|
||||
mode = InformationTypeMode.Inclusive;
|
||||
if( prevItem == 2)
|
||||
mode = InformationTypeMode.Exclusive;
|
||||
if( prevItem == 3)
|
||||
mode = InformationTypeMode.Hidden;
|
||||
|
||||
if( chmFile.GetInformationType( sName ) == null)
|
||||
{
|
||||
// check if the HtmlHelpSystem already holds such an information type
|
||||
if( chmFile.SystemInstance.GetInformationType( sName ) == null)
|
||||
{
|
||||
// info type not found yet
|
||||
|
||||
InformationType newType = new InformationType(sName, sDescription, mode);
|
||||
chmFile.InformationTypes.Add(newType);
|
||||
}
|
||||
else
|
||||
{
|
||||
InformationType sysType = chmFile.SystemInstance.GetInformationType( sName );
|
||||
chmFile.InformationTypes.Add( sysType );
|
||||
}
|
||||
}
|
||||
|
||||
prevItem = 0;
|
||||
};break;
|
||||
case "saveexclusive": // exclusive information type name
|
||||
{
|
||||
prevItem = 2;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "savehidden": // hidden information type name
|
||||
{
|
||||
prevItem = 3;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "category": // category name
|
||||
{
|
||||
prevItem = 4;
|
||||
sName = paramValue;
|
||||
curCategory = sName;
|
||||
};break;
|
||||
case "categorydesc": // category description
|
||||
{
|
||||
sDescription = paramValue;
|
||||
|
||||
if( chmFile.GetCategory( sName ) == null)
|
||||
{
|
||||
// check if the HtmlHelpSystem already holds such a category
|
||||
if( chmFile.SystemInstance.GetCategory( sName ) == null)
|
||||
{
|
||||
// add category
|
||||
Category newCat = new Category(sName, sDescription);
|
||||
chmFile.Categories.Add(newCat);
|
||||
}
|
||||
else
|
||||
{
|
||||
Category sysCat = chmFile.SystemInstance.GetCategory( sName );
|
||||
chmFile.Categories.Add( sysCat );
|
||||
}
|
||||
}
|
||||
|
||||
prevItem = 0;
|
||||
};break;
|
||||
case "type": // inclusive information type which is member of the previously read category
|
||||
{
|
||||
prevItem = 5;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "typedesc": // description of type for category
|
||||
{
|
||||
sDescription = paramValue;
|
||||
Category cat = chmFile.GetCategory( curCategory );
|
||||
|
||||
if( cat != null)
|
||||
{
|
||||
// category found
|
||||
InformationType infoType = chmFile.GetInformationType( sName );
|
||||
|
||||
if( infoType != null)
|
||||
{
|
||||
if( !cat.ContainsInformationType(infoType))
|
||||
{
|
||||
infoType.SetCategoryFlag(true);
|
||||
cat.AddInformationType(infoType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevItem = 0;
|
||||
};break;
|
||||
case "typeexclusive": // exclusive information type which is member of the previously read category
|
||||
{
|
||||
prevItem = 6;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "typehidden": // hidden information type which is member of the previously read category
|
||||
{
|
||||
prevItem = 7;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
default:
|
||||
{
|
||||
prevItem = 0;
|
||||
sName = "";
|
||||
sDescription = "";
|
||||
};break;
|
||||
}
|
||||
|
||||
nParamIndex = mP.Index+mP.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
220
irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs
Normal file
220
irc/TechBot/CHMLibrary/CHMDecoding/HHCParser2.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>HHCParser</c> implements a parser for HHC contents files.
|
||||
/// </summary>
|
||||
// internal sealed class HHCParser : IHHCParser
|
||||
public class HHCParser2
|
||||
{
|
||||
static private string m_text1="";
|
||||
static private string m_text2="";
|
||||
static private int m_CurrentPos=0;
|
||||
|
||||
/// <summary>
|
||||
/// Parses a HHC file and returns an ArrayList with the table of contents (TOC) tree
|
||||
/// </summary>
|
||||
/// <param name="hhcFile">string content of the hhc file</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
/// <returns>Returns an ArrayList with the table of contents (TOC) tree</returns>
|
||||
public static ArrayList ParseHHC(string hhcFile, CHMFile chmFile)
|
||||
{
|
||||
DateTime StartTime=DateTime.Now;
|
||||
|
||||
ArrayList tocList = new ArrayList();
|
||||
|
||||
m_text2=hhcFile;
|
||||
m_text1=hhcFile.ToLower();
|
||||
|
||||
int idx=m_text1.IndexOf("<ul>");
|
||||
if (idx==-1)
|
||||
return null;
|
||||
m_CurrentPos=idx+4;
|
||||
|
||||
ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
ParseTree(tocList,chmFile);
|
||||
|
||||
DateTime EndTime=DateTime.Now;
|
||||
TimeSpan Diff=EndTime-StartTime;
|
||||
string x=Diff.ToString();
|
||||
|
||||
return tocList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively parses a sitemap tree
|
||||
/// </summary>
|
||||
/// <param name="text">content text</param>
|
||||
/// <param name="arrNodes">arraylist which receives the extracted nodes</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
static private void ParseTree( ArrayList arrNodes, CHMFile chmFile )
|
||||
{
|
||||
bool bProcessing=true;
|
||||
do
|
||||
{
|
||||
bProcessing=false;
|
||||
|
||||
// Indent
|
||||
int idxa=m_text1.IndexOf("<ul>",m_CurrentPos);
|
||||
int idxb=m_text1.IndexOf("<li>",m_CurrentPos);
|
||||
int idxc=m_text1.IndexOf("</ul>",m_CurrentPos);
|
||||
|
||||
if ((idxa<idxb) && (idxa<idxc) && (idxa>-1))
|
||||
{
|
||||
bProcessing=true;
|
||||
m_CurrentPos=idxa+4;
|
||||
if (arrNodes.Count<1)
|
||||
{
|
||||
ParseTree(arrNodes,chmFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseTree(((TOCItem)(arrNodes[arrNodes.Count-1])).Children,chmFile);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// new item
|
||||
if ((idxb<idxa) && (idxb<idxc) && (idxb>-1))
|
||||
{
|
||||
|
||||
bProcessing=true;
|
||||
m_CurrentPos=idxb+4;
|
||||
|
||||
int idx2=m_text1.IndexOf("<object",m_CurrentPos);
|
||||
if (idx2!=-1)
|
||||
{
|
||||
int idx3=m_text1.IndexOf("</object>",idx2+7);
|
||||
if (idx3!=-1)
|
||||
{
|
||||
string text=m_text2.Substring(idx2,idx3-idx2);
|
||||
|
||||
m_CurrentPos=idx3+9;
|
||||
|
||||
// Parse items in text.
|
||||
TOCItem tocItem=ParseItems(text, chmFile);
|
||||
if (tocItem!=null)
|
||||
{
|
||||
arrNodes.Add(tocItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Undent
|
||||
if ((idxc<idxa) && (idxc<idxb) && (idxc>-1))
|
||||
{
|
||||
m_CurrentPos=idxc+5;
|
||||
bProcessing=true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (bProcessing);
|
||||
}
|
||||
|
||||
|
||||
private static string RE_ParamBoundaries = @"\<param(?<innerText>.*?)\>";
|
||||
private const string RE_QuoteAttributes = @"( |\t)*(?<attributeName>[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?<attributeTD>[\""\'])?(?<attributeValue>.*?(?(attributeTD)\k<attributeTD>|([\s>]|.$)))";
|
||||
private static Regex ParamRE;
|
||||
private static Regex AttributesRE;
|
||||
|
||||
/// <summary>
|
||||
/// Parses tree nodes from the text
|
||||
/// </summary>
|
||||
/// <param name="itemstext">text containing the items</param>
|
||||
/// <param name="arrNodes">arraylist where the nodes should be added</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static TOCItem ParseItems( string itemstext, CHMFile chmFile)
|
||||
{
|
||||
int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
|
||||
|
||||
// get group-name indexes
|
||||
int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
|
||||
int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
|
||||
int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
|
||||
|
||||
TOCItem tocItem = new TOCItem();
|
||||
|
||||
// read parameters
|
||||
int nParamIndex = 0;
|
||||
|
||||
while( ParamRE.IsMatch(itemstext, nParamIndex) )
|
||||
{
|
||||
Match mP = ParamRE.Match(itemstext, nParamIndex);
|
||||
|
||||
string innerP = mP.Groups[innerPTextIdx].Value;
|
||||
|
||||
string paramName = "";
|
||||
string paramValue = "";
|
||||
|
||||
int nAttrIdx = 0;
|
||||
|
||||
while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
|
||||
{
|
||||
Match mA = AttributesRE.Match(innerP, nAttrIdx);
|
||||
|
||||
string attributeName = mA.Groups[nameIndex].Value;
|
||||
string attributeValue = mA.Groups[valueIndex].Value;
|
||||
string attributeTD = mA.Groups[tdIndex].Value;
|
||||
|
||||
if(attributeTD.Length > 0)
|
||||
{
|
||||
// delete the trailing textqualifier
|
||||
if( attributeValue.Length > 0)
|
||||
{
|
||||
int ltqi = attributeValue.LastIndexOf( attributeTD );
|
||||
|
||||
if(ltqi >= 0)
|
||||
{
|
||||
attributeValue = attributeValue.Substring(0,ltqi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "name")
|
||||
{
|
||||
paramName = attributeValue;
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "value")
|
||||
{
|
||||
paramValue = attributeValue;
|
||||
}
|
||||
|
||||
nAttrIdx = mA.Index+mA.Length;
|
||||
}
|
||||
|
||||
tocItem.Params[paramName] = paramValue;
|
||||
switch(paramName.ToLower())
|
||||
{
|
||||
case "name":
|
||||
{
|
||||
tocItem.Name = paramValue;
|
||||
};break;
|
||||
case "local":
|
||||
{
|
||||
tocItem.Local = paramValue;
|
||||
};break;
|
||||
case "imagenumber":
|
||||
{
|
||||
tocItem.ImageIndex = Int32.Parse(paramValue);
|
||||
|
||||
if( tocItem.ImageIndex == 2)
|
||||
tocItem.ImageIndex = TOCItem.STD_FOLDER_HH1;
|
||||
};break;
|
||||
}
|
||||
|
||||
nParamIndex = mP.Index+mP.Length;
|
||||
}
|
||||
|
||||
tocItem.ChmFile = chmFile.ChmFilePath;
|
||||
return tocItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
550
irc/TechBot/CHMLibrary/CHMDecoding/HHKParser.cs
Normal file
550
irc/TechBot/CHMLibrary/CHMDecoding/HHKParser.cs
Normal file
@@ -0,0 +1,550 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>HHKParser</c> implements a parser for HHK contents files.
|
||||
/// </summary>
|
||||
internal sealed class HHKParser
|
||||
{
|
||||
/// <summary>
|
||||
/// regular expressions for replacing the sitemap boundary tags
|
||||
/// </summary>
|
||||
private static string RE_ULOpening = @"\<ul\>"; // will be replaced by a '(' for nested parsing
|
||||
private static string RE_ULClosing = @"\</ul\>"; // will be replaced by a ')' for nested parsing
|
||||
|
||||
/// <summary>
|
||||
/// Matching ul-tags
|
||||
/// </summary>
|
||||
private static string RE_ULBoundaries = @"\<ul\>(?<innerText>.*)\</ul\>";
|
||||
/// <summary>
|
||||
/// Matching the nested tree structure.
|
||||
/// </summary>
|
||||
private static string RE_NestedBoundaries = @"\( (?> [^()]+ | \( (?<DEPTH>) | \) (?<-DEPTH>) )* (?(DEPTH)(?!)) \)";
|
||||
/// <summary>
|
||||
/// Matching object-tags
|
||||
/// </summary>
|
||||
private static string RE_ObjectBoundaries = @"\<object(?<innerText>.*?)\</object\>";
|
||||
/// <summary>
|
||||
/// Matching param tags
|
||||
/// </summary>
|
||||
private static string RE_ParamBoundaries = @"\<param(?<innerText>.*?)\>";
|
||||
/// <summary>
|
||||
/// Extracting tag attributes
|
||||
/// </summary>
|
||||
private const string RE_QuoteAttributes = @"( |\t)*(?<attributeName>[\-a-zA-Z0-9]*)( |\t)*=( |\t)*(?<attributeTD>[\""\'])?(?<attributeValue>.*?(?(attributeTD)\k<attributeTD>|([\s>]|.$)))";
|
||||
|
||||
/// <summary>
|
||||
/// private regular expressionobjects
|
||||
/// </summary>
|
||||
private static Regex ulRE;
|
||||
private static Regex NestedRE;
|
||||
private static Regex ObjectRE;
|
||||
private static Regex ParamRE;
|
||||
private static Regex AttributesRE;
|
||||
|
||||
/// <summary>
|
||||
/// Parses a HHK file and returns an ArrayList with the index tree
|
||||
/// </summary>
|
||||
/// <param name="hhkFile">string content of the hhk file</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
/// <returns>Returns an ArrayList with the index tree</returns>
|
||||
public static ArrayList ParseHHK(string hhkFile, CHMFile chmFile)
|
||||
{
|
||||
ArrayList indexList = new ArrayList();
|
||||
|
||||
ulRE = new Regex(RE_ULBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
NestedRE = new Regex(RE_NestedBoundaries, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
ObjectRE = new Regex(RE_ObjectBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
ParamRE = new Regex(RE_ParamBoundaries, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
AttributesRE = new Regex(RE_QuoteAttributes, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
int innerTextIdx = ulRE.GroupNumberFromName("innerText");
|
||||
|
||||
if( ulRE.IsMatch(hhkFile, 0) )
|
||||
{
|
||||
Match m = ulRE.Match(hhkFile, 0);
|
||||
|
||||
if( ObjectRE.IsMatch(hhkFile, 0) ) // first object block contains information types and categories
|
||||
{
|
||||
Match mO = ObjectRE.Match(hhkFile, 0);
|
||||
int iOTxt = ObjectRE.GroupNumberFromName("innerText");
|
||||
|
||||
string globalText = mO.Groups[iOTxt].Value;
|
||||
|
||||
ParseGlobalSettings( globalText, chmFile );
|
||||
}
|
||||
|
||||
string innerText = m.Groups["innerText"].Value;
|
||||
|
||||
innerText = innerText.Replace("(", "(");
|
||||
innerText = innerText.Replace(")", ")");
|
||||
innerText = Regex.Replace(innerText, RE_ULOpening, "(", RegexOptions.IgnoreCase);
|
||||
innerText = Regex.Replace(innerText, RE_ULClosing, ")", RegexOptions.IgnoreCase);
|
||||
|
||||
ParseTree( innerText, null, indexList, chmFile );
|
||||
}
|
||||
|
||||
return indexList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively parses a sitemap tree
|
||||
/// </summary>
|
||||
/// <param name="text">content text</param>
|
||||
/// <param name="parent">Parent for all read items</param>
|
||||
/// <param name="arrNodes">arraylist which receives the extracted nodes</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static void ParseTree( string text, IndexItem parent, ArrayList arrNodes, CHMFile chmFile )
|
||||
{
|
||||
string strPreItems="", strPostItems="";
|
||||
string innerText = "";
|
||||
|
||||
int nIndex = 0;
|
||||
|
||||
while( NestedRE.IsMatch(text, nIndex) )
|
||||
{
|
||||
Match m = NestedRE.Match(text, nIndex);
|
||||
|
||||
innerText = m.Value.Substring( 1, m.Length-2);
|
||||
|
||||
strPreItems = text.Substring(nIndex,m.Index-nIndex);
|
||||
|
||||
ParseItems(strPreItems, parent, arrNodes, chmFile);
|
||||
|
||||
if((arrNodes.Count>0) && (innerText.Length > 0) )
|
||||
{
|
||||
IndexItem p = ((IndexItem)(arrNodes[arrNodes.Count-1]));
|
||||
ParseTree( innerText, p, arrNodes, chmFile );
|
||||
}
|
||||
|
||||
nIndex = m.Index+m.Length;
|
||||
}
|
||||
|
||||
if( nIndex == 0)
|
||||
{
|
||||
strPostItems = text.Substring(nIndex, text.Length-nIndex);
|
||||
ParseItems(strPostItems, parent, arrNodes, chmFile);
|
||||
}
|
||||
else if( nIndex < text.Length-1)
|
||||
{
|
||||
strPostItems = text.Substring(nIndex, text.Length-nIndex);
|
||||
ParseTree(strPostItems, parent, arrNodes, chmFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parses nodes from the text
|
||||
/// </summary>
|
||||
/// <param name="itemstext">text containing the items</param>
|
||||
/// <param name="parentItem">parent index item</param>
|
||||
/// <param name="arrNodes">arraylist where the nodes should be added</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static void ParseItems( string itemstext, IndexItem parentItem, ArrayList arrNodes, CHMFile chmFile)
|
||||
{
|
||||
int innerTextIdx = ObjectRE.GroupNumberFromName("innerText");
|
||||
int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
|
||||
|
||||
// get group-name indexes
|
||||
int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
|
||||
int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
|
||||
int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
|
||||
|
||||
int nObjStartIndex = 0;
|
||||
int nLastObjStartIndex = 0;
|
||||
string sKeyword = "";
|
||||
|
||||
while( ObjectRE.IsMatch(itemstext, nObjStartIndex) )
|
||||
{
|
||||
Match m = ObjectRE.Match(itemstext, nObjStartIndex);
|
||||
|
||||
string innerText = m.Groups[innerTextIdx].Value;
|
||||
|
||||
IndexItem idxItem = new IndexItem();
|
||||
|
||||
// read parameters
|
||||
int nParamIndex = 0;
|
||||
int nNameCnt = 0;
|
||||
|
||||
string paramTitle = "";
|
||||
string paramLocal = "";
|
||||
bool bAdded = false;
|
||||
|
||||
while( ParamRE.IsMatch(innerText, nParamIndex) )
|
||||
{
|
||||
Match mP = ParamRE.Match(innerText, nParamIndex);
|
||||
|
||||
string innerP = mP.Groups[innerPTextIdx].Value;
|
||||
|
||||
string paramName = "";
|
||||
string paramValue = "";
|
||||
|
||||
int nAttrIdx = 0;
|
||||
//sKeyword = "";
|
||||
|
||||
while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
|
||||
{
|
||||
Match mA = AttributesRE.Match(innerP, nAttrIdx);
|
||||
|
||||
string attributeName = mA.Groups[nameIndex].Value;
|
||||
string attributeValue = mA.Groups[valueIndex].Value;
|
||||
string attributeTD = mA.Groups[tdIndex].Value;
|
||||
|
||||
if(attributeTD.Length > 0)
|
||||
{
|
||||
// delete the trailing textqualifier
|
||||
if( attributeValue.Length > 0)
|
||||
{
|
||||
int ltqi = attributeValue.LastIndexOf( attributeTD );
|
||||
|
||||
if(ltqi >= 0)
|
||||
{
|
||||
attributeValue = attributeValue.Substring(0,ltqi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "name")
|
||||
{
|
||||
paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
nNameCnt++;
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "value")
|
||||
{
|
||||
paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
// delete trailing /
|
||||
while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))
|
||||
paramValue = paramValue.Substring(0,paramValue.Length-1);
|
||||
}
|
||||
|
||||
nAttrIdx = mA.Index+mA.Length;
|
||||
}
|
||||
|
||||
if( nNameCnt == 1) // first "Name" param = keyword
|
||||
{
|
||||
sKeyword = "";
|
||||
|
||||
if(parentItem != null)
|
||||
sKeyword = parentItem.KeyWordPath + ",";
|
||||
|
||||
string sOldKW = sKeyword;
|
||||
|
||||
sKeyword += paramValue;
|
||||
|
||||
IndexItem idxFind = FindByKeyword(arrNodes, sKeyword);
|
||||
|
||||
if(idxFind != null)
|
||||
{
|
||||
idxItem = idxFind;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( sKeyword.Split(new char[] {','}).Length > 1 )
|
||||
{
|
||||
idxItem.CharIndex = sKeyword.Length - paramValue.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
sKeyword = paramValue;
|
||||
sOldKW = sKeyword;
|
||||
idxItem.CharIndex = 0;
|
||||
}
|
||||
|
||||
idxItem.KeyWordPath = sKeyword;
|
||||
idxItem.Indent = sKeyword.Split(new char[] {','}).Length - 1;
|
||||
idxItem.IsSeeAlso = false;
|
||||
|
||||
sKeyword = sOldKW;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if( (nNameCnt > 2) && (paramName.ToLower()=="name") )
|
||||
{
|
||||
bAdded = true;
|
||||
IndexTopic idxTopic = new IndexTopic(paramTitle, paramLocal, chmFile.CompileFile, chmFile.ChmFilePath);
|
||||
|
||||
idxItem.Topics.Add( idxTopic );
|
||||
|
||||
paramTitle = "";
|
||||
paramLocal = "";
|
||||
}
|
||||
|
||||
switch(paramName.ToLower())
|
||||
{
|
||||
case "name":
|
||||
//case "keyword":
|
||||
{
|
||||
paramTitle = paramValue;
|
||||
};break;
|
||||
case "local":
|
||||
{
|
||||
paramLocal = paramValue.Replace("../", "").Replace("./", "");
|
||||
};break;
|
||||
case "type": // information type assignment for item
|
||||
{
|
||||
idxItem.InfoTypeStrings.Add( paramValue );
|
||||
};break;
|
||||
case "see also":
|
||||
{
|
||||
idxItem.AddSeeAlso(paramValue);
|
||||
idxItem.IsSeeAlso = true;
|
||||
bAdded = true;
|
||||
};break;
|
||||
}
|
||||
}
|
||||
|
||||
nParamIndex = mP.Index+mP.Length;
|
||||
}
|
||||
|
||||
if(!bAdded)
|
||||
{
|
||||
bAdded=false;
|
||||
IndexTopic idxTopic = new IndexTopic(paramTitle, paramLocal, chmFile.CompileFile, chmFile.ChmFilePath);
|
||||
|
||||
idxItem.Topics.Add( idxTopic );
|
||||
|
||||
paramTitle = "";
|
||||
paramLocal = "";
|
||||
}
|
||||
|
||||
idxItem.ChmFile = chmFile;
|
||||
arrNodes.Add( idxItem );
|
||||
|
||||
nLastObjStartIndex = nObjStartIndex;
|
||||
nObjStartIndex = m.Index+m.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches an index-keyword in the index list
|
||||
/// </summary>
|
||||
/// <param name="indexList">index list to search</param>
|
||||
/// <param name="Keyword">keyword to find</param>
|
||||
/// <returns>Returns an <see cref="IndexItem">IndexItem</see> instance if found, otherwise null.</returns>
|
||||
private static IndexItem FindByKeyword(ArrayList indexList, string Keyword)
|
||||
{
|
||||
foreach(IndexItem curItem in indexList)
|
||||
{
|
||||
if( curItem.KeyWordPath == Keyword)
|
||||
return curItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the very first <OBJECT> tag in the sitemap file and extracts
|
||||
/// information types and categories.
|
||||
/// </summary>
|
||||
/// <param name="sText">text of the object tag</param>
|
||||
/// <param name="chmFile">CHMFile instance</param>
|
||||
private static void ParseGlobalSettings(string sText, CHMFile chmFile)
|
||||
{
|
||||
int innerPTextIdx = ParamRE.GroupNumberFromName("innerText");
|
||||
|
||||
// get group-name indexes
|
||||
int nameIndex = AttributesRE.GroupNumberFromName("attributeName");
|
||||
int valueIndex = AttributesRE.GroupNumberFromName("attributeValue");
|
||||
int tdIndex = AttributesRE.GroupNumberFromName("attributeTD");
|
||||
|
||||
// read parameters
|
||||
int nParamIndex = 0;
|
||||
|
||||
// 0... unknown
|
||||
// 1... inclusinve info type name
|
||||
// 2... exclusive info type name
|
||||
// 3... hidden info type name
|
||||
// 4... category name
|
||||
// 5... incl infotype name for category
|
||||
// 6... excl infotype name for category
|
||||
// 7... hidden infotype name for category
|
||||
int prevItem = 0;
|
||||
|
||||
string sName = "";
|
||||
string sDescription = "";
|
||||
string curCategory = "";
|
||||
|
||||
while( ParamRE.IsMatch(sText, nParamIndex) )
|
||||
{
|
||||
Match mP = ParamRE.Match(sText, nParamIndex);
|
||||
|
||||
string innerP = mP.Groups[innerPTextIdx].Value;
|
||||
|
||||
string paramName = "";
|
||||
string paramValue = "";
|
||||
|
||||
int nAttrIdx = 0;
|
||||
|
||||
while( AttributesRE.IsMatch( innerP, nAttrIdx ) )
|
||||
{
|
||||
Match mA = AttributesRE.Match(innerP, nAttrIdx);
|
||||
|
||||
string attributeName = mA.Groups[nameIndex].Value;
|
||||
string attributeValue = mA.Groups[valueIndex].Value;
|
||||
string attributeTD = mA.Groups[tdIndex].Value;
|
||||
|
||||
if(attributeTD.Length > 0)
|
||||
{
|
||||
// delete the trailing textqualifier
|
||||
if( attributeValue.Length > 0)
|
||||
{
|
||||
int ltqi = attributeValue.LastIndexOf( attributeTD );
|
||||
|
||||
if(ltqi >= 0)
|
||||
{
|
||||
attributeValue = attributeValue.Substring(0,ltqi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "name")
|
||||
{
|
||||
paramName = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
}
|
||||
|
||||
if( attributeName.ToLower() == "value")
|
||||
{
|
||||
paramValue = HttpUtility.HtmlDecode(attributeValue); // for unicode encoded values
|
||||
// delete trailing /
|
||||
while((paramValue.Length>0)&&(paramValue[paramValue.Length-1] == '/'))
|
||||
paramValue = paramValue.Substring(0,paramValue.Length-1);
|
||||
|
||||
}
|
||||
|
||||
nAttrIdx = mA.Index+mA.Length;
|
||||
}
|
||||
|
||||
switch(paramName.ToLower())
|
||||
{
|
||||
case "savetype": // inclusive information type name
|
||||
{
|
||||
prevItem = 1;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "savetypedesc": // description of information type
|
||||
{
|
||||
InformationTypeMode mode = InformationTypeMode.Inclusive;
|
||||
sDescription = paramValue;
|
||||
|
||||
if( prevItem == 1)
|
||||
mode = InformationTypeMode.Inclusive;
|
||||
if( prevItem == 2)
|
||||
mode = InformationTypeMode.Exclusive;
|
||||
if( prevItem == 3)
|
||||
mode = InformationTypeMode.Hidden;
|
||||
|
||||
if( chmFile.GetInformationType( sName ) == null)
|
||||
{
|
||||
// check if the HtmlHelpSystem already holds such an information type
|
||||
if( chmFile.SystemInstance.GetInformationType( sName ) == null)
|
||||
{
|
||||
// info type not found yet
|
||||
|
||||
InformationType newType = new InformationType(sName, sDescription, mode);
|
||||
chmFile.InformationTypes.Add(newType);
|
||||
}
|
||||
else
|
||||
{
|
||||
InformationType sysType = chmFile.SystemInstance.GetInformationType( sName );
|
||||
chmFile.InformationTypes.Add( sysType );
|
||||
}
|
||||
}
|
||||
|
||||
prevItem = 0;
|
||||
};break;
|
||||
case "saveexclusive": // exclusive information type name
|
||||
{
|
||||
prevItem = 2;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "savehidden": // hidden information type name
|
||||
{
|
||||
prevItem = 3;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "category": // category name
|
||||
{
|
||||
prevItem = 4;
|
||||
sName = paramValue;
|
||||
curCategory = sName;
|
||||
};break;
|
||||
case "categorydesc": // category description
|
||||
{
|
||||
sDescription = paramValue;
|
||||
|
||||
if( chmFile.GetCategory( sName ) == null)
|
||||
{
|
||||
// check if the HtmlHelpSystem already holds such a category
|
||||
if( chmFile.SystemInstance.GetCategory( sName ) == null)
|
||||
{
|
||||
// add category
|
||||
Category newCat = new Category(sName, sDescription);
|
||||
chmFile.Categories.Add(newCat);
|
||||
}
|
||||
else
|
||||
{
|
||||
Category sysCat = chmFile.SystemInstance.GetCategory( sName );
|
||||
chmFile.Categories.Add( sysCat );
|
||||
}
|
||||
}
|
||||
|
||||
prevItem = 0;
|
||||
};break;
|
||||
case "type": // inclusive information type which is member of the previously read category
|
||||
{
|
||||
prevItem = 5;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "typedesc": // description of type for category
|
||||
{
|
||||
sDescription = paramValue;
|
||||
Category cat = chmFile.GetCategory( curCategory );
|
||||
|
||||
if( cat != null)
|
||||
{
|
||||
// category found
|
||||
InformationType infoType = chmFile.GetInformationType( sName );
|
||||
|
||||
if( infoType != null)
|
||||
{
|
||||
if( !cat.ContainsInformationType(infoType))
|
||||
{
|
||||
infoType.SetCategoryFlag(true);
|
||||
cat.AddInformationType(infoType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevItem = 0;
|
||||
};break;
|
||||
case "typeexclusive": // exclusive information type which is member of the previously read category
|
||||
{
|
||||
prevItem = 6;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
case "typehidden": // hidden information type which is member of the previously read category
|
||||
{
|
||||
prevItem = 7;
|
||||
sName = paramValue;
|
||||
};break;
|
||||
default:
|
||||
{
|
||||
prevItem = 0;
|
||||
sName = "";
|
||||
sDescription = "";
|
||||
};break;
|
||||
}
|
||||
|
||||
nParamIndex = mP.Index+mP.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
245
irc/TechBot/CHMLibrary/CHMDecoding/TopicEntry.cs
Normal file
245
irc/TechBot/CHMLibrary/CHMDecoding/TopicEntry.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>TopicEntry</c> stores the data for one topic entry
|
||||
/// </summary>
|
||||
internal sealed class TopicEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal member storing the offset of this topic entry
|
||||
/// </summary>
|
||||
private int _entryOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the index of the binary toc
|
||||
/// </summary>
|
||||
private int _tocidxOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the string offset of the title
|
||||
/// </summary>
|
||||
private int _titleOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storuing the urltable offset
|
||||
/// </summary>
|
||||
private int _urltableOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the visibility mode
|
||||
/// </summary>
|
||||
private int _visibilityMode = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing an unknown mode
|
||||
/// </summary>
|
||||
private int _unknownMode = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="entryOffset">offset of this entry</param>
|
||||
/// <param name="tocidxOffset">offset in the binary toc index</param>
|
||||
/// <param name="titleOffset">offset of the title (in the #STRINGS file)</param>
|
||||
/// <param name="urltableOffset">offset in the urltable containing the urlstr offset for the url</param>
|
||||
/// <param name="visibilityMode">visibility mode 2 indicates not in contents, 6 indicates that it is in the contents, 0/4 something else (unknown)</param>
|
||||
/// <param name="unknownMode">0, 2, 4, 8, 10, 12, 16, 32 (unknown)</param>
|
||||
public TopicEntry(int entryOffset, int tocidxOffset, int titleOffset, int urltableOffset, int visibilityMode, int unknownMode) :this(entryOffset, tocidxOffset, titleOffset, urltableOffset, visibilityMode, unknownMode, null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="entryOffset">offset of this entry</param>
|
||||
/// <param name="tocidxOffset">offset in the binary toc index</param>
|
||||
/// <param name="titleOffset">offset of the title (in the #STRINGS file)</param>
|
||||
/// <param name="urltableOffset">offset in the urltable containing the urlstr offset for the url</param>
|
||||
/// <param name="visibilityMode">visibility mode 2 indicates not in contents, 6 indicates that it is in the contents, 0/4 something else (unknown)</param>
|
||||
/// <param name="unknownMode">0, 2, 4, 8, 10, 12, 16, 32 (unknown)</param>
|
||||
/// <param name="associatedFile">associated chmfile object</param>
|
||||
internal TopicEntry(int entryOffset, int tocidxOffset, int titleOffset, int urltableOffset, int visibilityMode, int unknownMode, CHMFile associatedFile)
|
||||
{
|
||||
_entryOffset = entryOffset;
|
||||
_tocidxOffset = tocidxOffset;
|
||||
_titleOffset = titleOffset;
|
||||
_urltableOffset = urltableOffset;
|
||||
_visibilityMode = visibilityMode;
|
||||
_unknownMode = unknownMode;
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
internal TopicEntry()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _entryOffset );
|
||||
writer.Write( _tocidxOffset );
|
||||
writer.Write( _titleOffset );
|
||||
writer.Write( _urltableOffset );
|
||||
writer.Write( _visibilityMode );
|
||||
writer.Write( _unknownMode );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
_entryOffset = reader.ReadInt32();
|
||||
_tocidxOffset = reader.ReadInt32();
|
||||
_titleOffset = reader.ReadInt32();
|
||||
_urltableOffset = reader.ReadInt32();
|
||||
_visibilityMode = reader.ReadInt32();
|
||||
_unknownMode = reader.ReadInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">instance to set</param>
|
||||
internal void SetCHMFile(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated chm file
|
||||
/// </summary>
|
||||
internal CHMFile ChmFile
|
||||
{
|
||||
get { return _associatedFile; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of this entry
|
||||
/// </summary>
|
||||
internal int EntryOffset
|
||||
{
|
||||
get { return _entryOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tocidx offset
|
||||
/// </summary>
|
||||
internal int TOCIdxOffset
|
||||
{
|
||||
get { return _tocidxOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title offset of the #STRINGS file
|
||||
/// </summary>
|
||||
internal int TitleOffset
|
||||
{
|
||||
get { return _titleOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urltable offset
|
||||
/// </summary>
|
||||
internal int UrlTableOffset
|
||||
{
|
||||
get { return _urltableOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the topic entry
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _associatedFile == null)
|
||||
return String.Empty;
|
||||
|
||||
if( _associatedFile.StringsFile == null)
|
||||
return String.Empty;
|
||||
|
||||
string sTemp = (string)_associatedFile.StringsFile[ _titleOffset ];
|
||||
|
||||
if(sTemp == null)
|
||||
return String.Empty;
|
||||
|
||||
return sTemp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url of the topic
|
||||
/// </summary>
|
||||
public string Locale
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _associatedFile == null)
|
||||
return String.Empty;
|
||||
|
||||
if( _associatedFile.UrltblFile == null)
|
||||
return String.Empty;
|
||||
|
||||
UrlTableEntry utEntry = (UrlTableEntry)_associatedFile.UrltblFile[ _urltableOffset ];
|
||||
|
||||
if(utEntry == null)
|
||||
return String.Empty;
|
||||
|
||||
if(utEntry.URL == "")
|
||||
return String.Empty;
|
||||
|
||||
return utEntry.URL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL of this topic
|
||||
/// </summary>
|
||||
public string URL
|
||||
{
|
||||
get
|
||||
{
|
||||
if(Locale.Length <= 0)
|
||||
return "about:blank";
|
||||
|
||||
if( (Locale.ToLower().IndexOf("http://") >= 0) ||
|
||||
(Locale.ToLower().IndexOf("https://") >= 0) ||
|
||||
(Locale.ToLower().IndexOf("mailto:") >= 0) ||
|
||||
(Locale.ToLower().IndexOf("ftp://") >= 0) ||
|
||||
(Locale.ToLower().IndexOf("ms-its:") >= 0))
|
||||
return Locale;
|
||||
|
||||
return HtmlHelpSystem.UrlPrefix + _associatedFile.ChmFilePath + "::/" + Locale;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the visibility mode
|
||||
/// </summary>
|
||||
public int VisibilityMode
|
||||
{
|
||||
get { return _visibilityMode; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unknown mode
|
||||
/// </summary>
|
||||
public int UknownMode
|
||||
{
|
||||
get { return _unknownMode; }
|
||||
}
|
||||
}
|
||||
}
|
||||
175
irc/TechBot/CHMLibrary/CHMDecoding/UrlTableEntry.cs
Normal file
175
irc/TechBot/CHMLibrary/CHMDecoding/UrlTableEntry.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>UrlTableEntry</c> stores data for an URL-Table entry
|
||||
/// </summary>
|
||||
internal sealed class UrlTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal member storing the offset of this entry
|
||||
/// </summary>
|
||||
private int _entryOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing a unique id
|
||||
/// </summary>
|
||||
private uint _uniqueID = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the topics index
|
||||
/// </summary>
|
||||
private int _topicsIndex = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset in the urlstr table
|
||||
/// </summary>
|
||||
private int _urlStrOffset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="uniqueID">unique id</param>
|
||||
/// <param name="entryOffset">offset of the entry</param>
|
||||
/// <param name="topicIndex">topic index</param>
|
||||
/// <param name="urlstrOffset">urlstr offset for filename</param>
|
||||
public UrlTableEntry(uint uniqueID, int entryOffset, int topicIndex, int urlstrOffset) : this(uniqueID, entryOffset, topicIndex, urlstrOffset, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="uniqueID">unique id</param>
|
||||
/// <param name="entryOffset">offset of the entry</param>
|
||||
/// <param name="topicIndex">topic index</param>
|
||||
/// <param name="urlstrOffset">urlstr offset for filename</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
internal UrlTableEntry(uint uniqueID, int entryOffset, int topicIndex, int urlstrOffset, CHMFile associatedFile)
|
||||
{
|
||||
_uniqueID = uniqueID;
|
||||
_entryOffset = entryOffset;
|
||||
_topicsIndex = topicIndex;
|
||||
_urlStrOffset = urlstrOffset;
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
internal UrlTableEntry()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _urlStrOffset );
|
||||
writer.Write( _entryOffset );
|
||||
writer.Write( _topicsIndex );
|
||||
writer.Write( _urlStrOffset );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
_urlStrOffset = reader.ReadInt32();
|
||||
_entryOffset = reader.ReadInt32();
|
||||
_topicsIndex = reader.ReadInt32();
|
||||
_urlStrOffset = reader.ReadInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">instance to set</param>
|
||||
internal void SetCHMFile(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id of the entry
|
||||
/// </summary>
|
||||
internal uint UniqueID
|
||||
{
|
||||
get {return _uniqueID; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of the entry
|
||||
/// </summary>
|
||||
internal int EntryOffset
|
||||
{
|
||||
get {return _entryOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the topics index
|
||||
/// </summary>
|
||||
internal int TopicIndex
|
||||
{
|
||||
get {return _topicsIndex; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urlstr offset
|
||||
/// </summary>
|
||||
internal int UrlstrOffset
|
||||
{
|
||||
get { return _urlStrOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url of the entry
|
||||
/// </summary>
|
||||
public string URL
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile == null)
|
||||
return String.Empty;
|
||||
|
||||
if(_associatedFile.UrlstrFile == null)
|
||||
return String.Empty;
|
||||
|
||||
string sTemp = (string)_associatedFile.UrlstrFile.GetURLatOffset( _urlStrOffset );
|
||||
|
||||
if( sTemp == null)
|
||||
return String.Empty;
|
||||
|
||||
return sTemp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated topic for this url entry
|
||||
/// </summary>
|
||||
internal TopicEntry Topic
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile == null)
|
||||
return null;
|
||||
|
||||
if(_associatedFile.TopicsFile == null)
|
||||
return null;
|
||||
|
||||
TopicEntry tentry = _associatedFile.TopicsFile[ _topicsIndex*16 ];
|
||||
|
||||
return tentry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
irc/TechBot/CHMLibrary/CHMDecoding/enumerations.cs
Normal file
19
irc/TechBot/CHMLibrary/CHMDecoding/enumerations.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration for specifying the extraction mode of an toc or index item.
|
||||
/// </summary>
|
||||
public enum DataMode
|
||||
{
|
||||
/// <summary>
|
||||
/// TextBased - this item comes from a text-based sitemap file
|
||||
/// </summary>
|
||||
TextBased = 0,
|
||||
/// <summary>
|
||||
/// Binary - this item was extracted out of a binary stream
|
||||
/// </summary>
|
||||
Binary = 1
|
||||
}
|
||||
}
|
||||
16
irc/TechBot/CHMLibrary/CHMLibrary.cmbx
Normal file
16
irc/TechBot/CHMLibrary/CHMLibrary.cmbx
Normal file
@@ -0,0 +1,16 @@
|
||||
<Combine fileversion="1.0" name="CHMLibrary" description="">
|
||||
<StartMode startupentry="CHMLibrary" single="True">
|
||||
<Execute entry="CHMLibrary" type="None" />
|
||||
</StartMode>
|
||||
<Entries>
|
||||
<Entry filename=".\.\CHMLibrary.prjx" />
|
||||
</Entries>
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release">
|
||||
<Entry name="CHMLibrary" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug">
|
||||
<Entry name="CHMLibrary" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Combine>
|
||||
58
irc/TechBot/CHMLibrary/CHMLibrary.prjx
Normal file
58
irc/TechBot/CHMLibrary/CHMLibrary.prjx
Normal file
@@ -0,0 +1,58 @@
|
||||
<Project name="CHMLibrary" standardNamespace="CHMLibrary" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
|
||||
<Contents>
|
||||
<File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Category.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\ChmFileInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
<File name=".\HtmlHelpSystem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\HttpUtility.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Index.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IndexItem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IndexTopic.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\InformationType.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\TableOfContents.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\TOCItem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Storage" subtype="Directory" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Storage\CHMStream.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding" subtype="Directory" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\BinaryReaderHelp.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMBtree.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMFile.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMIdxhdr.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMStrings.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMSystem.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMTocidx.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMTopics.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMUrlstr.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\CHMUrltable.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\DumpingInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\enumerations.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\FullTextEngine.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\HHCParser2.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\HHCParser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\HHKParser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\TopicEntry.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\CHMDecoding\UrlTableEntry.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
</Contents>
|
||||
<References>
|
||||
<Reference type="Project" refto="Compression" localcopy="True" />
|
||||
</References>
|
||||
<DeploymentInformation target="" script="" strategy="File" />
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="CHMLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configurations active="Debug">
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="CHMLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configuration runwithwarnings="True" name="Release">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Release" assembly="CHMLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Project>
|
||||
201
irc/TechBot/CHMLibrary/Category.cs
Normal file
201
irc/TechBot/CHMLibrary/Category.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>Category</c> implements methods/properties for handling an information category
|
||||
/// </summary>
|
||||
/// <remarks>Note: Information types and categories allow users to filter help contents.
|
||||
/// They are only supported if using sitemap TOC and/or sitemap Index.</remarks>
|
||||
public class Category
|
||||
{
|
||||
private string _name = "";
|
||||
private string _description = "";
|
||||
private ArrayList _infoTypes = null;
|
||||
private int _referenceCount = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
public Category() : this("","")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
/// <param name="name">name of the category</param>
|
||||
/// <param name="description">description</param>
|
||||
public Category(string name, string description) : this(name, description, new ArrayList())
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
/// <param name="name">name of the category</param>
|
||||
/// <param name="description">description</param>
|
||||
/// <param name="linkedInformationTypes">Arraylist of InformationType instances which applies to this category</param>
|
||||
public Category(string name, string description, ArrayList linkedInformationTypes)
|
||||
{
|
||||
_name = name;
|
||||
_description = description;
|
||||
_infoTypes = linkedInformationTypes;
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( _name );
|
||||
writer.Write( _description );
|
||||
|
||||
writer.Write( _infoTypes.Count );
|
||||
|
||||
for(int i=0; i<_infoTypes.Count;i++)
|
||||
{
|
||||
InformationType curType = _infoTypes[i] as InformationType;
|
||||
writer.Write( curType.Name );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
/// <param name="chmFile">current CHMFile instance which reads from dump</param>
|
||||
internal void ReadDump(ref BinaryReader reader, CHMFile chmFile)
|
||||
{
|
||||
_name = reader.ReadString();
|
||||
_description = reader.ReadString();
|
||||
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(int i=0; i<nCnt; i++)
|
||||
{
|
||||
string sITName = reader.ReadString();
|
||||
|
||||
InformationType linkedType = chmFile.GetInformationType( sITName );
|
||||
|
||||
if(linkedType != null)
|
||||
{
|
||||
linkedType.SetCategoryFlag(true);
|
||||
_infoTypes.Add(linkedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Merges the lineked information types from cat into this instance
|
||||
/// </summary>
|
||||
/// <param name="cat">category instance</param>
|
||||
internal void MergeInfoTypes(Category cat)
|
||||
{
|
||||
if(cat!=null)
|
||||
{
|
||||
if(cat.InformationTypes.Count > 0)
|
||||
{
|
||||
for(int i=0;i<cat.InformationTypes.Count;i++)
|
||||
{
|
||||
InformationType curType = cat.InformationTypes[i] as InformationType;
|
||||
|
||||
if(!ContainsInformationType(curType.Name))
|
||||
{
|
||||
curType.SetCategoryFlag(true);
|
||||
_infoTypes.Add(curType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the reference count of this information type instance
|
||||
/// </summary>
|
||||
internal int ReferenceCount
|
||||
{
|
||||
get { return _referenceCount; }
|
||||
set { _referenceCount = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the name of the information type
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set { _name = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the description of the information type
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return _description; }
|
||||
set { _name = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ArrayList with the linked Information types
|
||||
/// </summary>
|
||||
public ArrayList InformationTypes
|
||||
{
|
||||
get { return _infoTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new information type to the category
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
public void AddInformationType(InformationType type)
|
||||
{
|
||||
_infoTypes.Add(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an information type from the category
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
public void RemoveInformationType(InformationType type)
|
||||
{
|
||||
_infoTypes.Remove(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the category contains an information type
|
||||
/// </summary>
|
||||
/// <param name="type">information type instance to check</param>
|
||||
/// <returns>Return true if the information type is part of this category</returns>
|
||||
public bool ContainsInformationType(InformationType type)
|
||||
{
|
||||
return _infoTypes.Contains(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the category contains an information type
|
||||
/// </summary>
|
||||
/// <param name="name">name of the information type</param>
|
||||
/// <returns>Return true if the information type is part of this category</returns>
|
||||
public bool ContainsInformationType(string name)
|
||||
{
|
||||
for(int i=0;i<_infoTypes.Count;i++)
|
||||
{
|
||||
InformationType curType = _infoTypes[i] as InformationType;
|
||||
|
||||
if(curType.Name == name)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
478
irc/TechBot/CHMLibrary/ChmFileInfo.cs
Normal file
478
irc/TechBot/CHMLibrary/ChmFileInfo.cs
Normal file
@@ -0,0 +1,478 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
using System.ComponentModel;
|
||||
|
||||
using HtmlHelp.ChmDecoding;
|
||||
// using HtmlHelp.Storage;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>ChmFileInfo</c> only extracts system information from a CHM file.
|
||||
/// It doesn't build the index and table of contents.
|
||||
/// </summary>
|
||||
public class ChmFileInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal member storing the full filename
|
||||
/// </summary>
|
||||
private string _chmFileName = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for extrating the file information of the provided file.
|
||||
/// The constructor opens the chm-file and reads its system data.
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file name which information should be extracted</param>
|
||||
public ChmFileInfo(string chmFile)
|
||||
{
|
||||
if(!File.Exists(chmFile))
|
||||
throw new ArgumentException("Chm file must exist on disk !", "chmFileName");
|
||||
|
||||
if( ! chmFile.ToLower().EndsWith(".chm") )
|
||||
throw new ArgumentException("HtmlHelp file must have the extension .chm !", "chmFile");
|
||||
|
||||
_chmFileName = chmFile;
|
||||
_associatedFile = new CHMFile(null, chmFile, true); // only load system data of chm
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal constructor used in the class <see cref="HtmlHelp.ChmDecoding.CHMFile">CHMFile</see>.
|
||||
/// </summary>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
internal ChmFileInfo(CHMFile associatedFile)
|
||||
{
|
||||
_associatedFile = associatedFile;
|
||||
|
||||
if( _associatedFile == null)
|
||||
throw new ArgumentException("Associated CHMFile instance must not be null !", "associatedFile");
|
||||
}
|
||||
|
||||
#region default info properties
|
||||
/// <summary>
|
||||
/// Gets the full filename of the chm file
|
||||
/// </summary>
|
||||
public string ChmFileName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _associatedFile.ChmFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="System.IO.FileInfo">FileInfo</see> instance for the chm file.
|
||||
/// </summary>
|
||||
public FileInfo FileInfo
|
||||
{
|
||||
get { return new FileInfo(_associatedFile.ChmFilePath); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region #SYSTEM properties
|
||||
/// <summary>
|
||||
/// Gets the file version of the chm file.
|
||||
/// 2 for Compatibility=1.0, 3 for Compatibility=1.1
|
||||
/// </summary>
|
||||
public int FileVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.FileVersion;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents file name
|
||||
/// </summary>
|
||||
|
||||
public string ContentsFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.ContentsFile;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index file name
|
||||
/// </summary>
|
||||
|
||||
public string IndexFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.IndexFile;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default help topic
|
||||
/// </summary>
|
||||
|
||||
public string DefaultTopic
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.DefaultTopic;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the help window
|
||||
/// </summary>
|
||||
|
||||
public string HelpWindowTitle
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.HelpWindowTitle;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if DBCS is in use
|
||||
/// </summary>
|
||||
|
||||
public bool DBCS
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.DBCS;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if full-text-search is available
|
||||
/// </summary>
|
||||
|
||||
public bool FullTextSearch
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.FullTextSearch;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the file has ALinks
|
||||
/// </summary>
|
||||
|
||||
public bool HasALinks
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.HasALinks;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the file has KLinks
|
||||
/// </summary>
|
||||
|
||||
public bool HasKLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.HasKLinks;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default window name
|
||||
/// </summary>
|
||||
|
||||
public string DefaultWindow
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.DefaultWindow;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name of the compile file
|
||||
/// </summary>
|
||||
|
||||
public string CompileFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.CompileFile;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the chm has a binary index file
|
||||
/// </summary>
|
||||
|
||||
public bool BinaryIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.BinaryIndex;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the chm has a binary index file
|
||||
/// </summary>
|
||||
|
||||
public string CompilerVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.CompilerVersion;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag if the chm has a binary toc file
|
||||
/// </summary>
|
||||
|
||||
public bool BinaryTOC
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.BinaryTOC;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font face of the read font property.
|
||||
/// Empty string for default font.
|
||||
/// </summary>
|
||||
|
||||
public string FontFace
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.FontFace;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font size of the read font property.
|
||||
/// 0 for default font size
|
||||
/// </summary>
|
||||
|
||||
public double FontSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.FontSize;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the character set of the read font property
|
||||
/// 1 for default
|
||||
/// </summary>
|
||||
|
||||
public int CharacterSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.CharacterSet;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the codepage depending on the read font property
|
||||
/// </summary>
|
||||
|
||||
public int CodePage
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.CodePage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assiciated culture info
|
||||
/// </summary>
|
||||
public CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.Culture;
|
||||
|
||||
return CultureInfo.CurrentCulture;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region #IDXHDR properties
|
||||
/// <summary>
|
||||
/// Gets the number of topic nodes including the contents and index files
|
||||
/// </summary>
|
||||
|
||||
public int NumberOfTopicNodes
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.NumberOfTopicNodes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ImageList string specyfied in the #IDXHDR file.
|
||||
/// </summary>
|
||||
/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
|
||||
|
||||
public string ImageList
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.ImageList;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the background setting
|
||||
/// </summary>
|
||||
|
||||
public int Background
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.Background;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the foreground setting
|
||||
/// </summary>
|
||||
|
||||
public int Foreground
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.Foreground;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FrameName string specyfied in the #IDXHDR file.
|
||||
/// </summary>
|
||||
/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
|
||||
|
||||
public string FrameName
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.FrameName;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WindowName string specyfied in the #IDXHDR file.
|
||||
/// </summary>
|
||||
/// <remarks>This property uses the #STRINGS file to extract the string at a given offset.</remarks>
|
||||
|
||||
public string WindowName
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.WindowName;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string array containing the merged file names
|
||||
/// </summary>
|
||||
public string[] MergedFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.MergedFiles;
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
23
irc/TechBot/CHMLibrary/Default.build
Normal file
23
irc/TechBot/CHMLibrary/Default.build
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="CHMLibrary" default="build">
|
||||
|
||||
<property name="output.dir" value="..\bin" />
|
||||
|
||||
<target name="build" description="Build component">
|
||||
<mkdir dir="${output.dir}" />
|
||||
<csc target="library"
|
||||
output="${output.dir}\CHMLibrary.dll"
|
||||
optimize="true"
|
||||
debug="true"
|
||||
doc="${output.dir}\CHMLibrary.xml"
|
||||
warninglevel="0">
|
||||
<sources>
|
||||
<include name="**/*.cs" />
|
||||
</sources>
|
||||
<references>
|
||||
<include name="${output.dir}\Compression.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
894
irc/TechBot/CHMLibrary/HtmlHelpSystem.cs
Normal file
894
irc/TechBot/CHMLibrary/HtmlHelpSystem.cs
Normal file
@@ -0,0 +1,894 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Data;
|
||||
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>HtmlHelpSystem</c> implements the main object for reading chm files
|
||||
/// </summary>
|
||||
public sealed class HtmlHelpSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Private shared instance of current HtmlHelpSystem class
|
||||
/// </summary>
|
||||
private static HtmlHelpSystem _current=null;
|
||||
/// <summary>
|
||||
/// Internal member storing the attached files
|
||||
/// </summary>
|
||||
private ArrayList _chmFiles = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing a merged table of contents
|
||||
/// </summary>
|
||||
private TableOfContents _toc = new TableOfContents();
|
||||
/// <summary>
|
||||
/// Internal member storing a merged index
|
||||
/// </summary>
|
||||
private Index _index = new Index();
|
||||
/// <summary>
|
||||
/// URL prefix for specifying a chm destination
|
||||
/// </summary>
|
||||
private static string _urlPrefix = "ms-its:";
|
||||
/// <summary>
|
||||
/// Internal flag specifying if the system should use the tree-images list
|
||||
/// from HtmlHelp2. If false the standard CHM-Viewer pics will be used.
|
||||
/// </summary>
|
||||
private static bool _useHH2TreePics = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the read information types
|
||||
/// </summary>
|
||||
private ArrayList _informationTypes = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing the read categories
|
||||
/// </summary>
|
||||
private ArrayList _categories = new ArrayList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the url prefix for specifying a chm destination
|
||||
/// </summary>
|
||||
public static string UrlPrefix
|
||||
{
|
||||
get { return _urlPrefix; }
|
||||
set { _urlPrefix = value; }
|
||||
}
|
||||
|
||||
public CHMStream.CHMStream BaseStream
|
||||
{
|
||||
get
|
||||
{
|
||||
CHMFile chm=(CHMFile)_chmFiles[0];
|
||||
return chm.BaseStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the flag specifying if the system should use the tree-images list
|
||||
/// from HtmlHelp2. If false the standard CHM-Viewer pics will be used.
|
||||
/// </summary>
|
||||
public static bool UseHH2TreePics
|
||||
{
|
||||
get { return _useHH2TreePics; }
|
||||
set { _useHH2TreePics = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current HtmlHelpSystem instance
|
||||
/// </summary>
|
||||
public static HtmlHelpSystem Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
public HtmlHelpSystem() : this("")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the reader class
|
||||
/// </summary>
|
||||
/// <param name="chmFile">chm file to attach with the reader</param>
|
||||
public HtmlHelpSystem(string chmFile)
|
||||
{
|
||||
_current = this;
|
||||
OpenFile(chmFile);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Opens a chm file and creates
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file path of the chm file to open</param>
|
||||
/// <remarks>If you call this method, all existing merged files will be cleared.</remarks>
|
||||
public void OpenFile(string chmFile)
|
||||
{
|
||||
OpenFile(chmFile, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a chm file and creates
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file path of the chm file to open</param>
|
||||
/// <param name="dmpInfo">dumping info</param>
|
||||
/// <remarks>If you call this method, all existing merged files will be cleared.</remarks>
|
||||
public void OpenFile(string chmFile, DumpingInfo dmpInfo)
|
||||
{
|
||||
if( File.Exists(chmFile ) )
|
||||
{
|
||||
_chmFiles.Clear();
|
||||
_toc.Clear();
|
||||
_index.Clear();
|
||||
_informationTypes.Clear();
|
||||
_categories.Clear();
|
||||
|
||||
CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);
|
||||
|
||||
_toc = new TableOfContents( newFile.TOC );
|
||||
_index = new Index( newFile.IndexKLinks, newFile.IndexALinks );
|
||||
|
||||
_chmFiles.Add(newFile);
|
||||
// add all infotypes and categories of the read file to this system instance
|
||||
MergeFileInfoTypesCategories(newFile);
|
||||
|
||||
// check if the file has a merged files list
|
||||
if( newFile.MergedFiles.Length > 0 )
|
||||
{
|
||||
// extract the path of the chm file (usually merged files are in the same path)
|
||||
FileInfo fi = new FileInfo(chmFile);
|
||||
string sPath = fi.DirectoryName;
|
||||
|
||||
for(int i=0; i<newFile.MergedFiles.Length; i++)
|
||||
{
|
||||
string sFile = newFile.MergedFiles[i];
|
||||
|
||||
if(sFile.Length > 0)
|
||||
{
|
||||
if(sFile[1] != ':') // no full path setting
|
||||
{
|
||||
sFile = Path.Combine(sPath, sFile);
|
||||
}
|
||||
|
||||
MergeFile(sFile, dmpInfo, true);
|
||||
}
|
||||
}
|
||||
|
||||
// if (newFile.MergLinks.Count>0)
|
||||
// RecalculateMergeLinks(newFile);
|
||||
|
||||
RemoveMergeLinks(); // clear all merge-links which have no target !
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges a chm file to the current help contents
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file path of the chm file to merge</param>
|
||||
public void MergeFile(string chmFile)
|
||||
{
|
||||
MergeFile(chmFile, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges a chm file to the current help contents
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file path of the chm file to merge</param>
|
||||
/// <param name="dmpInfo">dumping info</param>
|
||||
public void MergeFile(string chmFile, DumpingInfo dmpInfo)
|
||||
{
|
||||
MergeFile(chmFile, dmpInfo, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges a chm file to the current help contents
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file path of the chm file to merge</param>
|
||||
/// <param name="dmpInfo">dumping info</param>
|
||||
/// <param name="mergedFileList">true if the merge is done because a merged file list
|
||||
/// was found in the previously loaded CHM.</param>
|
||||
internal void MergeFile(string chmFile, DumpingInfo dmpInfo, bool mergedFileList)
|
||||
{
|
||||
if( File.Exists(chmFile ) )
|
||||
{
|
||||
if( _chmFiles.Count == 1)
|
||||
{
|
||||
// if we open the first file, we directly point into the toc and index of this file.
|
||||
// So that we don't merge the new toc's indexe's into the first file, we have to
|
||||
// clone the internal arraylists first to a new instance of the toc/index holder classes.
|
||||
ArrayList atoc = _toc.TOC;
|
||||
ArrayList alinks = _index.ALinks;
|
||||
ArrayList klinks = _index.KLinks;
|
||||
|
||||
_toc = new TableOfContents();
|
||||
_index = new Index();
|
||||
|
||||
_toc.MergeToC( atoc );
|
||||
_index.MergeIndex( alinks, IndexType.AssiciativeLinks );
|
||||
_index.MergeIndex( klinks, IndexType.KeywordLinks );
|
||||
}
|
||||
|
||||
CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);
|
||||
|
||||
if(mergedFileList) // if we've called this method due to a merged file list merge
|
||||
{
|
||||
RecalculateMergeLinks(newFile);
|
||||
|
||||
_toc.MergeToC( newFile.TOC, _chmFiles );
|
||||
_index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );
|
||||
_index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );
|
||||
|
||||
_chmFiles.Add(newFile);
|
||||
|
||||
// add all infotypes and categories of the read file to this system instance
|
||||
MergeFileInfoTypesCategories(newFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
_toc.MergeToC( newFile.TOC, _chmFiles );
|
||||
_index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );
|
||||
_index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );
|
||||
|
||||
_chmFiles.Add(newFile);
|
||||
|
||||
// add all infotypes and categories of the read file to this system instance
|
||||
MergeFileInfoTypesCategories(newFile);
|
||||
|
||||
// check if the file has a merged files list
|
||||
if( newFile.MergedFiles.Length > 0 )
|
||||
{
|
||||
// extract the path of the chm file (usually merged files are in the same path)
|
||||
FileInfo fi = new FileInfo(chmFile);
|
||||
string sPath = fi.DirectoryName;
|
||||
|
||||
for(int i=0; i<newFile.MergedFiles.Length; i++)
|
||||
{
|
||||
string sFile = newFile.MergedFiles[i];
|
||||
|
||||
if(sFile.Length > 0)
|
||||
{
|
||||
if(sFile[1] != ':') // no full path setting
|
||||
{
|
||||
sFile = Path.Combine(sPath, sFile);
|
||||
}
|
||||
|
||||
MergeFile(sFile, dmpInfo, true);
|
||||
}
|
||||
}
|
||||
|
||||
RemoveMergeLinks(); // clear all merge-links which have no target !
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks all Merg-links read till now. Checks if the merg-link points to the
|
||||
/// file <c>currentFile</c>. If yes the link will be replaced by the contents of the
|
||||
/// merged file.
|
||||
/// </summary>
|
||||
/// <param name="currentFile">Current CHMFile instance</param>
|
||||
internal void RecalculateMergeLinks(CHMFile currentFile)
|
||||
{
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
if( curFile.MergLinks.Count > 0)
|
||||
{
|
||||
for(int i=0; i<curFile.MergLinks.Count; i++)
|
||||
{
|
||||
TOCItem curItem = curFile.MergLinks[i] as TOCItem;
|
||||
|
||||
string sMerge = curItem.MergeLink;
|
||||
string [] sSplit = sMerge.Split( new char[]{':'} );
|
||||
|
||||
string sFName = "";
|
||||
string sTarget = "";
|
||||
|
||||
if( sSplit.Length > 3) // merge info contains path name
|
||||
{
|
||||
sFName = sSplit[0] + ":" + sSplit[1];
|
||||
sTarget = sSplit[3];
|
||||
}
|
||||
else if( sSplit.Length == 3)// merge info contains only file name
|
||||
{
|
||||
FileInfo fi = new FileInfo(currentFile.ChmFilePath);
|
||||
string sPath = fi.DirectoryName;
|
||||
|
||||
string sFile = sSplit[0];
|
||||
|
||||
if(sFile.Length > 0)
|
||||
{
|
||||
if(sFile[1] != ':') // no full path setting
|
||||
{
|
||||
sFile = Path.Combine(sPath, sFile);
|
||||
}
|
||||
}
|
||||
|
||||
sFName = sFile;
|
||||
sTarget = sSplit[2];
|
||||
}
|
||||
|
||||
ArrayList arrToc = null;
|
||||
if( (sFName.Length>0) && (sTarget.Length>0) )
|
||||
{
|
||||
// if this link points into the current file
|
||||
if( sFName.ToLower() == currentFile.ChmFilePath.ToLower() )
|
||||
{
|
||||
if(sTarget.ToLower().IndexOf(".hhc") >= 0)
|
||||
{
|
||||
string sfCheck = sTarget;
|
||||
|
||||
// remove prefixing ./
|
||||
while( (sfCheck[0]=='.') || (sfCheck[0]=='/') )
|
||||
{
|
||||
sfCheck = sfCheck.Substring(1);
|
||||
}
|
||||
|
||||
if( currentFile.ContentsFile.ToLower() != sfCheck )
|
||||
{
|
||||
arrToc = currentFile.ParseHHC( sTarget );
|
||||
|
||||
if( arrToc.Count > 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arrToc = currentFile.TOC;
|
||||
}
|
||||
|
||||
// target points to a complete TOC
|
||||
int nCnt = 0;
|
||||
|
||||
foreach(TOCItem chkItem in arrToc)
|
||||
{
|
||||
if(nCnt == 0)
|
||||
{
|
||||
curItem.AssociatedFile = currentFile;
|
||||
curItem.Children = chkItem.Children;
|
||||
curItem.ChmFile = currentFile.ChmFilePath;
|
||||
curItem.ImageIndex = chkItem.ImageIndex;
|
||||
curItem.Local = chkItem.Local;
|
||||
curItem.MergeLink = chkItem.MergeLink;
|
||||
curItem.Name = chkItem.Name;
|
||||
curItem.TocMode = chkItem.TocMode;
|
||||
curItem.TopicOffset = chkItem.TopicOffset;
|
||||
|
||||
MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayList checkList = null;
|
||||
|
||||
if(curItem.Parent != null)
|
||||
checkList = curItem.Parent.Children;
|
||||
else
|
||||
checkList = curFile.TOC;
|
||||
|
||||
int nIdx = checkList.IndexOf(curItem);
|
||||
if((nIdx+nCnt)>checkList.Count)
|
||||
checkList.Add(chkItem);
|
||||
else
|
||||
checkList.Insert(nIdx+nCnt, chkItem);
|
||||
|
||||
curFile.MergLinks.Add(chkItem);
|
||||
MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
|
||||
}
|
||||
|
||||
nCnt++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// target points to a single topic
|
||||
TOCItem chkItem = currentFile.GetTOCItemByLocal(sTarget);
|
||||
if(chkItem != null)
|
||||
{
|
||||
curItem.AssociatedFile = currentFile;
|
||||
curItem.Children = chkItem.Children;
|
||||
curItem.ChmFile = currentFile.ChmFilePath;
|
||||
curItem.ImageIndex = chkItem.ImageIndex;
|
||||
curItem.Local = chkItem.Local;
|
||||
curItem.MergeLink = chkItem.MergeLink;
|
||||
curItem.Name = chkItem.Name;
|
||||
curItem.TocMode = chkItem.TocMode;
|
||||
curItem.TopicOffset = chkItem.TopicOffset;
|
||||
|
||||
curFile.MergLinks.Add(chkItem);
|
||||
MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds sub-items of an TOC-entry to the merg-linked list.
|
||||
/// This will mark this item as "added" during the extra merge run
|
||||
/// of the HtmlHelpSystem class.
|
||||
/// </summary>
|
||||
/// <param name="tocs">TOCItem list</param>
|
||||
/// <param name="merged">Arraylist which holds the merged-items</param>
|
||||
internal void MarkChildrenAdded(ArrayList tocs, ArrayList merged)
|
||||
{
|
||||
foreach(TOCItem curItem in tocs)
|
||||
{
|
||||
if(!merged.Contains(curItem))
|
||||
{
|
||||
merged.Add(curItem);
|
||||
|
||||
MarkChildrenAdded(curItem.Children, merged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes merge-links from the toc of files which were not loaded
|
||||
/// </summary>
|
||||
internal void RemoveMergeLinks()
|
||||
{
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
if( curFile.MergLinks.Count > 0)
|
||||
{
|
||||
while(curFile.MergLinks.Count > 0)
|
||||
{
|
||||
TOCItem curItem = curFile.MergLinks[0] as TOCItem;
|
||||
if(curItem.MergeLink.Length > 0)
|
||||
curFile.RemoveTOCItem(curItem);
|
||||
|
||||
curFile.MergLinks.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the information types and categories read by the CHMFile instance
|
||||
/// into the system instance
|
||||
/// </summary>
|
||||
/// <param name="chmFile">file instance</param>
|
||||
private void MergeFileInfoTypesCategories(CHMFile chmFile)
|
||||
{
|
||||
if(chmFile.HasInformationTypes)
|
||||
{
|
||||
for(int i=0; i<chmFile.InformationTypes.Count;i++)
|
||||
{
|
||||
InformationType curType = chmFile.InformationTypes[i] as InformationType;
|
||||
InformationType sysType = GetInformationType( curType.Name );
|
||||
|
||||
if( sysType == null)
|
||||
_informationTypes.Add(curType);
|
||||
else
|
||||
curType.ReferenceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(chmFile.HasCategories)
|
||||
{
|
||||
for(int i=0; i<chmFile.Categories.Count;i++)
|
||||
{
|
||||
Category curCat = chmFile.Categories[i] as Category;
|
||||
Category sysCat = GetCategory( curCat.Name );
|
||||
|
||||
if(sysCat == null)
|
||||
_categories.Add(curCat);
|
||||
else
|
||||
curCat.ReferenceCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the information types and categories read by the CHMFile instance
|
||||
/// </summary>
|
||||
/// <param name="chmFile">file instance</param>
|
||||
private void RemoveFileInfoTypesCategories(CHMFile chmFile)
|
||||
{
|
||||
if(chmFile.HasInformationTypes)
|
||||
{
|
||||
for(int i=0; i<chmFile.InformationTypes.Count;i++)
|
||||
{
|
||||
InformationType curType = chmFile.InformationTypes[i] as InformationType;
|
||||
InformationType sysType = GetInformationType( curType.Name );
|
||||
|
||||
if(sysType != null)
|
||||
{
|
||||
sysType.ReferenceCount--;
|
||||
|
||||
if(sysType.ReferenceCount<=0)
|
||||
_informationTypes.Remove(sysType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(chmFile.HasCategories)
|
||||
{
|
||||
for(int i=0; i<chmFile.Categories.Count;i++)
|
||||
{
|
||||
Category curCat = chmFile.Categories[i] as Category;
|
||||
Category sysCat = GetCategory( curCat.Name );
|
||||
|
||||
if(sysCat != null)
|
||||
{
|
||||
sysCat.ReferenceCount--;
|
||||
|
||||
if(sysCat.ReferenceCount<=0)
|
||||
_categories.Remove(sysCat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a chm file from the internal file collection
|
||||
/// </summary>
|
||||
/// <param name="chmFile">full file path of the chm file to remove</param>
|
||||
public void RemoveFile(string chmFile)
|
||||
{
|
||||
int nIdx = -1;
|
||||
CHMFile removeInstance=null;
|
||||
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
nIdx++;
|
||||
|
||||
if( curFile.ChmFilePath.ToLower() == chmFile.ToLower() )
|
||||
{
|
||||
removeInstance = curFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(nIdx >= 0)
|
||||
{
|
||||
_toc.Clear(); // forces a rebuild of the merged toc
|
||||
_index.Clear(); // force a rebuild of the merged index
|
||||
|
||||
RemoveFileInfoTypesCategories(removeInstance);
|
||||
_chmFiles.RemoveAt(nIdx);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes all files and destroys TOC/index
|
||||
/// </summary>
|
||||
public void CloseAllFiles()
|
||||
{
|
||||
for(int i=0; i < _chmFiles.Count; i++)
|
||||
{
|
||||
CHMFile curFile = _chmFiles[i] as CHMFile;
|
||||
|
||||
_chmFiles.RemoveAt(i);
|
||||
curFile.Dispose();
|
||||
i--;
|
||||
}
|
||||
|
||||
_chmFiles.Clear();
|
||||
_toc.Clear();
|
||||
_index.Clear();
|
||||
_informationTypes.Clear();
|
||||
_categories.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of loaded chm files.
|
||||
/// </summary>
|
||||
public CHMFile[] FileList
|
||||
{
|
||||
get
|
||||
{
|
||||
CHMFile[] ret = new CHMFile[ _chmFiles.Count ];
|
||||
for(int i=0;i<_chmFiles.Count;i++)
|
||||
ret[i] = (CHMFile)_chmFiles[i];
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the HtmlHelpSystem instance contains 1 or more information types
|
||||
/// </summary>
|
||||
public bool HasInformationTypes
|
||||
{
|
||||
get { return (_informationTypes.Count>0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the HtmlHelpSystem instance contains 1 or more categories
|
||||
/// </summary>
|
||||
public bool HasCategories
|
||||
{
|
||||
get { return (_categories.Count>0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ArrayList of <see cref="InformationType">InformationType</see> items
|
||||
/// </summary>
|
||||
public ArrayList InformationTypes
|
||||
{
|
||||
get { return _informationTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ArrayList of <see cref="Category">Category</see> items
|
||||
/// </summary>
|
||||
public ArrayList Categories
|
||||
{
|
||||
get { return _categories; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the information type specified by its name
|
||||
/// </summary>
|
||||
/// <param name="name">name of the information type to receive</param>
|
||||
/// <returns>Returns the Instance for the name or null if not found</returns>
|
||||
public InformationType GetInformationType(string name)
|
||||
{
|
||||
if(HasInformationTypes)
|
||||
{
|
||||
for(int i=0; i<_informationTypes.Count;i++)
|
||||
{
|
||||
InformationType iT = _informationTypes[i] as InformationType;
|
||||
|
||||
if(iT.Name == name)
|
||||
return iT;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category specifiyd by its name
|
||||
/// </summary>
|
||||
/// <param name="name">name of the category</param>
|
||||
/// <returns>Returns the Instance for the name or null if not found</returns>
|
||||
public Category GetCategory(string name)
|
||||
{
|
||||
if(HasCategories)
|
||||
{
|
||||
for(int i=0; i<_categories.Count;i++)
|
||||
{
|
||||
Category cat = _categories[i] as Category;
|
||||
|
||||
if(cat.Name == name)
|
||||
return cat;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default topic
|
||||
/// </summary>
|
||||
public string DefaultTopic
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _chmFiles.Count > 0 )
|
||||
{
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
if( curFile.DefaultTopic.Length > 0)
|
||||
{
|
||||
return curFile.FormURL( curFile.DefaultTopic );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "about:blank";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a merged table of contents of all opened chm files
|
||||
/// </summary>
|
||||
public TableOfContents TableOfContents
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _chmFiles.Count > 0 )
|
||||
{
|
||||
if( _toc.Count() <= 0)
|
||||
{
|
||||
// merge toc of files
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
_toc.MergeToC( curFile.TOC );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _toc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a merged index of all opened chm files
|
||||
/// </summary>
|
||||
public Index Index
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _chmFiles.Count > 0 )
|
||||
{
|
||||
if( (_index.Count(IndexType.KeywordLinks)+_index.Count(IndexType.AssiciativeLinks)) <= 0)
|
||||
{
|
||||
// merge index files
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
_index.MergeIndex( curFile.IndexKLinks, IndexType.KeywordLinks);
|
||||
_index.MergeIndex( curFile.IndexALinks, IndexType.AssiciativeLinks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag if the current instance offers a table of contents
|
||||
/// </summary>
|
||||
public bool HasTableOfContents
|
||||
{
|
||||
get
|
||||
{
|
||||
return (TableOfContents.Count() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag if the current instance offers an index
|
||||
/// </summary>
|
||||
public bool HasIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return (HasALinks || HasKLinks);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag if the index holds klinks
|
||||
/// </summary>
|
||||
public bool HasKLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_index.Count(IndexType.KeywordLinks) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag if the index holds alinks
|
||||
/// </summary>
|
||||
public bool HasALinks
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_index.Count(IndexType.AssiciativeLinks) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag if the current instance supports fulltext searching
|
||||
/// </summary>
|
||||
public bool FullTextSearch
|
||||
{
|
||||
get
|
||||
{
|
||||
bool bRet = false;
|
||||
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
bRet |= curFile.FullTextSearch;
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a full-text search over the chm files
|
||||
/// </summary>
|
||||
/// <param name="words">words to search</param>
|
||||
/// <param name="partialMatches">true if partial word should be matched also
|
||||
/// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
|
||||
/// <param name="titleOnly">true if titles only</param>
|
||||
/// <returns>A DataTable containing the search hits</returns>
|
||||
public DataTable PerformSearch(string words, bool partialMatches, bool titleOnly)
|
||||
{
|
||||
return PerformSearch(words, -1, partialMatches, titleOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a full-text search over the chm files
|
||||
/// </summary>
|
||||
/// <param name="words">words to search</param>
|
||||
/// <param name="MaxResults">maximal number of hits to return</param>
|
||||
/// <param name="partialMatches">true if partial word should be matched also
|
||||
/// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
|
||||
/// <param name="titleOnly">true if titles only</param>
|
||||
/// <returns>A DataTable containing the search hits</returns>
|
||||
public DataTable PerformSearch(string words, int MaxResults, bool partialMatches, bool titleOnly)
|
||||
{
|
||||
if( ! FullTextSearch )
|
||||
return null;
|
||||
|
||||
DataTable dtResult = null;
|
||||
|
||||
int nCnt = 0;
|
||||
|
||||
foreach(CHMFile curFile in _chmFiles)
|
||||
{
|
||||
if(nCnt == 0)
|
||||
{
|
||||
if(curFile.FullTextSearchEngine.CanSearch)
|
||||
{
|
||||
if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))
|
||||
{
|
||||
dtResult = curFile.FullTextSearchEngine.Hits;
|
||||
dtResult.DefaultView.Sort = "Rating DESC";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(curFile.FullTextSearchEngine.CanSearch)
|
||||
{
|
||||
if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))
|
||||
{
|
||||
DataTable table = curFile.FullTextSearchEngine.Hits;
|
||||
|
||||
// append rows from 2nd file
|
||||
foreach(DataRow curRow in table.Rows)
|
||||
{
|
||||
dtResult.ImportRow( curRow );
|
||||
}
|
||||
|
||||
dtResult.DefaultView.Sort = "Rating DESC";
|
||||
|
||||
// adjust max hits
|
||||
if(MaxResults >= 0)
|
||||
{
|
||||
if(dtResult.DefaultView.Count > MaxResults)
|
||||
{
|
||||
for(int i=MaxResults-1; i<dtResult.DefaultView.Count-1;i++)
|
||||
{
|
||||
if(dtResult.DefaultView[i].Row.RowState != DataRowState.Deleted)
|
||||
{
|
||||
dtResult.DefaultView[i].Row.Delete();
|
||||
dtResult.DefaultView[i].Row.AcceptChanges();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
dtResult.AcceptChanges();
|
||||
dtResult.DefaultView.Sort = "Rating DESC";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nCnt++;
|
||||
}
|
||||
|
||||
return dtResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
855
irc/TechBot/CHMLibrary/HttpUtility.cs
Normal file
855
irc/TechBot/CHMLibrary/HttpUtility.cs
Normal file
@@ -0,0 +1,855 @@
|
||||
//
|
||||
// System.Web.HttpUtility
|
||||
//
|
||||
// Authors:
|
||||
// Patrik Torstensson (Patrik.Torstensson@labs2.com)
|
||||
// Wictor Wilén (decode/encode functions) (wictor@ibizkit.se)
|
||||
// Tim Coleman (tim@timcoleman.com)
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
//
|
||||
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
// using System.Web.Util;
|
||||
|
||||
namespace HtmlHelp.ChmDecoding
|
||||
{
|
||||
public sealed class HttpUtility {
|
||||
|
||||
#region Fields
|
||||
|
||||
const string _hex = "0123456789ABCDEF";
|
||||
const string _chars = "<>;:.?=&@*+%/\\";
|
||||
static Hashtable entities;
|
||||
static object lock_ = new object ();
|
||||
|
||||
#endregion // Fields
|
||||
|
||||
static Hashtable Entities {
|
||||
get {
|
||||
lock (lock_) {
|
||||
if (entities == null)
|
||||
InitEntities ();
|
||||
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Constructors
|
||||
|
||||
static void InitEntities ()
|
||||
{
|
||||
// Build the hash table of HTML entity references. This list comes
|
||||
// from the HTML 4.01 W3C recommendation.
|
||||
entities = new Hashtable ();
|
||||
entities.Add ("nbsp", '\u00A0');
|
||||
entities.Add ("iexcl", '\u00A1');
|
||||
entities.Add ("cent", '\u00A2');
|
||||
entities.Add ("pound", '\u00A3');
|
||||
entities.Add ("curren", '\u00A4');
|
||||
entities.Add ("yen", '\u00A5');
|
||||
entities.Add ("brvbar", '\u00A6');
|
||||
entities.Add ("sect", '\u00A7');
|
||||
entities.Add ("uml", '\u00A8');
|
||||
entities.Add ("copy", '\u00A9');
|
||||
entities.Add ("ordf", '\u00AA');
|
||||
entities.Add ("laquo", '\u00AB');
|
||||
entities.Add ("not", '\u00AC');
|
||||
entities.Add ("shy", '\u00AD');
|
||||
entities.Add ("reg", '\u00AE');
|
||||
entities.Add ("macr", '\u00AF');
|
||||
entities.Add ("deg", '\u00B0');
|
||||
entities.Add ("plusmn", '\u00B1');
|
||||
entities.Add ("sup2", '\u00B2');
|
||||
entities.Add ("sup3", '\u00B3');
|
||||
entities.Add ("acute", '\u00B4');
|
||||
entities.Add ("micro", '\u00B5');
|
||||
entities.Add ("para", '\u00B6');
|
||||
entities.Add ("middot", '\u00B7');
|
||||
entities.Add ("cedil", '\u00B8');
|
||||
entities.Add ("sup1", '\u00B9');
|
||||
entities.Add ("ordm", '\u00BA');
|
||||
entities.Add ("raquo", '\u00BB');
|
||||
entities.Add ("frac14", '\u00BC');
|
||||
entities.Add ("frac12", '\u00BD');
|
||||
entities.Add ("frac34", '\u00BE');
|
||||
entities.Add ("iquest", '\u00BF');
|
||||
entities.Add ("Agrave", '\u00C0');
|
||||
entities.Add ("Aacute", '\u00C1');
|
||||
entities.Add ("Acirc", '\u00C2');
|
||||
entities.Add ("Atilde", '\u00C3');
|
||||
entities.Add ("Auml", '\u00C4');
|
||||
entities.Add ("Aring", '\u00C5');
|
||||
entities.Add ("AElig", '\u00C6');
|
||||
entities.Add ("Ccedil", '\u00C7');
|
||||
entities.Add ("Egrave", '\u00C8');
|
||||
entities.Add ("Eacute", '\u00C9');
|
||||
entities.Add ("Ecirc", '\u00CA');
|
||||
entities.Add ("Euml", '\u00CB');
|
||||
entities.Add ("Igrave", '\u00CC');
|
||||
entities.Add ("Iacute", '\u00CD');
|
||||
entities.Add ("Icirc", '\u00CE');
|
||||
entities.Add ("Iuml", '\u00CF');
|
||||
entities.Add ("ETH", '\u00D0');
|
||||
entities.Add ("Ntilde", '\u00D1');
|
||||
entities.Add ("Ograve", '\u00D2');
|
||||
entities.Add ("Oacute", '\u00D3');
|
||||
entities.Add ("Ocirc", '\u00D4');
|
||||
entities.Add ("Otilde", '\u00D5');
|
||||
entities.Add ("Ouml", '\u00D6');
|
||||
entities.Add ("times", '\u00D7');
|
||||
entities.Add ("Oslash", '\u00D8');
|
||||
entities.Add ("Ugrave", '\u00D9');
|
||||
entities.Add ("Uacute", '\u00DA');
|
||||
entities.Add ("Ucirc", '\u00DB');
|
||||
entities.Add ("Uuml", '\u00DC');
|
||||
entities.Add ("Yacute", '\u00DD');
|
||||
entities.Add ("THORN", '\u00DE');
|
||||
entities.Add ("szlig", '\u00DF');
|
||||
entities.Add ("agrave", '\u00E0');
|
||||
entities.Add ("aacute", '\u00E1');
|
||||
entities.Add ("acirc", '\u00E2');
|
||||
entities.Add ("atilde", '\u00E3');
|
||||
entities.Add ("auml", '\u00E4');
|
||||
entities.Add ("aring", '\u00E5');
|
||||
entities.Add ("aelig", '\u00E6');
|
||||
entities.Add ("ccedil", '\u00E7');
|
||||
entities.Add ("egrave", '\u00E8');
|
||||
entities.Add ("eacute", '\u00E9');
|
||||
entities.Add ("ecirc", '\u00EA');
|
||||
entities.Add ("euml", '\u00EB');
|
||||
entities.Add ("igrave", '\u00EC');
|
||||
entities.Add ("iacute", '\u00ED');
|
||||
entities.Add ("icirc", '\u00EE');
|
||||
entities.Add ("iuml", '\u00EF');
|
||||
entities.Add ("eth", '\u00F0');
|
||||
entities.Add ("ntilde", '\u00F1');
|
||||
entities.Add ("ograve", '\u00F2');
|
||||
entities.Add ("oacute", '\u00F3');
|
||||
entities.Add ("ocirc", '\u00F4');
|
||||
entities.Add ("otilde", '\u00F5');
|
||||
entities.Add ("ouml", '\u00F6');
|
||||
entities.Add ("divide", '\u00F7');
|
||||
entities.Add ("oslash", '\u00F8');
|
||||
entities.Add ("ugrave", '\u00F9');
|
||||
entities.Add ("uacute", '\u00FA');
|
||||
entities.Add ("ucirc", '\u00FB');
|
||||
entities.Add ("uuml", '\u00FC');
|
||||
entities.Add ("yacute", '\u00FD');
|
||||
entities.Add ("thorn", '\u00FE');
|
||||
entities.Add ("yuml", '\u00FF');
|
||||
entities.Add ("fnof", '\u0192');
|
||||
entities.Add ("Alpha", '\u0391');
|
||||
entities.Add ("Beta", '\u0392');
|
||||
entities.Add ("Gamma", '\u0393');
|
||||
entities.Add ("Delta", '\u0394');
|
||||
entities.Add ("Epsilon", '\u0395');
|
||||
entities.Add ("Zeta", '\u0396');
|
||||
entities.Add ("Eta", '\u0397');
|
||||
entities.Add ("Theta", '\u0398');
|
||||
entities.Add ("Iota", '\u0399');
|
||||
entities.Add ("Kappa", '\u039A');
|
||||
entities.Add ("Lambda", '\u039B');
|
||||
entities.Add ("Mu", '\u039C');
|
||||
entities.Add ("Nu", '\u039D');
|
||||
entities.Add ("Xi", '\u039E');
|
||||
entities.Add ("Omicron", '\u039F');
|
||||
entities.Add ("Pi", '\u03A0');
|
||||
entities.Add ("Rho", '\u03A1');
|
||||
entities.Add ("Sigma", '\u03A3');
|
||||
entities.Add ("Tau", '\u03A4');
|
||||
entities.Add ("Upsilon", '\u03A5');
|
||||
entities.Add ("Phi", '\u03A6');
|
||||
entities.Add ("Chi", '\u03A7');
|
||||
entities.Add ("Psi", '\u03A8');
|
||||
entities.Add ("Omega", '\u03A9');
|
||||
entities.Add ("alpha", '\u03B1');
|
||||
entities.Add ("beta", '\u03B2');
|
||||
entities.Add ("gamma", '\u03B3');
|
||||
entities.Add ("delta", '\u03B4');
|
||||
entities.Add ("epsilon", '\u03B5');
|
||||
entities.Add ("zeta", '\u03B6');
|
||||
entities.Add ("eta", '\u03B7');
|
||||
entities.Add ("theta", '\u03B8');
|
||||
entities.Add ("iota", '\u03B9');
|
||||
entities.Add ("kappa", '\u03BA');
|
||||
entities.Add ("lambda", '\u03BB');
|
||||
entities.Add ("mu", '\u03BC');
|
||||
entities.Add ("nu", '\u03BD');
|
||||
entities.Add ("xi", '\u03BE');
|
||||
entities.Add ("omicron", '\u03BF');
|
||||
entities.Add ("pi", '\u03C0');
|
||||
entities.Add ("rho", '\u03C1');
|
||||
entities.Add ("sigmaf", '\u03C2');
|
||||
entities.Add ("sigma", '\u03C3');
|
||||
entities.Add ("tau", '\u03C4');
|
||||
entities.Add ("upsilon", '\u03C5');
|
||||
entities.Add ("phi", '\u03C6');
|
||||
entities.Add ("chi", '\u03C7');
|
||||
entities.Add ("psi", '\u03C8');
|
||||
entities.Add ("omega", '\u03C9');
|
||||
entities.Add ("thetasym", '\u03D1');
|
||||
entities.Add ("upsih", '\u03D2');
|
||||
entities.Add ("piv", '\u03D6');
|
||||
entities.Add ("bull", '\u2022');
|
||||
entities.Add ("hellip", '\u2026');
|
||||
entities.Add ("prime", '\u2032');
|
||||
entities.Add ("Prime", '\u2033');
|
||||
entities.Add ("oline", '\u203E');
|
||||
entities.Add ("frasl", '\u2044');
|
||||
entities.Add ("weierp", '\u2118');
|
||||
entities.Add ("image", '\u2111');
|
||||
entities.Add ("real", '\u211C');
|
||||
entities.Add ("trade", '\u2122');
|
||||
entities.Add ("alefsym", '\u2135');
|
||||
entities.Add ("larr", '\u2190');
|
||||
entities.Add ("uarr", '\u2191');
|
||||
entities.Add ("rarr", '\u2192');
|
||||
entities.Add ("darr", '\u2193');
|
||||
entities.Add ("harr", '\u2194');
|
||||
entities.Add ("crarr", '\u21B5');
|
||||
entities.Add ("lArr", '\u21D0');
|
||||
entities.Add ("uArr", '\u21D1');
|
||||
entities.Add ("rArr", '\u21D2');
|
||||
entities.Add ("dArr", '\u21D3');
|
||||
entities.Add ("hArr", '\u21D4');
|
||||
entities.Add ("forall", '\u2200');
|
||||
entities.Add ("part", '\u2202');
|
||||
entities.Add ("exist", '\u2203');
|
||||
entities.Add ("empty", '\u2205');
|
||||
entities.Add ("nabla", '\u2207');
|
||||
entities.Add ("isin", '\u2208');
|
||||
entities.Add ("notin", '\u2209');
|
||||
entities.Add ("ni", '\u220B');
|
||||
entities.Add ("prod", '\u220F');
|
||||
entities.Add ("sum", '\u2211');
|
||||
entities.Add ("minus", '\u2212');
|
||||
entities.Add ("lowast", '\u2217');
|
||||
entities.Add ("radic", '\u221A');
|
||||
entities.Add ("prop", '\u221D');
|
||||
entities.Add ("infin", '\u221E');
|
||||
entities.Add ("ang", '\u2220');
|
||||
entities.Add ("and", '\u2227');
|
||||
entities.Add ("or", '\u2228');
|
||||
entities.Add ("cap", '\u2229');
|
||||
entities.Add ("cup", '\u222A');
|
||||
entities.Add ("int", '\u222B');
|
||||
entities.Add ("there4", '\u2234');
|
||||
entities.Add ("sim", '\u223C');
|
||||
entities.Add ("cong", '\u2245');
|
||||
entities.Add ("asymp", '\u2248');
|
||||
entities.Add ("ne", '\u2260');
|
||||
entities.Add ("equiv", '\u2261');
|
||||
entities.Add ("le", '\u2264');
|
||||
entities.Add ("ge", '\u2265');
|
||||
entities.Add ("sub", '\u2282');
|
||||
entities.Add ("sup", '\u2283');
|
||||
entities.Add ("nsub", '\u2284');
|
||||
entities.Add ("sube", '\u2286');
|
||||
entities.Add ("supe", '\u2287');
|
||||
entities.Add ("oplus", '\u2295');
|
||||
entities.Add ("otimes", '\u2297');
|
||||
entities.Add ("perp", '\u22A5');
|
||||
entities.Add ("sdot", '\u22C5');
|
||||
entities.Add ("lceil", '\u2308');
|
||||
entities.Add ("rceil", '\u2309');
|
||||
entities.Add ("lfloor", '\u230A');
|
||||
entities.Add ("rfloor", '\u230B');
|
||||
entities.Add ("lang", '\u2329');
|
||||
entities.Add ("rang", '\u232A');
|
||||
entities.Add ("loz", '\u25CA');
|
||||
entities.Add ("spades", '\u2660');
|
||||
entities.Add ("clubs", '\u2663');
|
||||
entities.Add ("hearts", '\u2665');
|
||||
entities.Add ("diams", '\u2666');
|
||||
entities.Add ("quot", '\u0022');
|
||||
entities.Add ("amp", '\u0026');
|
||||
entities.Add ("lt", '\u003C');
|
||||
entities.Add ("gt", '\u003E');
|
||||
entities.Add ("OElig", '\u0152');
|
||||
entities.Add ("oelig", '\u0153');
|
||||
entities.Add ("Scaron", '\u0160');
|
||||
entities.Add ("scaron", '\u0161');
|
||||
entities.Add ("Yuml", '\u0178');
|
||||
entities.Add ("circ", '\u02C6');
|
||||
entities.Add ("tilde", '\u02DC');
|
||||
entities.Add ("ensp", '\u2002');
|
||||
entities.Add ("emsp", '\u2003');
|
||||
entities.Add ("thinsp", '\u2009');
|
||||
entities.Add ("zwnj", '\u200C');
|
||||
entities.Add ("zwj", '\u200D');
|
||||
entities.Add ("lrm", '\u200E');
|
||||
entities.Add ("rlm", '\u200F');
|
||||
entities.Add ("ndash", '\u2013');
|
||||
entities.Add ("mdash", '\u2014');
|
||||
entities.Add ("lsquo", '\u2018');
|
||||
entities.Add ("rsquo", '\u2019');
|
||||
entities.Add ("sbquo", '\u201A');
|
||||
entities.Add ("ldquo", '\u201C');
|
||||
entities.Add ("rdquo", '\u201D');
|
||||
entities.Add ("bdquo", '\u201E');
|
||||
entities.Add ("dagger", '\u2020');
|
||||
entities.Add ("Dagger", '\u2021');
|
||||
entities.Add ("permil", '\u2030');
|
||||
entities.Add ("lsaquo", '\u2039');
|
||||
entities.Add ("rsaquo", '\u203A');
|
||||
entities.Add ("euro", '\u20AC');
|
||||
}
|
||||
|
||||
public HttpUtility ()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion // Constructors
|
||||
|
||||
#region Methods
|
||||
|
||||
public static void HtmlAttributeEncode (string s, TextWriter output)
|
||||
{
|
||||
output.Write(HtmlAttributeEncode(s));
|
||||
}
|
||||
|
||||
public static string HtmlAttributeEncode (string s)
|
||||
{
|
||||
if (null == s)
|
||||
return null;
|
||||
|
||||
if (s.IndexOf ('&') == -1 && s.IndexOf ('"') == -1)
|
||||
return s;
|
||||
|
||||
StringBuilder output = new StringBuilder ();
|
||||
foreach (char c in s)
|
||||
switch (c) {
|
||||
case '&' :
|
||||
output.Append ("&");
|
||||
break;
|
||||
case '"' :
|
||||
output.Append (""");
|
||||
break;
|
||||
default:
|
||||
output.Append (c);
|
||||
break;
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
public static string UrlDecode (string str)
|
||||
{
|
||||
return UrlDecode(str, Encoding.UTF8);
|
||||
}
|
||||
|
||||
private static char [] GetChars (MemoryStream b, Encoding e)
|
||||
{
|
||||
return e.GetChars (b.GetBuffer (), 0, (int) b.Length);
|
||||
}
|
||||
|
||||
public static string UrlDecode (string s, Encoding e)
|
||||
{
|
||||
if (null == s)
|
||||
return null;
|
||||
|
||||
if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)
|
||||
return s;
|
||||
|
||||
if (e == null)
|
||||
e = Encoding.UTF8;
|
||||
|
||||
StringBuilder output = new StringBuilder ();
|
||||
long len = s.Length;
|
||||
NumberStyles hexa = NumberStyles.HexNumber;
|
||||
MemoryStream bytes = new MemoryStream ();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (s [i] == '%' && i + 2 < len) {
|
||||
if (s [i + 1] == 'u' && i + 5 < len) {
|
||||
if (bytes.Length > 0) {
|
||||
output.Append (GetChars (bytes, e));
|
||||
bytes.SetLength (0);
|
||||
}
|
||||
output.Append ((char) Int32.Parse (s.Substring (i + 2, 4), hexa));
|
||||
i += 5;
|
||||
} else {
|
||||
bytes.WriteByte ((byte) Int32.Parse (s.Substring (i + 1, 2), hexa));
|
||||
i += 2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytes.Length > 0) {
|
||||
output.Append (GetChars (bytes, e));
|
||||
bytes.SetLength (0);
|
||||
}
|
||||
|
||||
if (s [i] == '+') {
|
||||
output.Append (' ');
|
||||
} else {
|
||||
output.Append (s [i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes.Length > 0) {
|
||||
output.Append (GetChars (bytes, e));
|
||||
}
|
||||
|
||||
bytes = null;
|
||||
return output.ToString ();
|
||||
}
|
||||
|
||||
public static string UrlDecode (byte [] bytes, Encoding e)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
return UrlDecode (bytes, 0, bytes.Length, e);
|
||||
}
|
||||
|
||||
private static int GetInt (byte b)
|
||||
{
|
||||
char c = Char.ToUpper ((char) b);
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
|
||||
if (c < 'A' || c > 'F')
|
||||
return 0;
|
||||
|
||||
return (c - 'A' + 10);
|
||||
}
|
||||
|
||||
private static char GetChar (byte [] bytes, int offset, int length)
|
||||
{
|
||||
int value = 0;
|
||||
int end = length + offset;
|
||||
for (int i = offset; i < end; i++)
|
||||
value = (value << 4) + GetInt (bytes [offset]);
|
||||
|
||||
return (char) value;
|
||||
}
|
||||
|
||||
public static string UrlDecode (byte [] bytes, int offset, int count, Encoding e)
|
||||
{
|
||||
if (bytes == null || count == 0)
|
||||
return null;
|
||||
|
||||
if (bytes == null)
|
||||
throw new ArgumentNullException ("bytes");
|
||||
|
||||
if (offset < 0 || offset > bytes.Length)
|
||||
throw new ArgumentOutOfRangeException ("offset");
|
||||
|
||||
if (count < 0 || offset + count > bytes.Length)
|
||||
throw new ArgumentOutOfRangeException ("count");
|
||||
|
||||
StringBuilder output = new StringBuilder ();
|
||||
MemoryStream acc = new MemoryStream ();
|
||||
|
||||
int end = count + offset;
|
||||
for (int i = offset; i < end; i++) {
|
||||
if (bytes [i] == '%' && i + 2 < count) {
|
||||
if (bytes [i + 1] == (byte) 'u' && i + 5 < end) {
|
||||
if (acc.Length > 0) {
|
||||
output.Append (GetChars (acc, e));
|
||||
acc.SetLength (0);
|
||||
}
|
||||
output.Append (GetChar (bytes, offset + 2, 4));
|
||||
i += 5;
|
||||
} else {
|
||||
acc.WriteByte ((byte) GetChar (bytes, offset + 1, 2));
|
||||
i += 2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (acc.Length > 0) {
|
||||
output.Append (GetChars (acc, e));
|
||||
acc.SetLength (0);
|
||||
}
|
||||
|
||||
if (bytes [i] == '+') {
|
||||
output.Append (' ');
|
||||
} else {
|
||||
output.Append ((char) bytes [i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (acc.Length > 0) {
|
||||
output.Append (GetChars (acc, e));
|
||||
}
|
||||
|
||||
acc = null;
|
||||
return output.ToString ();
|
||||
}
|
||||
|
||||
public static byte [] UrlDecodeToBytes (byte [] bytes)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
return UrlDecodeToBytes (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public static byte [] UrlDecodeToBytes (string str)
|
||||
{
|
||||
return UrlDecodeToBytes (str, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static byte [] UrlDecodeToBytes (string str, Encoding e)
|
||||
{
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
if (e == null)
|
||||
throw new ArgumentNullException ("e");
|
||||
|
||||
return UrlDecodeToBytes (e.GetBytes (str));
|
||||
}
|
||||
|
||||
public static byte [] UrlDecodeToBytes (byte [] bytes, int offset, int count)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
int len = bytes.Length;
|
||||
if (offset < 0 || offset >= len)
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
|
||||
if (count < 0 || offset > len - count)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
MemoryStream result = new MemoryStream ();
|
||||
int end = offset + count;
|
||||
for (int i = offset; i < end; i++){
|
||||
char c = (char) bytes [i];
|
||||
if (c == '+')
|
||||
c = ' ';
|
||||
else if (c == '%' && i < end - 2) {
|
||||
c = GetChar (bytes, i, 2);
|
||||
i += 2;
|
||||
}
|
||||
result.WriteByte ((byte) c);
|
||||
}
|
||||
|
||||
return result.ToArray ();
|
||||
}
|
||||
|
||||
public static string UrlEncode(string str)
|
||||
{
|
||||
return UrlEncode(str, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static string UrlEncode (string s, Encoding Enc)
|
||||
{
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (s == "")
|
||||
return "";
|
||||
|
||||
byte [] bytes = Enc.GetBytes (s);
|
||||
byte [] b =UrlEncodeToBytes (bytes, 0, bytes.Length);
|
||||
return Encoding.ASCII.GetString (b,0,b.Length);
|
||||
}
|
||||
|
||||
public static string UrlEncode (byte [] bytes)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
if (bytes.Length == 0)
|
||||
return "";
|
||||
|
||||
byte []b=UrlEncodeToBytes(bytes, 0, bytes.Length);
|
||||
return Encoding.ASCII.GetString (b,0,b.Length);
|
||||
}
|
||||
|
||||
public static string UrlEncode (byte [] bytes, int offset, int count)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
if (bytes.Length == 0)
|
||||
return "";
|
||||
|
||||
byte []b=UrlEncodeToBytes(bytes, offset, count);
|
||||
return Encoding.ASCII.GetString (b,0,b.Length);
|
||||
}
|
||||
|
||||
public static byte [] UrlEncodeToBytes (string str)
|
||||
{
|
||||
return UrlEncodeToBytes (str, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static byte [] UrlEncodeToBytes (string str, Encoding e)
|
||||
{
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
if (str == "")
|
||||
return new byte [0];
|
||||
|
||||
byte [] bytes = e.GetBytes (str);
|
||||
return UrlEncodeToBytes (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public static byte [] UrlEncodeToBytes (byte [] bytes)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
if (bytes.Length == 0)
|
||||
return new byte [0];
|
||||
|
||||
return UrlEncodeToBytes (bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
static char [] hexChars = "0123456789abcdef".ToCharArray ();
|
||||
|
||||
public static byte [] UrlEncodeToBytes (byte [] bytes, int offset, int count)
|
||||
{
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
int len = bytes.Length;
|
||||
if (len == 0)
|
||||
return new byte [0];
|
||||
|
||||
if (offset < 0 || offset >= len)
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
|
||||
if (count < 0 || count > len - offset)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
MemoryStream result = new MemoryStream ();
|
||||
int end = offset + count;
|
||||
for (int i = offset; i < end; i++) {
|
||||
char c = (char) bytes [i];
|
||||
if ((c == ' ') || (c < '0' && c != '-' && c != '.') ||
|
||||
(c < 'A' && c > '9') ||
|
||||
(c > 'Z' && c < 'a' && c != '_') ||
|
||||
(c > 'z')) {
|
||||
result.WriteByte ((byte) '%');
|
||||
int idx = ((int) c) >> 4;
|
||||
result.WriteByte ((byte) hexChars [idx]);
|
||||
idx = ((int) c) & 0x0F;
|
||||
result.WriteByte ((byte) hexChars [idx]);
|
||||
} else {
|
||||
result.WriteByte ((byte) c);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray ();
|
||||
}
|
||||
|
||||
public static string UrlEncodeUnicode (string str)
|
||||
{
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
StringBuilder result = new StringBuilder ();
|
||||
int end = str.Length;
|
||||
for (int i = 0; i < end; i++) {
|
||||
int idx;
|
||||
char c = str [i];
|
||||
if (c > 255) {
|
||||
result.Append ("%u");
|
||||
idx = ((int) c) >> 24;
|
||||
result.Append (hexChars [idx]);
|
||||
idx = (((int) c) >> 16) & 0x0F;
|
||||
result.Append (hexChars [idx]);
|
||||
idx = (((int) c) >> 8) & 0x0F;
|
||||
result.Append (hexChars [idx]);
|
||||
idx = ((int) c) & 0x0F;
|
||||
result.Append (hexChars [idx]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c == ' ') || (c < '0' && c != '-' && c != '.') ||
|
||||
(c < 'A' && c > '9') ||
|
||||
(c > 'Z' && c < 'a' && c != '_') ||
|
||||
(c > 'z')) {
|
||||
result.Append ('%');
|
||||
idx = ((int) c) >> 4;
|
||||
result.Append (hexChars [idx]);
|
||||
idx = ((int) c) & 0x0F;
|
||||
result.Append (hexChars [idx]);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Append (c);
|
||||
}
|
||||
|
||||
return result.ToString ();
|
||||
}
|
||||
|
||||
public static byte [] UrlEncodeUnicodeToBytes (string str)
|
||||
{
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
if (str == "")
|
||||
return new byte [0];
|
||||
|
||||
return Encoding.ASCII.GetBytes (UrlEncodeUnicode (str));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an HTML-encoded string and returns the decoded string.
|
||||
/// </summary>
|
||||
/// <param name="s">The HTML string to decode. </param>
|
||||
/// <returns>The decoded text.</returns>
|
||||
public static string HtmlDecode (string s)
|
||||
{
|
||||
if (s == null)
|
||||
throw new ArgumentNullException ("s");
|
||||
|
||||
if (s.IndexOf ('&') == -1)
|
||||
return s;
|
||||
|
||||
bool insideEntity = false; // used to indicate that we are in a potential entity
|
||||
string entity = String.Empty;
|
||||
StringBuilder output = new StringBuilder ();
|
||||
int len = s.Length;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = s [i];
|
||||
switch (c) {
|
||||
case '&' :
|
||||
output.Append (entity);
|
||||
entity = "&";
|
||||
insideEntity = true;
|
||||
break;
|
||||
case ';' :
|
||||
if (!insideEntity) {
|
||||
output.Append (c);
|
||||
break;
|
||||
}
|
||||
|
||||
entity += c;
|
||||
int length = entity.Length;
|
||||
if (length >= 2 && entity[1] == '#' && entity[2] != ';')
|
||||
entity = ((char) Int32.Parse (entity.Substring (2, entity.Length - 3))).ToString();
|
||||
else if (length > 1 && Entities.ContainsKey (entity.Substring (1, entity.Length - 2)))
|
||||
entity = Entities [entity.Substring (1, entity.Length - 2)].ToString ();
|
||||
|
||||
output.Append (entity);
|
||||
entity = String.Empty;
|
||||
insideEntity = false;
|
||||
break;
|
||||
default :
|
||||
if (insideEntity)
|
||||
entity += c;
|
||||
else
|
||||
output.Append (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
output.Append (entity);
|
||||
return output.ToString ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
|
||||
/// </summary>
|
||||
/// <param name="s">The HTML string to decode</param>
|
||||
/// <param name="output">The TextWriter output stream containing the decoded string. </param>
|
||||
public static void HtmlDecode(string s, TextWriter output)
|
||||
{
|
||||
if (s != null)
|
||||
output.Write (HtmlDecode (s));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTML-encodes a string and returns the encoded string.
|
||||
/// </summary>
|
||||
/// <param name="s">The text string to encode. </param>
|
||||
/// <returns>The HTML-encoded text.</returns>
|
||||
public static string HtmlEncode (string s)
|
||||
{
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
StringBuilder output = new StringBuilder ();
|
||||
|
||||
foreach (char c in s)
|
||||
switch (c) {
|
||||
case '&' :
|
||||
output.Append ("&");
|
||||
break;
|
||||
case '>' :
|
||||
output.Append (">");
|
||||
break;
|
||||
case '<' :
|
||||
output.Append ("<");
|
||||
break;
|
||||
case '"' :
|
||||
output.Append (""");
|
||||
break;
|
||||
default:
|
||||
if ((int) c > 128) {
|
||||
output.Append ("&#");
|
||||
output.Append (((int) c).ToString ());
|
||||
output.Append (";");
|
||||
}
|
||||
else
|
||||
output.Append (c);
|
||||
break;
|
||||
}
|
||||
return output.ToString ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
|
||||
/// </summary>
|
||||
/// <param name="s">The string to encode. </param>
|
||||
/// <param name="output">The TextWriter output stream containing the encoded string. </param>
|
||||
public static void HtmlEncode(string s, TextWriter output)
|
||||
{
|
||||
if (s != null)
|
||||
output.Write (HtmlEncode (s));
|
||||
}
|
||||
|
||||
#if NET_1_1
|
||||
public string UrlPathEncode (string s)
|
||||
{
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
int idx = s.IndexOf ("?");
|
||||
string s2 = null;
|
||||
if (idx != -1) {
|
||||
s2 = s.Substring (0, idx-1);
|
||||
s2 = UrlEncode (s2) + s.Substring (idx);
|
||||
} else {
|
||||
s2 = UrlEncode (s);
|
||||
}
|
||||
|
||||
return s2;
|
||||
}
|
||||
#endif
|
||||
#endregion // Methods
|
||||
}
|
||||
}
|
||||
322
irc/TechBot/CHMLibrary/Index.cs
Normal file
322
irc/TechBot/CHMLibrary/Index.cs
Normal file
@@ -0,0 +1,322 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration for specifying the index type
|
||||
/// </summary>
|
||||
public enum IndexType
|
||||
{
|
||||
/// <summary>
|
||||
/// Keyword links should be used
|
||||
/// </summary>
|
||||
KeywordLinks = 0,
|
||||
/// <summary>
|
||||
/// Associative links should be used
|
||||
/// </summary>
|
||||
AssiciativeLinks = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class <c>Index</c> holds the (keyword links) KLinks and (associative links) ALinks of the htmlhelp
|
||||
/// system. It implements methods for easy index-based searching.
|
||||
/// </summary>
|
||||
public class Index
|
||||
{
|
||||
private ArrayList _kLinks = new ArrayList();
|
||||
private ArrayList _aLinks = new ArrayList();
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
public Index()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="kLinks">arraylist with keyword links</param>
|
||||
/// <param name="aLinks">arraylist with associative links</param>
|
||||
public Index(ArrayList kLinks, ArrayList aLinks)
|
||||
{
|
||||
_kLinks= kLinks;
|
||||
_aLinks = aLinks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current toc
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if(_aLinks != null)
|
||||
_aLinks.Clear();
|
||||
if(_kLinks != null)
|
||||
_kLinks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of index items for a specific type
|
||||
/// </summary>
|
||||
/// <param name="typeOfIndex">type of index</param>
|
||||
/// <returns>Returns the number of index items for a specific type</returns>
|
||||
public int Count(IndexType typeOfIndex)
|
||||
{
|
||||
ArrayList _index = null;
|
||||
|
||||
switch( typeOfIndex )
|
||||
{
|
||||
case IndexType.AssiciativeLinks: _index = _aLinks; break;
|
||||
case IndexType.KeywordLinks: _index = _kLinks; break;
|
||||
}
|
||||
|
||||
if(_index != null)
|
||||
return _index.Count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal index list of keyword links
|
||||
/// </summary>
|
||||
public ArrayList KLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_kLinks==null)
|
||||
_kLinks = new ArrayList();
|
||||
|
||||
return _kLinks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal index list of associative links
|
||||
/// </summary>
|
||||
public ArrayList ALinks
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_aLinks==null)
|
||||
_aLinks = new ArrayList();
|
||||
|
||||
return _aLinks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the the index list <c>arrIndex</c> into the current one
|
||||
/// </summary>
|
||||
/// <param name="arrIndex">indexlist which should be merged with the current one</param>
|
||||
/// <param name="typeOfIndex">type of index to merge</param>
|
||||
public void MergeIndex( ArrayList arrIndex, IndexType typeOfIndex )
|
||||
{
|
||||
ArrayList _index = null;
|
||||
|
||||
switch(typeOfIndex)
|
||||
{
|
||||
case IndexType.AssiciativeLinks: _index = _aLinks;break;
|
||||
case IndexType.KeywordLinks: _index = _kLinks;break;
|
||||
}
|
||||
|
||||
foreach(IndexItem curItem in arrIndex)
|
||||
{
|
||||
//IndexItem searchItem = ContainsIndex(_index, curItem.KeyWordPath);
|
||||
int insertIndex=0;
|
||||
IndexItem searchItem = BinSearch(0, _index.Count-1, _index, curItem.KeyWordPath, false, false, ref insertIndex);
|
||||
|
||||
if(searchItem != null)
|
||||
{
|
||||
// extend the keywords topics
|
||||
foreach(IndexTopic curEntry in curItem.Topics)
|
||||
{
|
||||
searchItem.Topics.Add( curEntry );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the item to the global collection
|
||||
//_index.Add( curItem );
|
||||
|
||||
if(insertIndex > _index.Count)
|
||||
_index.Add(curItem);
|
||||
else
|
||||
_index.Insert(insertIndex, curItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches an index entry using recursive binary search algo (divide and conquer).
|
||||
/// </summary>
|
||||
/// <param name="nStart">start index for searching</param>
|
||||
/// <param name="nEnd">end index for searching</param>
|
||||
/// <param name="arrIndex">arraylist containing sorted IndexItem entries</param>
|
||||
/// <param name="keywordPath">keyword path to search</param>
|
||||
/// <param name="searchKeyword">true if the keywordPath will only contain the keyword not the complete path</param>
|
||||
/// <param name="caseInsensitive">True if case should be ignored</param>
|
||||
/// <param name="insertIndex">out reference. will receive the index where the item with the
|
||||
/// keywordPath should be inserted if not found (receives -1 if the item was found)</param>
|
||||
/// <returns>Returns an IndexItem instance if found, otherwise null
|
||||
/// (use insertIndex for inserting the new item in a sorted order)</returns>
|
||||
private IndexItem BinSearch(int nStart, int nEnd, ArrayList arrIndex, string keywordPath,
|
||||
bool searchKeyword, bool caseInsensitive, ref int insertIndex)
|
||||
{
|
||||
if( arrIndex.Count <= 0 )
|
||||
{
|
||||
insertIndex=0;
|
||||
return null;
|
||||
}
|
||||
|
||||
if(caseInsensitive)
|
||||
keywordPath = keywordPath.ToLower();
|
||||
|
||||
if( (nEnd - nStart) > 1)
|
||||
{
|
||||
int nCheck = nStart + (nEnd-nStart)/2;
|
||||
|
||||
IndexItem iC = arrIndex[nCheck] as IndexItem;
|
||||
|
||||
string sCompare = iC.KeyWordPath;
|
||||
|
||||
if(searchKeyword)
|
||||
sCompare = iC.KeyWord;
|
||||
|
||||
if(caseInsensitive)
|
||||
sCompare = sCompare.ToLower();
|
||||
|
||||
if( sCompare == keywordPath )
|
||||
{
|
||||
insertIndex=-1;
|
||||
return iC;
|
||||
}
|
||||
|
||||
if( keywordPath.CompareTo(sCompare) < 0 )
|
||||
{
|
||||
return BinSearch(nStart, nCheck-1, arrIndex, keywordPath, searchKeyword, caseInsensitive, ref insertIndex);
|
||||
}
|
||||
|
||||
if( keywordPath.CompareTo(sCompare) > 0 )
|
||||
{
|
||||
return BinSearch(nCheck+1, nEnd, arrIndex, keywordPath, searchKeyword, caseInsensitive, ref insertIndex);
|
||||
}
|
||||
}
|
||||
else if(nEnd-nStart == 1)
|
||||
{
|
||||
IndexItem i1 = arrIndex[nStart] as IndexItem;
|
||||
IndexItem i2 = arrIndex[nEnd] as IndexItem;
|
||||
|
||||
string sCompare1 = i1.KeyWordPath;
|
||||
|
||||
if(searchKeyword)
|
||||
sCompare1 = i1.KeyWord;
|
||||
|
||||
if(caseInsensitive)
|
||||
sCompare1 = sCompare1.ToLower();
|
||||
|
||||
string sCompare2 = i2.KeyWordPath;
|
||||
|
||||
if(searchKeyword)
|
||||
sCompare2 = i2.KeyWord;
|
||||
|
||||
if(caseInsensitive)
|
||||
sCompare2 = sCompare2.ToLower();
|
||||
|
||||
if( sCompare1 == keywordPath)
|
||||
{
|
||||
insertIndex = -1;
|
||||
return i1;
|
||||
}
|
||||
|
||||
if( sCompare2 == keywordPath)
|
||||
{
|
||||
insertIndex = -1;
|
||||
return i2;
|
||||
}
|
||||
|
||||
if( sCompare1.CompareTo(keywordPath) > 0)
|
||||
{
|
||||
insertIndex = nStart;
|
||||
return null;
|
||||
}
|
||||
else if( sCompare2.CompareTo(keywordPath) > 0)
|
||||
{
|
||||
insertIndex = nEnd;
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
insertIndex = nEnd+1;
|
||||
}
|
||||
}
|
||||
|
||||
IndexItem itm = arrIndex[nEnd] as IndexItem;
|
||||
|
||||
string sCompareI = itm.KeyWordPath;
|
||||
|
||||
if(searchKeyword)
|
||||
sCompareI = itm.KeyWord;
|
||||
|
||||
if(caseInsensitive)
|
||||
sCompareI = sCompareI.ToLower();
|
||||
|
||||
if( sCompareI.CompareTo(keywordPath) > 0)
|
||||
{
|
||||
insertIndex = nStart;
|
||||
return null;
|
||||
}
|
||||
else if( sCompareI.CompareTo(keywordPath) < 0)
|
||||
{
|
||||
insertIndex = nEnd+1;
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
insertIndex = -1;
|
||||
return arrIndex[nEnd] as IndexItem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a keyword exists in a index collection
|
||||
/// </summary>
|
||||
/// <param name="arrIndex">index to search (arraylist of IndexItems)</param>
|
||||
/// <param name="keywordPath">keywordpath to search</param>
|
||||
/// <returns>Returns the found IndexItem, otherwise null</returns>
|
||||
private IndexItem ContainsIndex(ArrayList arrIndex, string keywordPath)
|
||||
{
|
||||
foreach(IndexItem curItem in arrIndex)
|
||||
{
|
||||
if(curItem.KeyWordPath == keywordPath)
|
||||
return curItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the alinks- or klinks-index for a specific keyword/associative
|
||||
/// </summary>
|
||||
/// <param name="search">keyword/associative to search</param>
|
||||
/// <param name="typeOfIndex">type of index to search</param>
|
||||
/// <returns>Returns an ArrayList which contains IndexTopic items or null if nothing was found</returns>
|
||||
public IndexItem SearchIndex(string search, IndexType typeOfIndex)
|
||||
{
|
||||
ArrayList _index = null;
|
||||
|
||||
switch( typeOfIndex )
|
||||
{
|
||||
case IndexType.AssiciativeLinks: _index = _aLinks;break;
|
||||
case IndexType.KeywordLinks: _index = _kLinks;break;
|
||||
}
|
||||
|
||||
int insertIdx=0;
|
||||
IndexItem foundItem = BinSearch(0, _index.Count, _index, search, true, true, ref insertIdx);
|
||||
|
||||
return foundItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
396
irc/TechBot/CHMLibrary/IndexItem.cs
Normal file
396
irc/TechBot/CHMLibrary/IndexItem.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>IndexItem</c> implements an help-index item
|
||||
/// </summary>
|
||||
public sealed class IndexItem : IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal member storing the keyword
|
||||
/// </summary>
|
||||
private string _keyWord = "";
|
||||
/// <summary>
|
||||
/// Internal member storing all associated information type strings
|
||||
/// </summary>
|
||||
private ArrayList _infoTypeStrings = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing the flag if this is a see-also keyword
|
||||
/// </summary>
|
||||
private bool _isSeeAlso = false;
|
||||
/// <summary>
|
||||
/// Internal member storing the indent of the keyword
|
||||
/// </summary>
|
||||
private int _indent = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the last index of the keyword in the seperated list
|
||||
/// </summary>
|
||||
private int _charIndex = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the entry index
|
||||
/// </summary>
|
||||
private int _entryIndex = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing an array of see-also values
|
||||
/// </summary>
|
||||
private string[] _seeAlso = new string[0];
|
||||
/// <summary>
|
||||
/// Internal member storing an array of topic offsets
|
||||
/// </summary>
|
||||
private int[] _nTopics = new int[0];
|
||||
/// <summary>
|
||||
/// Internal member storing the topics
|
||||
/// </summary>
|
||||
private ArrayList _Topics = null;
|
||||
/// <summary>
|
||||
/// Associated CHMFile instance
|
||||
/// </summary>
|
||||
private CHMFile _chmFile = null;
|
||||
/// <summary>
|
||||
/// Internal flag specifying the chm file path
|
||||
/// </summary>
|
||||
private string _chmFileName = "";
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="chmFile">associated CHMFile instance</param>
|
||||
/// <param name="keyWord">keyword</param>
|
||||
/// <param name="isSeeAlso">true if it is a see-also keyword</param>
|
||||
/// <param name="indent">indent of the entry</param>
|
||||
/// <param name="charIndex">char index of the last keyword in the separated list</param>
|
||||
/// <param name="entryIndex">index of the entry</param>
|
||||
/// <param name="seeAlsoValues">string array with see-also values</param>
|
||||
/// <param name="topicOffsets">integer array with topic offsets</param>
|
||||
internal IndexItem(CHMFile chmFile, string keyWord, bool isSeeAlso, int indent, int charIndex, int entryIndex, string[] seeAlsoValues, int[] topicOffsets)
|
||||
{
|
||||
_chmFile = chmFile;
|
||||
_chmFileName = _chmFile.ChmFilePath;
|
||||
_keyWord = keyWord;
|
||||
_isSeeAlso = isSeeAlso;
|
||||
_indent = indent;
|
||||
_charIndex = charIndex;
|
||||
_entryIndex = entryIndex;
|
||||
_seeAlso = seeAlsoValues;
|
||||
_nTopics = topicOffsets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
public IndexItem()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
/// <param name="writeFileName">true if the chm filename should be written</param>
|
||||
internal void Dump(ref BinaryWriter writer, bool writeFileName)
|
||||
{
|
||||
int i=0;
|
||||
|
||||
writer.Write(_keyWord);
|
||||
writer.Write(_isSeeAlso);
|
||||
writer.Write(_indent);
|
||||
|
||||
if(writeFileName)
|
||||
writer.Write(_chmFileName);
|
||||
|
||||
writer.Write(_infoTypeStrings.Count);
|
||||
|
||||
for(i=0; i<_infoTypeStrings.Count; i++)
|
||||
writer.Write( (_infoTypeStrings[i]).ToString() );
|
||||
|
||||
writer.Write(_seeAlso.Length);
|
||||
|
||||
for(i=0; i<_seeAlso.Length; i++)
|
||||
{
|
||||
if(_seeAlso[i] == null)
|
||||
writer.Write("");
|
||||
else
|
||||
writer.Write( _seeAlso[i] );
|
||||
}
|
||||
|
||||
writer.Write(Topics.Count);
|
||||
|
||||
for(i=0; i<Topics.Count; i++)
|
||||
{
|
||||
IndexTopic topic = ((IndexTopic)(Topics[i]));
|
||||
topic.Dump(ref writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
Dump(ref writer, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
/// <param name="filesList">filelist from helpsystem</param>
|
||||
internal bool ReadDump(ref BinaryReader reader, ArrayList filesList)
|
||||
{
|
||||
int i=0;
|
||||
_keyWord = reader.ReadString();
|
||||
_isSeeAlso = reader.ReadBoolean();
|
||||
_indent = reader.ReadInt32();
|
||||
_chmFileName = reader.ReadString();
|
||||
|
||||
foreach(CHMFile curFile in filesList)
|
||||
{
|
||||
if(curFile.ChmFilePath == _chmFileName)
|
||||
{
|
||||
_chmFile = curFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_chmFile==null)
|
||||
return false;
|
||||
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
string sIT = reader.ReadString();
|
||||
_infoTypeStrings.Add(sIT);
|
||||
}
|
||||
|
||||
nCnt = reader.ReadInt32();
|
||||
|
||||
_seeAlso = new string[nCnt];
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
_seeAlso[i] = reader.ReadString();
|
||||
}
|
||||
|
||||
nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
IndexTopic topic = new IndexTopic("","","","");
|
||||
topic.SetChmInfo( _chmFile.CompileFile, _chmFile.ChmFilePath);
|
||||
topic.AssociatedFile = _chmFile;
|
||||
topic.ReadDump(ref reader);
|
||||
|
||||
Topics.Add(topic);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
int i=0;
|
||||
_keyWord = reader.ReadString();
|
||||
_isSeeAlso = reader.ReadBoolean();
|
||||
_indent = reader.ReadInt32();
|
||||
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
string sIT = reader.ReadString();
|
||||
_infoTypeStrings.Add(sIT);
|
||||
}
|
||||
|
||||
nCnt = reader.ReadInt32();
|
||||
|
||||
_seeAlso = new string[nCnt];
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
_seeAlso[i] = reader.ReadString();
|
||||
}
|
||||
|
||||
nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
IndexTopic topic = new IndexTopic("","","","");
|
||||
topic.AssociatedFile = _chmFile;
|
||||
topic.SetChmInfo( _chmFile.CompileFile, _chmFile.ChmFilePath);
|
||||
topic.ReadDump(ref reader);
|
||||
Topics.Add(topic);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Implements the compareto method which allows sorting.
|
||||
/// </summary>
|
||||
/// <param name="obj">object to compare to</param>
|
||||
/// <returns>See <see cref="System.IComparable">IComparable.CompareTo()</see></returns>
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if( obj.GetType() == this.GetType() )
|
||||
{
|
||||
IndexItem cmp = (IndexItem)obj;
|
||||
|
||||
return this.KeyWordPath.CompareTo( cmp.KeyWordPath );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
internal CHMFile ChmFile
|
||||
{
|
||||
get { return _chmFile; }
|
||||
set { _chmFile = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ArrayList which holds all information types/categories this item is associated
|
||||
/// </summary>
|
||||
internal ArrayList InfoTypeStrings
|
||||
{
|
||||
get { return _infoTypeStrings; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a see-also string to the index item and marks it as see also item
|
||||
/// </summary>
|
||||
/// <param name="seeAlsoString">see also string to add</param>
|
||||
internal void AddSeeAlso(string seeAlsoString)
|
||||
{
|
||||
string[] seeAlso = new string[ _seeAlso.Length +1 ];
|
||||
for(int i=0; i<_seeAlso.Length; i++)
|
||||
seeAlso[i] = _seeAlso[i];
|
||||
|
||||
seeAlso[_seeAlso.Length] = seeAlsoString;
|
||||
_seeAlso = seeAlso;
|
||||
_isSeeAlso = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the full keyword-path of this item ( ", " separated list)
|
||||
/// </summary>
|
||||
public string KeyWordPath
|
||||
{
|
||||
get { return _keyWord; }
|
||||
set { _keyWord = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyword of this item
|
||||
/// </summary>
|
||||
public string KeyWord
|
||||
{
|
||||
get
|
||||
{
|
||||
return _keyWord.Substring(_charIndex, _keyWord.Length-_charIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyword of this item with prefixing indent spaces
|
||||
/// </summary>
|
||||
public string IndentKeyWord
|
||||
{
|
||||
get
|
||||
{
|
||||
string sKW = this.KeyWord;
|
||||
StringBuilder sb = new StringBuilder("",this.Indent*3 + sKW.Length);
|
||||
for(int i=0; i<this.Indent; i++)
|
||||
sb.Append(" ");
|
||||
sb.Append(sKW);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the see-also flag of this item
|
||||
/// </summary>
|
||||
public bool IsSeeAlso
|
||||
{
|
||||
get { return _isSeeAlso; }
|
||||
set { _isSeeAlso = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the listbox indent for this item
|
||||
/// </summary>
|
||||
public int Indent
|
||||
{
|
||||
get { return _indent; }
|
||||
set { _indent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the character index of an indent keyword
|
||||
/// </summary>
|
||||
public int CharIndex
|
||||
{
|
||||
get { return _charIndex; }
|
||||
set { _charIndex = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the see-also values of this item
|
||||
/// </summary>
|
||||
public string[] SeeAlso
|
||||
{
|
||||
get { return _seeAlso; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array with the associated topics
|
||||
/// </summary>
|
||||
public ArrayList Topics
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _Topics == null )
|
||||
{
|
||||
if(IsSeeAlso)
|
||||
{
|
||||
_Topics = new ArrayList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (_chmFile != null) && (_chmFile.TopicsFile != null) )
|
||||
{
|
||||
_Topics = new ArrayList();
|
||||
|
||||
for(int i=0; i<_nTopics.Length; i++)
|
||||
{
|
||||
IndexTopic newTopic = IndexTopic.FromTopicEntry((TopicEntry)_chmFile.TopicsFile.TopicTable[ _nTopics[i] ]);
|
||||
newTopic.AssociatedFile = _chmFile;
|
||||
_Topics.Add( newTopic );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_Topics = new ArrayList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _Topics;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
216
irc/TechBot/CHMLibrary/IndexTopic.cs
Normal file
216
irc/TechBot/CHMLibrary/IndexTopic.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>IndexTopic</c> implements an entry for the <see cref="IndexItem">IndexItem</see> topics list.
|
||||
/// </summary>
|
||||
public sealed class IndexTopic
|
||||
{
|
||||
private DataMode _topicMode = DataMode.TextBased;
|
||||
private string _title="";
|
||||
private string _local="";
|
||||
private string _compileFile = "";
|
||||
private string _chmPath = "";
|
||||
private int _topicOffset = -1;
|
||||
private CHMFile _associatedFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the class based on an existing TopicEntry
|
||||
/// </summary>
|
||||
/// <param name="entry"></param>
|
||||
internal static IndexTopic FromTopicEntry(TopicEntry entry)
|
||||
{
|
||||
return new IndexTopic(entry.EntryOffset, entry.ChmFile);
|
||||
//return new IndexTopic( entry.Title, entry.Locale, entry.ChmFile.CompileFile, entry.ChmFile.ChmFilePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the class (binary extraction mode)
|
||||
/// </summary>
|
||||
/// <param name="topicOffset">offset of the topic entry</param>
|
||||
/// <param name="associatedFile">associated CHMFile instance</param>
|
||||
internal IndexTopic(int topicOffset, CHMFile associatedFile)
|
||||
{
|
||||
_topicMode = DataMode.Binary;
|
||||
_topicOffset = topicOffset;
|
||||
_associatedFile = associatedFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="Title">topic title</param>
|
||||
/// <param name="local">topic local (content filename)</param>
|
||||
/// <param name="compilefile">name of the chm file (location of topic)</param>
|
||||
/// <param name="chmpath">path of the chm file</param>
|
||||
public IndexTopic(string Title, string local, string compilefile, string chmpath)
|
||||
{
|
||||
_topicMode = DataMode.TextBased;
|
||||
_title = Title;
|
||||
_local = local;
|
||||
_compileFile = compilefile;
|
||||
_chmPath = chmpath;
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write((int)_topicMode);
|
||||
|
||||
if(_topicMode==DataMode.TextBased)
|
||||
{
|
||||
writer.Write(_title);
|
||||
writer.Write(_local);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(_topicOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
_topicMode = (DataMode)reader.ReadInt32();
|
||||
|
||||
if(_topicMode==DataMode.TextBased)
|
||||
{
|
||||
_title = reader.ReadString();
|
||||
_local = reader.ReadString();
|
||||
}
|
||||
else
|
||||
{
|
||||
_topicOffset = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Internally used to set the chm-finos when reading from dump store
|
||||
/// </summary>
|
||||
/// <param name="compilefile"></param>
|
||||
/// <param name="chmpath"></param>
|
||||
internal void SetChmInfo(string compilefile, string chmpath)
|
||||
{
|
||||
_compileFile = compilefile;
|
||||
_chmPath = chmpath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
internal CHMFile AssociatedFile
|
||||
{
|
||||
get { return _associatedFile; }
|
||||
set { _associatedFile = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the topic title
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get
|
||||
{
|
||||
if((_topicMode == DataMode.Binary )&&(_associatedFile!=null))
|
||||
{
|
||||
if( _topicOffset >= 0)
|
||||
{
|
||||
TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);
|
||||
if(te != null)
|
||||
{
|
||||
return te.Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _title;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local (content filename)
|
||||
/// </summary>
|
||||
public string Local
|
||||
{
|
||||
get
|
||||
{
|
||||
if((_topicMode == DataMode.Binary )&&(_associatedFile!=null))
|
||||
{
|
||||
if( _topicOffset >= 0)
|
||||
{
|
||||
TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);
|
||||
if(te != null)
|
||||
{
|
||||
return te.Locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _local;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compile file (location)
|
||||
/// </summary>
|
||||
public string CompileFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.CompileFile;
|
||||
|
||||
return _compileFile;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chm file path
|
||||
/// </summary>
|
||||
public string ChmFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile != null)
|
||||
return _associatedFile.ChmFilePath;
|
||||
|
||||
return _chmPath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url
|
||||
/// </summary>
|
||||
public string URL
|
||||
{
|
||||
get
|
||||
{
|
||||
string sL = Local;
|
||||
|
||||
if(sL.Length<=0)
|
||||
return "";//"about:blank";
|
||||
|
||||
if( (sL.ToLower().IndexOf("http://") >= 0) ||
|
||||
(sL.ToLower().IndexOf("https://") >= 0) ||
|
||||
(sL.ToLower().IndexOf("mailto:") >= 0) ||
|
||||
(sL.ToLower().IndexOf("ftp://") >= 0) ||
|
||||
(sL.ToLower().IndexOf("ms-its:") >= 0))
|
||||
return sL;
|
||||
|
||||
return HtmlHelpSystem.UrlPrefix + ChmFilePath + "::/" + sL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
irc/TechBot/CHMLibrary/InformationType.cs
Normal file
146
irc/TechBot/CHMLibrary/InformationType.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration for specifying the mode of the information type
|
||||
/// </summary>
|
||||
public enum InformationTypeMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Inclusive information type. The user will be allowed to select from one or more information types.
|
||||
/// </summary>
|
||||
Inclusive = 0,
|
||||
/// <summary>
|
||||
/// Exclusive information type. The user will be allowed to choose only one information type within each category
|
||||
/// </summary>
|
||||
Exclusive = 1,
|
||||
/// <summary>
|
||||
/// Hidden information type. The user cannot see this information types (only for API calls).
|
||||
/// </summary>
|
||||
Hidden = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class <c>InformationType</c> implements a methods/properties for an information type.
|
||||
/// </summary>
|
||||
/// <remarks>Note: Information types and categories allow users to filter help contents.
|
||||
/// They are only supported if using sitemap TOC and/or sitemap Index.</remarks>
|
||||
public class InformationType
|
||||
{
|
||||
private string _name = "";
|
||||
private string _description = "";
|
||||
private InformationTypeMode _typeMode = InformationTypeMode.Inclusive;
|
||||
private bool _isInCategory = false;
|
||||
private int _referenceCount = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
/// <remarks>the mode is set to InformationTypeMode.Inclusive by default</remarks>
|
||||
public InformationType() : this("","")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
/// <param name="name">name of the information type</param>
|
||||
/// <param name="description">description</param>
|
||||
/// <remarks>the mode is set to InformationTypeMode.Inclusive by default</remarks>
|
||||
public InformationType(string name, string description) : this(name, description, InformationTypeMode.Inclusive)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
/// <param name="name">name of the information type</param>
|
||||
/// <param name="description">description</param>
|
||||
/// <param name="mode">mode of the information type</param>
|
||||
public InformationType(string name, string description, InformationTypeMode mode)
|
||||
{
|
||||
_name = name;
|
||||
_description = description;
|
||||
_typeMode = mode;
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
writer.Write( (int)_typeMode );
|
||||
writer.Write( _name );
|
||||
writer.Write( _description );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
_typeMode = (InformationTypeMode)reader.ReadInt32();
|
||||
_name = reader.ReadString();
|
||||
_description = reader.ReadString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Sets the flag if this information type is nested in at least one category
|
||||
/// </summary>
|
||||
/// <param name="newValue">true or false</param>
|
||||
internal void SetCategoryFlag(bool newValue)
|
||||
{
|
||||
_isInCategory = newValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the reference count of this information type instance
|
||||
/// </summary>
|
||||
internal int ReferenceCount
|
||||
{
|
||||
get { return _referenceCount; }
|
||||
set { _referenceCount = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets true if this information type is nested in at least one category
|
||||
/// </summary>
|
||||
public bool IsInCategory
|
||||
{
|
||||
get { return _isInCategory; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the name of the information type
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set { _name = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the description of the information type
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return _description; }
|
||||
set { _name = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the mode of the information type
|
||||
/// </summary>
|
||||
public InformationTypeMode Mode
|
||||
{
|
||||
get { return _typeMode; }
|
||||
set { _typeMode = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
2842
irc/TechBot/CHMLibrary/Storage/CHMStream.cs
Normal file
2842
irc/TechBot/CHMLibrary/Storage/CHMStream.cs
Normal file
File diff suppressed because it is too large
Load Diff
494
irc/TechBot/CHMLibrary/TOCItem.cs
Normal file
494
irc/TechBot/CHMLibrary/TOCItem.cs
Normal file
@@ -0,0 +1,494 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>TOCItem</c> implements a toc-entry item
|
||||
/// </summary>
|
||||
public sealed class TOCItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant for standard folder (closed) image index (HH2 image list)
|
||||
/// </summary>
|
||||
public const int STD_FOLDER_HH2 = 4;
|
||||
/// <summary>
|
||||
/// Constant for standard folder (opened) image index (HH2 image list)
|
||||
/// </summary>
|
||||
public const int STD_FOLDER_OPEN_HH2 = 6;
|
||||
/// <summary>
|
||||
/// Constant for standard file image index (HH2 image list)
|
||||
/// </summary>
|
||||
public const int STD_FILE_HH2 = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Constant for standard folder (closed) image index (HH1 image list)
|
||||
/// </summary>
|
||||
public const int STD_FOLDER_HH1 = 0;
|
||||
/// <summary>
|
||||
/// Constant for standard folder (opened) image index (HH1 image list)
|
||||
/// </summary>
|
||||
public const int STD_FOLDER_OPEN_HH1 = 1;
|
||||
/// <summary>
|
||||
/// Constant for standard file image index (HH1 image list)
|
||||
/// </summary>
|
||||
public const int STD_FILE_HH1 = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Internal flag specifying the data extraction mode used for this item
|
||||
/// </summary>
|
||||
private DataMode _tocMode = DataMode.TextBased;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset (only used in binary tocs)
|
||||
/// </summary>
|
||||
private int _offset = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset of the next item(only used in binary tocs)
|
||||
/// </summary>
|
||||
private int _offsetNext = 0;
|
||||
/// <summary>
|
||||
/// Internal member storing a merge link.
|
||||
/// If the target file is in the merged files list of the CHM,
|
||||
/// this item will be replaced with the target TOC or Topic, if not it will
|
||||
/// be removed from TOC.
|
||||
/// </summary>
|
||||
private string _mergeLink = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the toc name
|
||||
/// </summary>
|
||||
private string _name = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the toc loca (content file)
|
||||
/// </summary>
|
||||
private string _local = "";
|
||||
/// <summary>
|
||||
/// Internal member storing all associated information type strings
|
||||
/// </summary>
|
||||
private ArrayList _infoTypeStrings = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chm file
|
||||
/// </summary>
|
||||
private string _chmFile = "";
|
||||
/// <summary>
|
||||
/// Internal member storing the image index
|
||||
/// </summary>
|
||||
private int _imageIndex = -1;
|
||||
/// <summary>
|
||||
/// Internal member storing the offset of the associated topic entry (for binary tocs)
|
||||
/// </summary>
|
||||
private int _topicOffset = -1;
|
||||
/// <summary>
|
||||
/// Internal member storing the toc children
|
||||
/// </summary>
|
||||
private ArrayList _children = new ArrayList();
|
||||
/// <summary>
|
||||
/// Internal member storing the parameter collection
|
||||
/// </summary>
|
||||
private Hashtable _otherParams = new Hashtable();
|
||||
/// <summary>
|
||||
/// Internal member storing the associated chmfile object
|
||||
/// </summary>
|
||||
private CHMFile _associatedFile = null;
|
||||
/// <summary>
|
||||
/// Parent item
|
||||
/// </summary>
|
||||
private TOCItem _parent=null;
|
||||
|
||||
/// <summary>
|
||||
/// Holds a pointer to the next item in the TOC
|
||||
/// </summary>
|
||||
public TOCItem Next=null;
|
||||
|
||||
/// <summary>
|
||||
/// Holds a pointer to the previous item in the TOC
|
||||
/// </summary>
|
||||
public TOCItem Prev=null;
|
||||
|
||||
/// <summary>
|
||||
/// Holds a pointer to the TreeNode where this TOC Item is used
|
||||
/// </summary>
|
||||
public System.Windows.Forms.TreeNode treeNode=null;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class used during text-based data extraction
|
||||
/// </summary>
|
||||
/// <param name="name">name of the item</param>
|
||||
/// <param name="local">local content file</param>
|
||||
/// <param name="ImageIndex">image index</param>
|
||||
/// <param name="chmFile">associated chm file</param>
|
||||
public TOCItem(string name, string local, int ImageIndex, string chmFile)
|
||||
{
|
||||
_tocMode = DataMode.TextBased;
|
||||
_name = name;
|
||||
_local = local;
|
||||
_imageIndex = ImageIndex;
|
||||
_chmFile = chmFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class used during binary data extraction
|
||||
/// </summary>
|
||||
/// <param name="topicOffset">offset of the associated topic entry</param>
|
||||
/// <param name="ImageIndex">image index to use</param>
|
||||
/// <param name="associatedFile">associated chm file</param>
|
||||
public TOCItem(int topicOffset, int ImageIndex, CHMFile associatedFile)
|
||||
{
|
||||
_tocMode = DataMode.Binary;
|
||||
_associatedFile = associatedFile;
|
||||
_chmFile = associatedFile.ChmFilePath;
|
||||
_topicOffset = topicOffset;
|
||||
_imageIndex = ImageIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
public TOCItem()
|
||||
{
|
||||
}
|
||||
|
||||
#region Data dumping
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
/// <param name="writeFilename">true if the chmfile name should be written</param>
|
||||
internal void Dump(ref BinaryWriter writer, bool writeFilename)
|
||||
{
|
||||
writer.Write((int)_tocMode);
|
||||
writer.Write(_topicOffset);
|
||||
writer.Write(_name);
|
||||
|
||||
if((_tocMode == DataMode.TextBased)||(_topicOffset<0))
|
||||
{
|
||||
writer.Write(_local);
|
||||
}
|
||||
|
||||
writer.Write(_imageIndex);
|
||||
|
||||
writer.Write(_mergeLink);
|
||||
|
||||
if(writeFilename)
|
||||
writer.Write(_chmFile);
|
||||
|
||||
writer.Write(_infoTypeStrings.Count);
|
||||
|
||||
for(int i=0; i<_infoTypeStrings.Count; i++)
|
||||
writer.Write( (_infoTypeStrings[i]).ToString() );
|
||||
|
||||
writer.Write(_children.Count);
|
||||
|
||||
for(int i=0; i<_children.Count; i++)
|
||||
{
|
||||
TOCItem child = ((TOCItem)(_children[i]));
|
||||
child.Dump(ref writer, writeFilename);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump the class data to a binary writer
|
||||
/// </summary>
|
||||
/// <param name="writer">writer to write the data</param>
|
||||
internal void Dump(ref BinaryWriter writer)
|
||||
{
|
||||
Dump(ref writer, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
/// <param name="readFilename">true if the chmfile name should be read</param>
|
||||
internal void ReadDump(ref BinaryReader reader, bool readFilename)
|
||||
{
|
||||
int i=0;
|
||||
_tocMode = (DataMode)reader.ReadInt32();
|
||||
_topicOffset = reader.ReadInt32();
|
||||
_name = reader.ReadString();
|
||||
|
||||
if((_tocMode == DataMode.TextBased)||(_topicOffset<0))
|
||||
{
|
||||
_local = reader.ReadString();
|
||||
}
|
||||
|
||||
_imageIndex = reader.ReadInt32();
|
||||
|
||||
_mergeLink = reader.ReadString();
|
||||
|
||||
if(readFilename)
|
||||
_chmFile = reader.ReadString();
|
||||
|
||||
int nCnt = reader.ReadInt32();
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
string sIT = reader.ReadString();
|
||||
_infoTypeStrings.Add(sIT);
|
||||
}
|
||||
|
||||
nCnt = reader.ReadInt32();
|
||||
|
||||
if(_associatedFile != null)
|
||||
_chmFile = _associatedFile.ChmFilePath;
|
||||
|
||||
for(i=0; i<nCnt; i++)
|
||||
{
|
||||
TOCItem child = new TOCItem();
|
||||
child.AssociatedFile = _associatedFile;
|
||||
|
||||
child.ReadDump(ref reader, readFilename);
|
||||
|
||||
if(_associatedFile != null)
|
||||
child.ChmFile = _associatedFile.ChmFilePath;
|
||||
else if(!readFilename)
|
||||
child.ChmFile = _chmFile;
|
||||
child.Parent = this;
|
||||
|
||||
_children.Add(child);
|
||||
|
||||
if(child.MergeLink.Length > 0)
|
||||
_associatedFile.MergLinks.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the object data from a dump store
|
||||
/// </summary>
|
||||
/// <param name="reader">reader to read the data</param>
|
||||
internal void ReadDump(ref BinaryReader reader)
|
||||
{
|
||||
ReadDump(ref reader, false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the data extraction mode with which this item was created.
|
||||
/// </summary>
|
||||
internal DataMode TocMode
|
||||
{
|
||||
get { return _tocMode; }
|
||||
set { _tocMode = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the offset of the associated topic entry
|
||||
/// </summary>
|
||||
internal int TopicOffset
|
||||
{
|
||||
get { return _topicOffset; }
|
||||
set { _topicOffset = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the associated CHMFile instance
|
||||
/// </summary>
|
||||
internal CHMFile AssociatedFile
|
||||
{
|
||||
get { return _associatedFile; }
|
||||
set
|
||||
{
|
||||
_associatedFile = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the offset of the item.
|
||||
/// </summary>
|
||||
/// <remarks>Only used in binary tocs</remarks>
|
||||
internal int Offset
|
||||
{
|
||||
get { return _offset; }
|
||||
set { _offset = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the offset of the next item.
|
||||
/// </summary>
|
||||
/// <remarks>Only used in binary tocs</remarks>
|
||||
internal int OffsetNext
|
||||
{
|
||||
get { return _offsetNext; }
|
||||
set { _offsetNext = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ArrayList which holds all information types/categories this item is associated
|
||||
/// </summary>
|
||||
internal ArrayList InfoTypeStrings
|
||||
{
|
||||
get { return _infoTypeStrings; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the parent of this item
|
||||
/// </summary>
|
||||
public TOCItem Parent
|
||||
{
|
||||
get { return _parent; }
|
||||
set { _parent = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the mergelink for this item.
|
||||
/// <b>You should not set the mergedlink by your own !</b>
|
||||
/// This is only for loading merged CHMs.
|
||||
/// </summary>
|
||||
public string MergeLink
|
||||
{
|
||||
get { return _mergeLink; }
|
||||
set { _mergeLink = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the name of the item
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_mergeLink.Length > 0)
|
||||
return "";
|
||||
|
||||
if(_name.Length <= 0)
|
||||
{
|
||||
if((_tocMode == DataMode.Binary )&&(_associatedFile!=null))
|
||||
{
|
||||
if( _topicOffset >= 0)
|
||||
{
|
||||
TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);
|
||||
if(te != null)
|
||||
{
|
||||
return te.Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _name;
|
||||
}
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the local of the item
|
||||
/// </summary>
|
||||
public string Local
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_mergeLink.Length > 0)
|
||||
return "";
|
||||
|
||||
if(_local.Length <= 0)
|
||||
{
|
||||
if((_tocMode == DataMode.Binary )&&(_associatedFile!=null))
|
||||
{
|
||||
if( _topicOffset >= 0)
|
||||
{
|
||||
TopicEntry te = (TopicEntry) (_associatedFile.TopicsFile[_topicOffset]);
|
||||
if(te != null)
|
||||
{
|
||||
return te.Locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _local;
|
||||
}
|
||||
set { _local = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the chm file
|
||||
/// </summary>
|
||||
public string ChmFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_associatedFile!=null)
|
||||
return _associatedFile.ChmFilePath;
|
||||
|
||||
return _chmFile;
|
||||
}
|
||||
set { _chmFile = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url for the webbrowser for this file
|
||||
/// </summary>
|
||||
public string Url
|
||||
{
|
||||
get
|
||||
{
|
||||
string sL = Local;
|
||||
|
||||
if( (sL.ToLower().IndexOf("http://") >= 0) ||
|
||||
(sL.ToLower().IndexOf("https://") >= 0) ||
|
||||
(sL.ToLower().IndexOf("mailto:") >= 0) ||
|
||||
(sL.ToLower().IndexOf("ftp://") >= 0) ||
|
||||
(sL.ToLower().IndexOf("ms-its:") >= 0))
|
||||
return sL;
|
||||
|
||||
return HtmlHelpSystem.UrlPrefix + ChmFile + "::/" + sL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the image index of the item
|
||||
/// </summary>
|
||||
/// <remarks>Set this to -1 for a default icon</remarks>
|
||||
public int ImageIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _imageIndex == -1)
|
||||
{
|
||||
int nFolderAdd = 0;
|
||||
|
||||
if((_associatedFile != null) && (_associatedFile.ImageTypeFolder))
|
||||
{
|
||||
// get the value which should be added, to display folders instead of books
|
||||
if(HtmlHelpSystem.UseHH2TreePics)
|
||||
nFolderAdd = 8;
|
||||
else
|
||||
nFolderAdd = 4;
|
||||
}
|
||||
|
||||
|
||||
if( _children.Count > 0)
|
||||
return (HtmlHelpSystem.UseHH2TreePics ? (STD_FOLDER_HH2+nFolderAdd) : (STD_FOLDER_HH1+nFolderAdd));
|
||||
|
||||
return (HtmlHelpSystem.UseHH2TreePics ? STD_FILE_HH2 : STD_FILE_HH1);
|
||||
}
|
||||
return _imageIndex;
|
||||
}
|
||||
set { _imageIndex = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the children of this item.
|
||||
/// </summary>
|
||||
/// <remarks>Each entry in the ArrayList is of type TOCItem</remarks>
|
||||
public ArrayList Children
|
||||
{
|
||||
get { return _children; }
|
||||
set { _children = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal hashtable storing all params
|
||||
/// </summary>
|
||||
public Hashtable Params
|
||||
{
|
||||
get { return _otherParams; }
|
||||
}
|
||||
}
|
||||
}
|
||||
198
irc/TechBot/CHMLibrary/TableOfContents.cs
Normal file
198
irc/TechBot/CHMLibrary/TableOfContents.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace HtmlHelp
|
||||
{
|
||||
/// <summary>
|
||||
/// The class <c>TableOfContents</c> holds the TOC of the htmlhelp system class.
|
||||
/// </summary>
|
||||
public class TableOfContents
|
||||
{
|
||||
private ArrayList _toc = new ArrayList();
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor
|
||||
/// </summary>
|
||||
public TableOfContents()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the class
|
||||
/// </summary>
|
||||
/// <param name="toc"></param>
|
||||
public TableOfContents(ArrayList toc)
|
||||
{
|
||||
_toc = toc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal stored table of contents
|
||||
/// </summary>
|
||||
public ArrayList TOC
|
||||
{
|
||||
get { return _toc; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current toc
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if(_toc!=null)
|
||||
_toc.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of topics in the toc
|
||||
/// </summary>
|
||||
/// <returns>Returns the number of topics in the toc</returns>
|
||||
public int Count()
|
||||
{
|
||||
if(_toc!=null)
|
||||
return _toc.Count;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the <c>arrToC</c> list to the one in this instance
|
||||
/// </summary>
|
||||
/// <param name="arrToC">the toc list which should be merged with the current one</param>
|
||||
internal void MergeToC( ArrayList arrToC )
|
||||
{
|
||||
if(_toc==null)
|
||||
_toc = new ArrayList();
|
||||
|
||||
MergeToC(_toc, arrToC, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the <c>arrToC</c> list to the one in this instance (called if merged files
|
||||
/// were found in a CHM)
|
||||
/// </summary>
|
||||
/// <param name="arrToC">the toc list which should be merged with the current one</param>
|
||||
/// <param name="openFiles">An arraylist of CHMFile instances.</param>
|
||||
internal void MergeToC( ArrayList arrToC, ArrayList openFiles )
|
||||
{
|
||||
if(_toc==null)
|
||||
_toc = new ArrayList();
|
||||
MergeToC(_toc, arrToC, openFiles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method for recursive toc merging
|
||||
/// </summary>
|
||||
/// <param name="globalLevel">level of global toc</param>
|
||||
/// <param name="localLevel">level of local toc</param>
|
||||
/// <param name="openFiles">An arraylist of CHMFile instances.</param>
|
||||
private void MergeToC( ArrayList globalLevel, ArrayList localLevel, ArrayList openFiles )
|
||||
{
|
||||
foreach( TOCItem curItem in localLevel)
|
||||
{
|
||||
// if it is a part of the merged-links, we have to do nothing,
|
||||
// because the method HtmlHelpSystem.RecalculateMergeLinks() has already
|
||||
// placed this item at its correct position.
|
||||
if(!IsMergedItem(curItem.Name, curItem.Local, openFiles))
|
||||
{
|
||||
TOCItem globalItem = ContainsToC(globalLevel, curItem.Name);
|
||||
if(globalItem == null)
|
||||
{
|
||||
// the global toc doesn't have a topic with this name
|
||||
// so we need to add the complete toc node to the global toc
|
||||
|
||||
globalLevel.Add( curItem );
|
||||
}
|
||||
else
|
||||
{
|
||||
// the global toc contains the current topic
|
||||
// advance to the next level
|
||||
|
||||
if( (globalItem.Local.Length <= 0) && (curItem.Local.Length > 0) )
|
||||
{
|
||||
// set the associated url
|
||||
globalItem.Local = curItem.Local;
|
||||
globalItem.ChmFile = curItem.ChmFile;
|
||||
}
|
||||
|
||||
MergeToC(globalItem.Children, curItem.Children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the item is part of the merged-links
|
||||
/// </summary>
|
||||
/// <param name="name">name of the topic</param>
|
||||
/// <param name="local">local of the topic</param>
|
||||
/// <param name="openFiles">An arraylist of CHMFile instances.</param>
|
||||
/// <returns>Returns true if this item is part of the merged-links</returns>
|
||||
private bool IsMergedItem(string name, string local, ArrayList openFiles)
|
||||
{
|
||||
if(openFiles==null)
|
||||
return false;
|
||||
|
||||
foreach(CHMFile curFile in openFiles)
|
||||
{
|
||||
foreach(TOCItem curItem in curFile.MergLinks)
|
||||
if( (curItem.Name == name) && (curItem.Local == local) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a topicname exists in a SINGLE toc level
|
||||
/// </summary>
|
||||
/// <param name="arrToC">toc list</param>
|
||||
/// <param name="Topic">topic to search</param>
|
||||
/// <returns>Returns the topic item if found, otherwise null</returns>
|
||||
private TOCItem ContainsToC(ArrayList arrToC, string Topic)
|
||||
{
|
||||
foreach(TOCItem curItem in arrToC)
|
||||
{
|
||||
if(curItem.Name == Topic)
|
||||
return curItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the table of contents for a special topic
|
||||
/// </summary>
|
||||
/// <param name="topic">topic to search</param>
|
||||
/// <returns>Returns an instance of TOCItem if found, otherwise null</returns>
|
||||
public TOCItem SearchTopic(string topic)
|
||||
{
|
||||
return SearchTopic(topic, _toc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal recursive tree search
|
||||
/// </summary>
|
||||
/// <param name="topic">topic to search</param>
|
||||
/// <param name="searchIn">tree level list to look in</param>
|
||||
/// <returns>Returns an instance of TOCItem if found, otherwise null</returns>
|
||||
private TOCItem SearchTopic(string topic, ArrayList searchIn)
|
||||
{
|
||||
foreach(TOCItem curItem in searchIn)
|
||||
{
|
||||
if(curItem.Name.ToLower() == topic.ToLower() )
|
||||
return curItem;
|
||||
|
||||
if(curItem.Children.Count>0)
|
||||
{
|
||||
TOCItem nf = SearchTopic(topic, curItem.Children);
|
||||
if(nf != null)
|
||||
return nf;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
irc/TechBot/Compression/AssemblyInfo.cs
Normal file
32
irc/TechBot/Compression/AssemblyInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
200
irc/TechBot/Compression/Checksums/Adler32.cs
Normal file
200
irc/TechBot/Compression/Checksums/Adler32.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
// Adler32.cs - Computes Adler32 data checksum of a data stream
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Checksums
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Computes Adler32 checksum for a stream of data. An Adler32
|
||||
/// checksum is not as reliable as a CRC32 checksum, but a lot faster to
|
||||
/// compute.
|
||||
///
|
||||
/// The specification for Adler32 may be found in RFC 1950.
|
||||
/// ZLIB Compressed Data Format Specification version 3.3)
|
||||
///
|
||||
///
|
||||
/// From that document:
|
||||
///
|
||||
/// "ADLER32 (Adler-32 checksum)
|
||||
/// This contains a checksum value of the uncompressed data
|
||||
/// (excluding any dictionary data) computed according to Adler-32
|
||||
/// algorithm. This algorithm is a 32-bit extension and improvement
|
||||
/// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
|
||||
/// standard.
|
||||
///
|
||||
/// Adler-32 is composed of two sums accumulated per byte: s1 is
|
||||
/// the sum of all bytes, s2 is the sum of all s1 values. Both sums
|
||||
/// are done modulo 65521. s1 is initialized to 1, s2 to zero. The
|
||||
/// Adler-32 checksum is stored as s2*65536 + s1 in most-
|
||||
/// significant-byte first (network) order."
|
||||
///
|
||||
/// "8.2. The Adler-32 algorithm
|
||||
///
|
||||
/// The Adler-32 algorithm is much faster than the CRC32 algorithm yet
|
||||
/// still provides an extremely low probability of undetected errors.
|
||||
///
|
||||
/// The modulo on unsigned long accumulators can be delayed for 5552
|
||||
/// bytes, so the modulo operation time is negligible. If the bytes
|
||||
/// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
|
||||
/// and order sensitive, unlike the first sum, which is just a
|
||||
/// checksum. That 65521 is prime is important to avoid a possible
|
||||
/// large class of two-byte errors that leave the check unchanged.
|
||||
/// (The Fletcher checksum uses 255, which is not prime and which also
|
||||
/// makes the Fletcher check insensitive to single byte changes 0 -
|
||||
/// 255.)
|
||||
///
|
||||
/// The sum s1 is initialized to 1 instead of zero to make the length
|
||||
/// of the sequence part of s2, so that the length does not have to be
|
||||
/// checked separately. (Any sequence of zeroes has a Fletcher
|
||||
/// checksum of zero.)"
|
||||
/// </summary>
|
||||
/// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream"/>
|
||||
/// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream"/>
|
||||
public sealed class Adler32 : IChecksum
|
||||
{
|
||||
/// <summary>
|
||||
/// largest prime smaller than 65536
|
||||
/// </summary>
|
||||
readonly static uint BASE = 65521;
|
||||
|
||||
uint checksum;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Adler32 data checksum computed so far.
|
||||
/// </summary>
|
||||
public long Value {
|
||||
get {
|
||||
return checksum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <code>Adler32</code> class.
|
||||
/// The checksum starts off with a value of 1.
|
||||
/// </summary>
|
||||
public Adler32()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Adler32 checksum to the initial value.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
checksum = 1; //Initialize to 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the checksum with the byte b.
|
||||
/// </summary>
|
||||
/// <param name="bval">
|
||||
/// the data value to add. The high byte of the int is ignored.
|
||||
/// </param>
|
||||
public void Update(int bval)
|
||||
{
|
||||
//We could make a length 1 byte array and call update again, but I
|
||||
//would rather not have that overhead
|
||||
uint s1 = checksum & 0xFFFF;
|
||||
uint s2 = checksum >> 16;
|
||||
|
||||
s1 = (s1 + ((uint)bval & 0xFF)) % BASE;
|
||||
s2 = (s1 + s2) % BASE;
|
||||
|
||||
checksum = (s2 << 16) + s1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the checksum with the bytes taken from the array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// buffer an array of bytes
|
||||
/// </param>
|
||||
public void Update(byte[] buffer)
|
||||
{
|
||||
Update(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the checksum with the bytes taken from the array.
|
||||
/// </summary>
|
||||
/// <param name="buf">
|
||||
/// an array of bytes
|
||||
/// </param>
|
||||
/// <param name="off">
|
||||
/// the start of the data used for this update
|
||||
/// </param>
|
||||
/// <param name="len">
|
||||
/// the number of bytes to use for this update
|
||||
/// </param>
|
||||
public void Update(byte[] buf, int off, int len)
|
||||
{
|
||||
if (buf == null) {
|
||||
throw new ArgumentNullException("buf");
|
||||
}
|
||||
|
||||
if (off < 0 || len < 0 || off + len > buf.Length) {
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
//(By Per Bothner)
|
||||
uint s1 = checksum & 0xFFFF;
|
||||
uint s2 = checksum >> 16;
|
||||
|
||||
while (len > 0) {
|
||||
// We can defer the modulo operation:
|
||||
// s1 maximally grows from 65521 to 65521 + 255 * 3800
|
||||
// s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
|
||||
int n = 3800;
|
||||
if (n > len) {
|
||||
n = len;
|
||||
}
|
||||
len -= n;
|
||||
while (--n >= 0) {
|
||||
s1 = s1 + (uint)(buf[off++] & 0xFF);
|
||||
s2 = s2 + s1;
|
||||
}
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
|
||||
checksum = (s2 << 16) | s1;
|
||||
}
|
||||
}
|
||||
}
|
||||
211
irc/TechBot/Compression/Checksums/CRC32.cs
Normal file
211
irc/TechBot/Compression/Checksums/CRC32.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
// CRC32.cs - Computes CRC32 data checksum of a data stream
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Checksums
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
|
||||
/// 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.
|
||||
///
|
||||
/// Polynomials over GF(2) are represented in binary, one bit per coefficient,
|
||||
/// with the lowest powers in the most significant bit. Then adding polynomials
|
||||
/// is just exclusive-or, and multiplying a polynomial by x is a right shift by
|
||||
/// one. If we call the above polynomial p, and represent a byte as the
|
||||
/// polynomial q, also with the lowest power in the most significant bit (so the
|
||||
/// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
|
||||
/// where a mod b means the remainder after dividing a by b.
|
||||
///
|
||||
/// This calculation is done using the shift-register method of multiplying and
|
||||
/// taking the remainder. The register is initialized to zero, and for each
|
||||
/// incoming bit, x^32 is added mod p to the register if the bit is a one (where
|
||||
/// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
|
||||
/// x (which is shifting right by one and adding x^32 mod p if the bit shifted
|
||||
/// out is a one). We start with the highest power (least significant bit) of
|
||||
/// q and repeat for all eight bits of q.
|
||||
///
|
||||
/// The table is simply the CRC of all possible eight bit values. This is all
|
||||
/// the information needed to generate CRC's on data a byte at a time for all
|
||||
/// combinations of CRC register values and incoming bytes.
|
||||
/// </summary>
|
||||
public sealed class Crc32 : IChecksum
|
||||
{
|
||||
readonly static uint CrcSeed = 0xFFFFFFFF;
|
||||
|
||||
readonly static uint[] CrcTable = new uint[] {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
|
||||
0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
|
||||
0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
|
||||
0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
|
||||
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
|
||||
0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
|
||||
0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
|
||||
0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
|
||||
0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
|
||||
0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
|
||||
0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
|
||||
0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
|
||||
0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
|
||||
0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
|
||||
0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
|
||||
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
|
||||
0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
|
||||
0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
|
||||
0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
|
||||
0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
|
||||
0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
|
||||
0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
|
||||
0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
|
||||
0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
|
||||
0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
|
||||
0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
|
||||
0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
|
||||
0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
|
||||
0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
|
||||
0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
|
||||
0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
|
||||
0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
|
||||
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
|
||||
0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
|
||||
0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
|
||||
0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
|
||||
0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
|
||||
0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
|
||||
0x2D02EF8D
|
||||
};
|
||||
|
||||
internal static uint ComputeCrc32(uint oldCrc, byte bval)
|
||||
{
|
||||
return (uint)(Crc32.CrcTable[(oldCrc ^ bval) & 0xFF] ^ (oldCrc >> 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The crc data checksum so far.
|
||||
/// </summary>
|
||||
uint crc = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 data checksum computed so far.
|
||||
/// </summary>
|
||||
public long Value {
|
||||
get {
|
||||
return (long)crc;
|
||||
}
|
||||
set {
|
||||
crc = (uint)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the CRC32 data checksum as if no update was ever called.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
crc = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the checksum with the int bval.
|
||||
/// </summary>
|
||||
/// <param name = "bval">
|
||||
/// the byte is taken as the lower 8 bits of bval
|
||||
/// </param>
|
||||
public void Update(int bval)
|
||||
{
|
||||
crc ^= CrcSeed;
|
||||
crc = CrcTable[(crc ^ bval) & 0xFF] ^ (crc >> 8);
|
||||
crc ^= CrcSeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the checksum with the bytes taken from the array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// buffer an array of bytes
|
||||
/// </param>
|
||||
public void Update(byte[] buffer)
|
||||
{
|
||||
Update(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the byte array to the data checksum.
|
||||
/// </summary>
|
||||
/// <param name = "buf">
|
||||
/// the buffer which contains the data
|
||||
/// </param>
|
||||
/// <param name = "off">
|
||||
/// the offset in the buffer where the data starts
|
||||
/// </param>
|
||||
/// <param name = "len">
|
||||
/// the length of the data
|
||||
/// </param>
|
||||
public void Update(byte[] buf, int off, int len)
|
||||
{
|
||||
if (buf == null) {
|
||||
throw new ArgumentNullException("buf");
|
||||
}
|
||||
|
||||
if (off < 0 || len < 0 || off + len > buf.Length) {
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
crc ^= CrcSeed;
|
||||
|
||||
while (--len >= 0) {
|
||||
crc = CrcTable[(crc ^ buf[off++]) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
crc ^= CrcSeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
93
irc/TechBot/Compression/Checksums/IChecksum.cs
Normal file
93
irc/TechBot/Compression/Checksums/IChecksum.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
// IChecksum.cs - Interface to compute a data checksum
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Checksums
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Interface to compute a data checksum used by checked input/output streams.
|
||||
/// A data checksum can be updated by one byte or with a byte array. After each
|
||||
/// update the value of the current checksum can be returned by calling
|
||||
/// <code>getValue</code>. The complete checksum object can also be reset
|
||||
/// so it can be used again with new data.
|
||||
/// </summary>
|
||||
public interface IChecksum
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the data checksum computed so far.
|
||||
/// </summary>
|
||||
long Value
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the data checksum as if no update was ever called.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Adds one byte to the data checksum.
|
||||
/// </summary>
|
||||
/// <param name = "bval">
|
||||
/// the data value to add. The high byte of the int is ignored.
|
||||
/// </param>
|
||||
void Update(int bval);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the data checksum with the bytes taken from the array.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// buffer an array of bytes
|
||||
/// </param>
|
||||
void Update(byte[] buffer);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the byte array to the data checksum.
|
||||
/// </summary>
|
||||
/// <param name = "buf">
|
||||
/// the buffer which contains the data
|
||||
/// </param>
|
||||
/// <param name = "off">
|
||||
/// the offset in the buffer where the data starts
|
||||
/// </param>
|
||||
/// <param name = "len">
|
||||
/// the length of the data
|
||||
/// </param>
|
||||
void Update(byte[] buf, int off, int len);
|
||||
}
|
||||
}
|
||||
159
irc/TechBot/Compression/Checksums/StrangeCRC.cs
Normal file
159
irc/TechBot/Compression/Checksums/StrangeCRC.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
// StrangeCRC.cs - computes a crc used in the bziplib ... I don't think that
|
||||
// this is the 'standard' crc, please correct me, if I'm wrong
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Checksums
|
||||
{
|
||||
|
||||
public class StrangeCRC : IChecksum
|
||||
{
|
||||
readonly static uint[] crc32Table = {
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
|
||||
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
|
||||
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||||
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
|
||||
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||||
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
|
||||
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
|
||||
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
|
||||
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
|
||||
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||||
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
|
||||
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||||
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
|
||||
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
|
||||
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||||
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
|
||||
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||||
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
|
||||
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
|
||||
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
|
||||
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
|
||||
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||||
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
|
||||
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||||
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
|
||||
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
|
||||
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||||
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
|
||||
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||||
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
|
||||
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
|
||||
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
|
||||
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
|
||||
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||||
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
|
||||
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||||
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
|
||||
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
|
||||
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||||
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
|
||||
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||||
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
|
||||
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
|
||||
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
|
||||
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
|
||||
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||||
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
|
||||
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||||
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
|
||||
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
|
||||
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||||
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
int globalCrc;
|
||||
|
||||
public StrangeCRC()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
globalCrc = -1;
|
||||
}
|
||||
|
||||
public long Value {
|
||||
get {
|
||||
return ~globalCrc;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(int inCh)
|
||||
{
|
||||
int temp = (globalCrc >> 24) ^ inCh;
|
||||
if (temp < 0) {
|
||||
temp = 256 + temp;
|
||||
}
|
||||
globalCrc = (int)((globalCrc << 8) ^ crc32Table[temp]);
|
||||
}
|
||||
|
||||
public void Update(byte[] buf)
|
||||
{
|
||||
Update(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
public void Update(byte[] buf, int off, int len)
|
||||
{
|
||||
if (buf == null) {
|
||||
throw new ArgumentNullException("buf");
|
||||
}
|
||||
|
||||
if (off < 0 || len < 0 || off + len > buf.Length) {
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
Update(buf[off++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
irc/TechBot/Compression/Compression.cmbx
Normal file
16
irc/TechBot/Compression/Compression.cmbx
Normal file
@@ -0,0 +1,16 @@
|
||||
<Combine fileversion="1.0" name="Compression" description="">
|
||||
<StartMode startupentry="Compression" single="True">
|
||||
<Execute entry="Compression" type="None" />
|
||||
</StartMode>
|
||||
<Entries>
|
||||
<Entry filename=".\.\Compression.prjx" />
|
||||
</Entries>
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release">
|
||||
<Entry name="Compression" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug">
|
||||
<Entry name="Compression" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Combine>
|
||||
45
irc/TechBot/Compression/Compression.prjx
Normal file
45
irc/TechBot/Compression/Compression.prjx
Normal file
@@ -0,0 +1,45 @@
|
||||
<Project name="Compression" standardNamespace="Compression" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
|
||||
<Contents>
|
||||
<File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\ZipException.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Deflater.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\DeflaterConstants.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\DeflaterEngine.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\DeflaterHuffman.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\DeflaterPending.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Inflater.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\InflaterDynHeader.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\InflaterHuffmanTree.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\PendingBuffer.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Checksums" subtype="Directory" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Checksums\StrangeCRC.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Checksums\Adler32.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Checksums\CRC32.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Checksums\IChecksum.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Streams" subtype="Directory" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Streams\StreamManipulator.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Streams\DeflaterOutputStream.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Streams\InflaterInputStream.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Streams\OutputWindow.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
</Contents>
|
||||
<References />
|
||||
<DeploymentInformation target="" script="" strategy="File" />
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="Compression" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configurations active="Debug">
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="Compression" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configuration runwithwarnings="True" name="Release">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Release" assembly="Compression" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Project>
|
||||
20
irc/TechBot/Compression/Default.build
Normal file
20
irc/TechBot/Compression/Default.build
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="Compression" default="build">
|
||||
|
||||
<property name="output.dir" value="..\bin" />
|
||||
|
||||
<target name="build" description="Build component">
|
||||
<mkdir dir="${output.dir}" />
|
||||
<csc target="library"
|
||||
output="${output.dir}\Compression.dll"
|
||||
optimize="true"
|
||||
debug="true"
|
||||
doc="${output.dir}\Compression.xml"
|
||||
warninglevel="0">
|
||||
<sources>
|
||||
<include name="**/*.cs" />
|
||||
</sources>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
542
irc/TechBot/Compression/Deflater.cs
Normal file
542
irc/TechBot/Compression/Deflater.cs
Normal file
@@ -0,0 +1,542 @@
|
||||
// Deflater.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This is the Deflater class. The deflater class compresses input
|
||||
/// with the deflate algorithm described in RFC 1951. It has several
|
||||
/// compression levels and three different strategies described below.
|
||||
///
|
||||
/// This class is <i>not</i> thread safe. This is inherent in the API, due
|
||||
/// to the split of deflate and setInput.
|
||||
///
|
||||
/// author of the original java version : Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class Deflater
|
||||
{
|
||||
/// <summary>
|
||||
/// The best and slowest compression level. This tries to find very
|
||||
/// long and distant string repetitions.
|
||||
/// </summary>
|
||||
public static int BEST_COMPRESSION = 9;
|
||||
|
||||
/// <summary>
|
||||
/// The worst but fastest compression level.
|
||||
/// </summary>
|
||||
public static int BEST_SPEED = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The default compression level.
|
||||
/// </summary>
|
||||
public static int DEFAULT_COMPRESSION = -1;
|
||||
|
||||
/// <summary>
|
||||
/// This level won't compress at all but output uncompressed blocks.
|
||||
/// </summary>
|
||||
public static int NO_COMPRESSION = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The compression method. This is the only method supported so far.
|
||||
/// There is no need to use this constant at all.
|
||||
/// </summary>
|
||||
public static int DEFLATED = 8;
|
||||
|
||||
/*
|
||||
* The Deflater can do the following state transitions:
|
||||
*
|
||||
* (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---.
|
||||
* / | (2) (5) |
|
||||
* / v (5) |
|
||||
* (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
|
||||
* \ | (3) | ,-------'
|
||||
* | | | (3) /
|
||||
* v v (5) v v
|
||||
* (1) -> BUSY_STATE ----> FINISHING_STATE
|
||||
* | (6)
|
||||
* v
|
||||
* FINISHED_STATE
|
||||
* \_____________________________________/
|
||||
* | (7)
|
||||
* v
|
||||
* CLOSED_STATE
|
||||
*
|
||||
* (1) If we should produce a header we start in INIT_STATE, otherwise
|
||||
* we start in BUSY_STATE.
|
||||
* (2) A dictionary may be set only when we are in INIT_STATE, then
|
||||
* we change the state as indicated.
|
||||
* (3) Whether a dictionary is set or not, on the first call of deflate
|
||||
* we change to BUSY_STATE.
|
||||
* (4) -- intentionally left blank -- :)
|
||||
* (5) FINISHING_STATE is entered, when flush() is called to indicate that
|
||||
* there is no more INPUT. There are also states indicating, that
|
||||
* the header wasn't written yet.
|
||||
* (6) FINISHED_STATE is entered, when everything has been flushed to the
|
||||
* internal pending output buffer.
|
||||
* (7) At any time (7)
|
||||
*
|
||||
*/
|
||||
|
||||
private static int IS_SETDICT = 0x01;
|
||||
private static int IS_FLUSHING = 0x04;
|
||||
private static int IS_FINISHING = 0x08;
|
||||
|
||||
private static int INIT_STATE = 0x00;
|
||||
private static int SETDICT_STATE = 0x01;
|
||||
// private static int INIT_FINISHING_STATE = 0x08;
|
||||
// private static int SETDICT_FINISHING_STATE = 0x09;
|
||||
private static int BUSY_STATE = 0x10;
|
||||
private static int FLUSHING_STATE = 0x14;
|
||||
private static int FINISHING_STATE = 0x1c;
|
||||
private static int FINISHED_STATE = 0x1e;
|
||||
private static int CLOSED_STATE = 0x7f;
|
||||
|
||||
/// <summary>
|
||||
/// Compression level.
|
||||
/// </summary>
|
||||
private int level;
|
||||
|
||||
/// <summary>
|
||||
/// should we include a header.
|
||||
/// </summary>
|
||||
private bool noHeader;
|
||||
|
||||
// /// <summary>
|
||||
// /// Compression strategy.
|
||||
// /// </summary>
|
||||
// private int strategy;
|
||||
|
||||
/// <summary>
|
||||
/// The current state.
|
||||
/// </summary>
|
||||
private int state;
|
||||
|
||||
/// <summary>
|
||||
/// The total bytes of output written.
|
||||
/// </summary>
|
||||
private int totalOut;
|
||||
|
||||
/// <summary>
|
||||
/// The pending output.
|
||||
/// </summary>
|
||||
private DeflaterPending pending;
|
||||
|
||||
/// <summary>
|
||||
/// The deflater engine.
|
||||
/// </summary>
|
||||
private DeflaterEngine engine;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new deflater with default compression level.
|
||||
/// </summary>
|
||||
public Deflater() : this(DEFAULT_COMPRESSION, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new deflater with given compression level.
|
||||
/// </summary>
|
||||
/// <param name="lvl">
|
||||
/// the compression level, a value between NO_COMPRESSION
|
||||
/// and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
|
||||
public Deflater(int lvl) : this(lvl, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new deflater with given compression level.
|
||||
/// </summary>
|
||||
/// <param name="lvl">
|
||||
/// the compression level, a value between NO_COMPRESSION
|
||||
/// and BEST_COMPRESSION.
|
||||
/// </param>
|
||||
/// <param name="nowrap">
|
||||
/// true, if we should suppress the deflate header at the
|
||||
/// beginning and the adler checksum at the end of the output. This is
|
||||
/// useful for the GZIP format.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
|
||||
public Deflater(int lvl, bool nowrap)
|
||||
{
|
||||
if (lvl == DEFAULT_COMPRESSION) {
|
||||
lvl = 6;
|
||||
} else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) {
|
||||
throw new ArgumentOutOfRangeException("lvl");
|
||||
}
|
||||
|
||||
pending = new DeflaterPending();
|
||||
engine = new DeflaterEngine(pending);
|
||||
this.noHeader = nowrap;
|
||||
SetStrategy(DeflateStrategy.Default);
|
||||
SetLevel(lvl);
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resets the deflater. The deflater acts afterwards as if it was
|
||||
/// just created with the same compression level and strategy as it
|
||||
/// had before.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
state = (noHeader ? BUSY_STATE : INIT_STATE);
|
||||
totalOut = 0;
|
||||
pending.Reset();
|
||||
engine.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current adler checksum of the data that was processed so far.
|
||||
/// </summary>
|
||||
public int Adler {
|
||||
get {
|
||||
return engine.Adler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of input bytes processed so far.
|
||||
/// </summary>
|
||||
public int TotalIn {
|
||||
get {
|
||||
return engine.TotalIn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of output bytes so far.
|
||||
/// </summary>
|
||||
public int TotalOut {
|
||||
get {
|
||||
return totalOut;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the current input block. Further calls to deflate() will
|
||||
/// produce enough output to inflate everything in the current input
|
||||
/// block. This is not part of Sun's JDK so I have made it package
|
||||
/// private. It is used by DeflaterOutputStream to implement
|
||||
/// flush().
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
state |= IS_FLUSHING;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the deflater with the current input block. It is an error
|
||||
/// to give more input after this method was called. This method must
|
||||
/// be called to force all bytes to be flushed.
|
||||
/// </summary>
|
||||
public void Finish()
|
||||
{
|
||||
state |= IS_FLUSHING | IS_FINISHING;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the stream was finished and no more output bytes
|
||||
/// are available.
|
||||
/// </summary>
|
||||
public bool IsFinished {
|
||||
get {
|
||||
return state == FINISHED_STATE && pending.IsFlushed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true, if the input buffer is empty.
|
||||
/// You should then call setInput().
|
||||
/// NOTE: This method can also return true when the stream
|
||||
/// was finished.
|
||||
/// </summary>
|
||||
public bool IsNeedingInput {
|
||||
get {
|
||||
return engine.NeedsInput();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the data which should be compressed next. This should be only
|
||||
/// called when needsInput indicates that more input is needed.
|
||||
/// If you call setInput when needsInput() returns false, the
|
||||
/// previous input that is still pending will be thrown away.
|
||||
/// The given byte array should not be changed, before needsInput() returns
|
||||
/// true again.
|
||||
/// This call is equivalent to <code>setInput(input, 0, input.length)</code>.
|
||||
/// </summary>
|
||||
/// <param name="input">
|
||||
/// the buffer containing the input data.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if the buffer was finished() or ended().
|
||||
/// </exception>
|
||||
public void SetInput(byte[] input)
|
||||
{
|
||||
SetInput(input, 0, input.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the data which should be compressed next. This should be
|
||||
/// only called when needsInput indicates that more input is needed.
|
||||
/// The given byte array should not be changed, before needsInput() returns
|
||||
/// true again.
|
||||
/// </summary>
|
||||
/// <param name="input">
|
||||
/// the buffer containing the input data.
|
||||
/// </param>
|
||||
/// <param name="off">
|
||||
/// the start of the data.
|
||||
/// </param>
|
||||
/// <param name="len">
|
||||
/// the length of the data.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if the buffer was finished() or ended() or if previous input is still pending.
|
||||
/// </exception>
|
||||
public void SetInput(byte[] input, int off, int len)
|
||||
{
|
||||
if ((state & IS_FINISHING) != 0) {
|
||||
throw new InvalidOperationException("finish()/end() already called");
|
||||
}
|
||||
engine.SetInput(input, off, len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the compression level. There is no guarantee of the exact
|
||||
/// position of the change, but if you call this when needsInput is
|
||||
/// true the change of compression level will occur somewhere near
|
||||
/// before the end of the so far given input.
|
||||
/// </summary>
|
||||
/// <param name="lvl">
|
||||
/// the new compression level.
|
||||
/// </param>
|
||||
public void SetLevel(int lvl)
|
||||
{
|
||||
if (lvl == DEFAULT_COMPRESSION) {
|
||||
lvl = 6;
|
||||
} else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) {
|
||||
throw new ArgumentOutOfRangeException("lvl");
|
||||
}
|
||||
|
||||
if (level != lvl) {
|
||||
level = lvl;
|
||||
engine.SetLevel(lvl);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the compression strategy. Strategy is one of
|
||||
/// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
|
||||
/// position where the strategy is changed, the same as for
|
||||
/// setLevel() applies.
|
||||
/// </summary>
|
||||
/// <param name="stgy">
|
||||
/// the new compression strategy.
|
||||
/// </param>
|
||||
public void SetStrategy(DeflateStrategy stgy)
|
||||
{
|
||||
engine.Strategy = stgy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deflates the current input block to the given array. It returns
|
||||
/// the number of bytes compressed, or 0 if either
|
||||
/// needsInput() or finished() returns true or length is zero.
|
||||
/// </summary>
|
||||
/// <param name="output">
|
||||
/// the buffer where to write the compressed data.
|
||||
/// </param>
|
||||
public int Deflate(byte[] output)
|
||||
{
|
||||
return Deflate(output, 0, output.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deflates the current input block to the given array. It returns
|
||||
/// the number of bytes compressed, or 0 if either
|
||||
/// needsInput() or finished() returns true or length is zero.
|
||||
/// </summary>
|
||||
/// <param name="output">
|
||||
/// the buffer where to write the compressed data.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// the offset into the output array.
|
||||
/// </param>
|
||||
/// <param name="length">
|
||||
/// the maximum number of bytes that may be written.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if end() was called.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// if offset and/or length don't match the array length.
|
||||
/// </exception>
|
||||
public int Deflate(byte[] output, int offset, int length)
|
||||
{
|
||||
int origLength = length;
|
||||
|
||||
if (state == CLOSED_STATE) {
|
||||
throw new InvalidOperationException("Deflater closed");
|
||||
}
|
||||
|
||||
if (state < BUSY_STATE) {
|
||||
/* output header */
|
||||
int header = (DEFLATED +
|
||||
((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
|
||||
int level_flags = (level - 1) >> 1;
|
||||
if (level_flags < 0 || level_flags > 3) {
|
||||
level_flags = 3;
|
||||
}
|
||||
header |= level_flags << 6;
|
||||
if ((state & IS_SETDICT) != 0) {
|
||||
/* Dictionary was set */
|
||||
header |= DeflaterConstants.PRESET_DICT;
|
||||
}
|
||||
header += 31 - (header % 31);
|
||||
|
||||
|
||||
pending.WriteShortMSB(header);
|
||||
if ((state & IS_SETDICT) != 0) {
|
||||
int chksum = engine.Adler;
|
||||
engine.ResetAdler();
|
||||
pending.WriteShortMSB(chksum >> 16);
|
||||
pending.WriteShortMSB(chksum & 0xffff);
|
||||
}
|
||||
|
||||
state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int count = pending.Flush(output, offset, length);
|
||||
offset += count;
|
||||
totalOut += count;
|
||||
length -= count;
|
||||
|
||||
if (length == 0 || state == FINISHED_STATE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) {
|
||||
if (state == BUSY_STATE) {
|
||||
/* We need more input now */
|
||||
return origLength - length;
|
||||
} else if (state == FLUSHING_STATE) {
|
||||
if (level != NO_COMPRESSION) {
|
||||
/* We have to supply some lookahead. 8 bit lookahead
|
||||
* are needed by the zlib inflater, and we must fill
|
||||
* the next byte, so that all bits are flushed.
|
||||
*/
|
||||
int neededbits = 8 + ((-pending.BitCount) & 7);
|
||||
while (neededbits > 0) {
|
||||
/* write a static tree block consisting solely of
|
||||
* an EOF:
|
||||
*/
|
||||
pending.WriteBits(2, 10);
|
||||
neededbits -= 10;
|
||||
}
|
||||
}
|
||||
state = BUSY_STATE;
|
||||
} else if (state == FINISHING_STATE) {
|
||||
pending.AlignToByte();
|
||||
/* We have completed the stream */
|
||||
if (!noHeader) {
|
||||
int adler = engine.Adler;
|
||||
pending.WriteShortMSB(adler >> 16);
|
||||
pending.WriteShortMSB(adler & 0xffff);
|
||||
}
|
||||
state = FINISHED_STATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return origLength - length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the dictionary which should be used in the deflate process.
|
||||
/// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>.
|
||||
/// </summary>
|
||||
/// <param name="dict">
|
||||
/// the dictionary.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if setInput () or deflate () were already called or another dictionary was already set.
|
||||
/// </exception>
|
||||
public void SetDictionary(byte[] dict)
|
||||
{
|
||||
SetDictionary(dict, 0, dict.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the dictionary which should be used in the deflate process.
|
||||
/// The dictionary should be a byte array containing strings that are
|
||||
/// likely to occur in the data which should be compressed. The
|
||||
/// dictionary is not stored in the compressed output, only a
|
||||
/// checksum. To decompress the output you need to supply the same
|
||||
/// dictionary again.
|
||||
/// </summary>
|
||||
/// <param name="dict">
|
||||
/// the dictionary.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// an offset into the dictionary.
|
||||
/// </param>
|
||||
/// <param name="length">
|
||||
/// the length of the dictionary.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if setInput () or deflate () were already called or another dictionary was already set.
|
||||
/// </exception>
|
||||
public void SetDictionary(byte[] dict, int offset, int length)
|
||||
{
|
||||
if (state != INIT_STATE) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
state = SETDICT_STATE;
|
||||
engine.SetDictionary(dict, offset, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
irc/TechBot/Compression/DeflaterConstants.cs
Normal file
85
irc/TechBot/Compression/DeflaterConstants.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
// DeflaterConstants.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class contains constants used for the deflater.
|
||||
/// </summary>
|
||||
public class DeflaterConstants
|
||||
{
|
||||
public const bool DEBUGGING = false;
|
||||
|
||||
public const int STORED_BLOCK = 0;
|
||||
public const int STATIC_TREES = 1;
|
||||
public const int DYN_TREES = 2;
|
||||
public const int PRESET_DICT = 0x20;
|
||||
|
||||
public const int DEFAULT_MEM_LEVEL = 8;
|
||||
|
||||
public const int MAX_MATCH = 258;
|
||||
public const int MIN_MATCH = 3;
|
||||
|
||||
public const int MAX_WBITS = 15;
|
||||
public const int WSIZE = 1 << MAX_WBITS;
|
||||
public const int WMASK = WSIZE - 1;
|
||||
|
||||
public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
|
||||
public const int HASH_SIZE = 1 << HASH_BITS;
|
||||
public const int HASH_MASK = HASH_SIZE - 1;
|
||||
public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
|
||||
|
||||
public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
|
||||
public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
|
||||
|
||||
public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
|
||||
public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE-5);
|
||||
|
||||
public const int DEFLATE_STORED = 0;
|
||||
public const int DEFLATE_FAST = 1;
|
||||
public const int DEFLATE_SLOW = 2;
|
||||
|
||||
public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 };
|
||||
public static int[] MAX_LAZY = { 0, 4, 5, 6, 4,16, 16, 32, 128, 258 };
|
||||
public static int[] NICE_LENGTH = { 0, 8,16,32,16,32,128,128, 258, 258 };
|
||||
public static int[] MAX_CHAIN = { 0, 4, 8,32,16,32,128,256,1024,4096 };
|
||||
public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 };
|
||||
}
|
||||
}
|
||||
653
irc/TechBot/Compression/DeflaterEngine.cs
Normal file
653
irc/TechBot/Compression/DeflaterEngine.cs
Normal file
@@ -0,0 +1,653 @@
|
||||
// DeflaterEngine.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
using ICSharpCode.SharpZipLib.Checksums;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
public enum DeflateStrategy
|
||||
{
|
||||
// The default strategy.
|
||||
Default = 0,
|
||||
|
||||
// This strategy will only allow longer string repetitions. It is
|
||||
// useful for random data with a small character set.
|
||||
Filtered = 1,
|
||||
|
||||
// This strategy will not look for string repetitions at all. It
|
||||
// only encodes with Huffman trees (which means, that more common
|
||||
// characters get a smaller encoding.
|
||||
HuffmanOnly = 2
|
||||
}
|
||||
|
||||
public class DeflaterEngine : DeflaterConstants
|
||||
{
|
||||
static int TOO_FAR = 4096;
|
||||
|
||||
int ins_h;
|
||||
// private byte[] buffer;
|
||||
short[] head;
|
||||
short[] prev;
|
||||
|
||||
int matchStart, matchLen;
|
||||
bool prevAvailable;
|
||||
int blockStart;
|
||||
int strstart, lookahead;
|
||||
byte[] window;
|
||||
|
||||
DeflateStrategy strategy;
|
||||
int max_chain, max_lazy, niceLength, goodLength;
|
||||
|
||||
/// <summary>
|
||||
/// The current compression function.
|
||||
/// </summary>
|
||||
int comprFunc;
|
||||
|
||||
/// <summary>
|
||||
/// The input data for compression.
|
||||
/// </summary>
|
||||
byte[] inputBuf;
|
||||
|
||||
/// <summary>
|
||||
/// The total bytes of input read.
|
||||
/// </summary>
|
||||
int totalIn;
|
||||
|
||||
/// <summary>
|
||||
/// The offset into inputBuf, where input data starts.
|
||||
/// </summary>
|
||||
int inputOff;
|
||||
|
||||
/// <summary>
|
||||
/// The end offset of the input data.
|
||||
/// </summary>
|
||||
int inputEnd;
|
||||
|
||||
DeflaterPending pending;
|
||||
DeflaterHuffman huffman;
|
||||
|
||||
/// <summary>
|
||||
/// The adler checksum
|
||||
/// </summary>
|
||||
Adler32 adler;
|
||||
|
||||
public DeflaterEngine(DeflaterPending pending)
|
||||
{
|
||||
this.pending = pending;
|
||||
huffman = new DeflaterHuffman(pending);
|
||||
adler = new Adler32();
|
||||
|
||||
window = new byte[2 * WSIZE];
|
||||
head = new short[HASH_SIZE];
|
||||
prev = new short[WSIZE];
|
||||
|
||||
/* We start at index 1, to avoid a implementation deficiency, that
|
||||
* we cannot build a repeat pattern at index 0.
|
||||
*/
|
||||
blockStart = strstart = 1;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
huffman.Reset();
|
||||
adler.Reset();
|
||||
blockStart = strstart = 1;
|
||||
lookahead = 0;
|
||||
totalIn = 0;
|
||||
prevAvailable = false;
|
||||
matchLen = MIN_MATCH - 1;
|
||||
|
||||
for (int i = 0; i < HASH_SIZE; i++) {
|
||||
head[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < WSIZE; i++) {
|
||||
prev[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetAdler()
|
||||
{
|
||||
adler.Reset();
|
||||
}
|
||||
|
||||
public int Adler {
|
||||
get {
|
||||
return (int)adler.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public int TotalIn {
|
||||
get {
|
||||
return totalIn;
|
||||
}
|
||||
}
|
||||
|
||||
public DeflateStrategy Strategy {
|
||||
get {
|
||||
return strategy;
|
||||
}
|
||||
set {
|
||||
strategy = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLevel(int lvl)
|
||||
{
|
||||
goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
|
||||
max_lazy = DeflaterConstants.MAX_LAZY[lvl];
|
||||
niceLength = DeflaterConstants.NICE_LENGTH[lvl];
|
||||
max_chain = DeflaterConstants.MAX_CHAIN[lvl];
|
||||
|
||||
if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) {
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("Change from "+comprFunc +" to "
|
||||
// + DeflaterConstants.COMPR_FUNC[lvl]);
|
||||
// }
|
||||
switch (comprFunc) {
|
||||
case DEFLATE_STORED:
|
||||
if (strstart > blockStart) {
|
||||
huffman.FlushStoredBlock(window, blockStart,
|
||||
strstart - blockStart, false);
|
||||
blockStart = strstart;
|
||||
}
|
||||
UpdateHash();
|
||||
break;
|
||||
case DEFLATE_FAST:
|
||||
if (strstart > blockStart) {
|
||||
huffman.FlushBlock(window, blockStart, strstart - blockStart,
|
||||
false);
|
||||
blockStart = strstart;
|
||||
}
|
||||
break;
|
||||
case DEFLATE_SLOW:
|
||||
if (prevAvailable) {
|
||||
huffman.TallyLit(window[strstart-1] & 0xff);
|
||||
}
|
||||
if (strstart > blockStart) {
|
||||
huffman.FlushBlock(window, blockStart, strstart - blockStart, false);
|
||||
blockStart = strstart;
|
||||
}
|
||||
prevAvailable = false;
|
||||
matchLen = MIN_MATCH - 1;
|
||||
break;
|
||||
}
|
||||
comprFunc = COMPR_FUNC[lvl];
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateHash()
|
||||
{
|
||||
// if (DEBUGGING) {
|
||||
// //Console.WriteLine("updateHash: "+strstart);
|
||||
// }
|
||||
ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
|
||||
}
|
||||
|
||||
int InsertString()
|
||||
{
|
||||
short match;
|
||||
int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) & HASH_MASK;
|
||||
|
||||
// if (DEBUGGING) {
|
||||
// if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^
|
||||
// (window[strstart + 1] << HASH_SHIFT) ^
|
||||
// (window[strstart + 2])) & HASH_MASK)) {
|
||||
// throw new Exception("hash inconsistent: "+hash+"/"
|
||||
// +window[strstart]+","
|
||||
// +window[strstart+1]+","
|
||||
// +window[strstart+2]+","+HASH_SHIFT);
|
||||
// }
|
||||
// }
|
||||
|
||||
prev[strstart & WMASK] = match = head[hash];
|
||||
head[hash] = (short)strstart;
|
||||
ins_h = hash;
|
||||
return match & 0xffff;
|
||||
}
|
||||
|
||||
void SlideWindow()
|
||||
{
|
||||
Array.Copy(window, WSIZE, window, 0, WSIZE);
|
||||
matchStart -= WSIZE;
|
||||
strstart -= WSIZE;
|
||||
blockStart -= WSIZE;
|
||||
|
||||
/* Slide the hash table (could be avoided with 32 bit values
|
||||
* at the expense of memory usage).
|
||||
*/
|
||||
for (int i = 0; i < HASH_SIZE; ++i) {
|
||||
int m = head[i] & 0xffff;
|
||||
head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);
|
||||
}
|
||||
|
||||
/* Slide the prev table. */
|
||||
for (int i = 0; i < WSIZE; i++) {
|
||||
int m = prev[i] & 0xffff;
|
||||
prev[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void FillWindow()
|
||||
{
|
||||
/* If the window is almost full and there is insufficient lookahead,
|
||||
* move the upper half to the lower one to make room in the upper half.
|
||||
*/
|
||||
if (strstart >= WSIZE + MAX_DIST) {
|
||||
SlideWindow();
|
||||
}
|
||||
|
||||
/* If there is not enough lookahead, but still some input left,
|
||||
* read in the input
|
||||
*/
|
||||
while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) {
|
||||
int more = 2 * WSIZE - lookahead - strstart;
|
||||
|
||||
if (more > inputEnd - inputOff) {
|
||||
more = inputEnd - inputOff;
|
||||
}
|
||||
|
||||
System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more);
|
||||
adler.Update(inputBuf, inputOff, more);
|
||||
|
||||
inputOff += more;
|
||||
totalIn += more;
|
||||
lookahead += more;
|
||||
}
|
||||
|
||||
if (lookahead >= MIN_MATCH) {
|
||||
UpdateHash();
|
||||
}
|
||||
}
|
||||
|
||||
bool FindLongestMatch(int curMatch)
|
||||
{
|
||||
int chainLength = this.max_chain;
|
||||
int niceLength = this.niceLength;
|
||||
short[] prev = this.prev;
|
||||
int scan = this.strstart;
|
||||
int match;
|
||||
int best_end = this.strstart + matchLen;
|
||||
int best_len = Math.Max(matchLen, MIN_MATCH - 1);
|
||||
|
||||
int limit = Math.Max(strstart - MAX_DIST, 0);
|
||||
|
||||
int strend = strstart + MAX_MATCH - 1;
|
||||
byte scan_end1 = window[best_end - 1];
|
||||
byte scan_end = window[best_end];
|
||||
|
||||
/* Do not waste too much time if we already have a good match: */
|
||||
if (best_len >= this.goodLength) {
|
||||
chainLength >>= 2;
|
||||
}
|
||||
|
||||
/* Do not look for matches beyond the end of the input. This is necessary
|
||||
* to make deflate deterministic.
|
||||
*/
|
||||
if (niceLength > lookahead) {
|
||||
niceLength = lookahead;
|
||||
}
|
||||
|
||||
if (DeflaterConstants.DEBUGGING && strstart > 2 * WSIZE - MIN_LOOKAHEAD) {
|
||||
throw new InvalidOperationException("need lookahead");
|
||||
}
|
||||
|
||||
do {
|
||||
if (DeflaterConstants.DEBUGGING && curMatch >= strstart) {
|
||||
throw new InvalidOperationException("future match");
|
||||
}
|
||||
if (window[curMatch + best_len] != scan_end ||
|
||||
window[curMatch + best_len - 1] != scan_end1 ||
|
||||
window[curMatch] != window[scan] ||
|
||||
window[curMatch + 1] != window[scan + 1]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match = curMatch + 2;
|
||||
scan += 2;
|
||||
|
||||
/* We check for insufficient lookahead only every 8th comparison;
|
||||
* the 256th check will be made at strstart+258.
|
||||
*/
|
||||
while (window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] &&
|
||||
window[++scan] == window[++match] && scan < strend) ;
|
||||
|
||||
if (scan > best_end) {
|
||||
// if (DeflaterConstants.DEBUGGING && ins_h == 0)
|
||||
// System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
|
||||
matchStart = curMatch;
|
||||
best_end = scan;
|
||||
best_len = scan - strstart;
|
||||
|
||||
if (best_len >= niceLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
scan_end1 = window[best_end - 1];
|
||||
scan_end = window[best_end];
|
||||
}
|
||||
scan = strstart;
|
||||
} while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit && --chainLength != 0);
|
||||
|
||||
matchLen = Math.Min(best_len, lookahead);
|
||||
return matchLen >= MIN_MATCH;
|
||||
}
|
||||
|
||||
public void SetDictionary(byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && strstart != 1) {
|
||||
throw new InvalidOperationException("strstart not 1");
|
||||
}
|
||||
adler.Update(buffer, offset, length);
|
||||
if (length < MIN_MATCH) {
|
||||
return;
|
||||
}
|
||||
if (length > MAX_DIST) {
|
||||
offset += length - MAX_DIST;
|
||||
length = MAX_DIST;
|
||||
}
|
||||
|
||||
System.Array.Copy(buffer, offset, window, strstart, length);
|
||||
|
||||
UpdateHash();
|
||||
--length;
|
||||
while (--length > 0) {
|
||||
InsertString();
|
||||
strstart++;
|
||||
}
|
||||
strstart += 2;
|
||||
blockStart = strstart;
|
||||
}
|
||||
|
||||
bool DeflateStored(bool flush, bool finish)
|
||||
{
|
||||
if (!flush && lookahead == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strstart += lookahead;
|
||||
lookahead = 0;
|
||||
|
||||
int storedLen = strstart - blockStart;
|
||||
|
||||
if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) || /* Block is full */
|
||||
(blockStart < WSIZE && storedLen >= MAX_DIST) || /* Block may move out of window */
|
||||
flush) {
|
||||
bool lastBlock = finish;
|
||||
if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) {
|
||||
storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
|
||||
lastBlock = false;
|
||||
}
|
||||
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("storedBlock["+storedLen+","+lastBlock+"]");
|
||||
// }
|
||||
|
||||
huffman.FlushStoredBlock(window, blockStart, storedLen, lastBlock);
|
||||
blockStart += storedLen;
|
||||
return !lastBlock;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool DeflateFast(bool flush, bool finish)
|
||||
{
|
||||
if (lookahead < MIN_LOOKAHEAD && !flush) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (lookahead >= MIN_LOOKAHEAD || flush) {
|
||||
if (lookahead == 0) {
|
||||
/* We are flushing everything */
|
||||
huffman.FlushBlock(window, blockStart, strstart - blockStart, finish);
|
||||
blockStart = strstart;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) {
|
||||
/* slide window, as findLongestMatch need this.
|
||||
* This should only happen when flushing and the window
|
||||
* is almost full.
|
||||
*/
|
||||
SlideWindow();
|
||||
}
|
||||
|
||||
int hashHead;
|
||||
if (lookahead >= MIN_MATCH &&
|
||||
(hashHead = InsertString()) != 0 &&
|
||||
strategy != DeflateStrategy.HuffmanOnly &&
|
||||
strstart - hashHead <= MAX_DIST &&
|
||||
FindLongestMatch(hashHead)) {
|
||||
/* longestMatch sets matchStart and matchLen */
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// for (int i = 0 ; i < matchLen; i++) {
|
||||
// if (window[strstart+i] != window[matchStart + i]) {
|
||||
// throw new Exception();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// -jr- Hak hak hak this stops problems with fast/low compression and index out of range
|
||||
if (huffman.TallyDist(strstart - matchStart, matchLen)) {
|
||||
bool lastBlock = finish && lookahead == 0;
|
||||
huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);
|
||||
blockStart = strstart;
|
||||
}
|
||||
|
||||
lookahead -= matchLen;
|
||||
if (matchLen <= max_lazy && lookahead >= MIN_MATCH) {
|
||||
while (--matchLen > 0) {
|
||||
++strstart;
|
||||
InsertString();
|
||||
}
|
||||
++strstart;
|
||||
} else {
|
||||
strstart += matchLen;
|
||||
if (lookahead >= MIN_MATCH - 1) {
|
||||
UpdateHash();
|
||||
}
|
||||
}
|
||||
matchLen = MIN_MATCH - 1;
|
||||
continue;
|
||||
} else {
|
||||
/* No match found */
|
||||
huffman.TallyLit(window[strstart] & 0xff);
|
||||
++strstart;
|
||||
--lookahead;
|
||||
}
|
||||
|
||||
if (huffman.IsFull()) {
|
||||
bool lastBlock = finish && lookahead == 0;
|
||||
huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock);
|
||||
blockStart = strstart;
|
||||
return !lastBlock;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeflateSlow(bool flush, bool finish)
|
||||
{
|
||||
if (lookahead < MIN_LOOKAHEAD && !flush) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (lookahead >= MIN_LOOKAHEAD || flush) {
|
||||
if (lookahead == 0) {
|
||||
if (prevAvailable) {
|
||||
huffman.TallyLit(window[strstart-1] & 0xff);
|
||||
}
|
||||
prevAvailable = false;
|
||||
|
||||
/* We are flushing everything */
|
||||
if (DeflaterConstants.DEBUGGING && !flush) {
|
||||
throw new Exception("Not flushing, but no lookahead");
|
||||
}
|
||||
huffman.FlushBlock(window, blockStart, strstart - blockStart,
|
||||
finish);
|
||||
blockStart = strstart;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) {
|
||||
/* slide window, as findLongestMatch need this.
|
||||
* This should only happen when flushing and the window
|
||||
* is almost full.
|
||||
*/
|
||||
SlideWindow();
|
||||
}
|
||||
|
||||
int prevMatch = matchStart;
|
||||
int prevLen = matchLen;
|
||||
if (lookahead >= MIN_MATCH) {
|
||||
int hashHead = InsertString();
|
||||
if (strategy != DeflateStrategy.HuffmanOnly && hashHead != 0 && strstart - hashHead <= MAX_DIST && FindLongestMatch(hashHead)) {
|
||||
/* longestMatch sets matchStart and matchLen */
|
||||
|
||||
/* Discard match if too small and too far away */
|
||||
if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == MIN_MATCH && strstart - matchStart > TOO_FAR))) {
|
||||
matchLen = MIN_MATCH - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* previous match was better */
|
||||
if (prevLen >= MIN_MATCH && matchLen <= prevLen) {
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// for (int i = 0 ; i < matchLen; i++) {
|
||||
// if (window[strstart-1+i] != window[prevMatch + i])
|
||||
// throw new Exception();
|
||||
// }
|
||||
// }
|
||||
huffman.TallyDist(strstart - 1 - prevMatch, prevLen);
|
||||
prevLen -= 2;
|
||||
do {
|
||||
strstart++;
|
||||
lookahead--;
|
||||
if (lookahead >= MIN_MATCH) {
|
||||
InsertString();
|
||||
}
|
||||
} while (--prevLen > 0);
|
||||
strstart ++;
|
||||
lookahead--;
|
||||
prevAvailable = false;
|
||||
matchLen = MIN_MATCH - 1;
|
||||
} else {
|
||||
if (prevAvailable) {
|
||||
huffman.TallyLit(window[strstart-1] & 0xff);
|
||||
}
|
||||
prevAvailable = true;
|
||||
strstart++;
|
||||
lookahead--;
|
||||
}
|
||||
|
||||
if (huffman.IsFull()) {
|
||||
int len = strstart - blockStart;
|
||||
if (prevAvailable) {
|
||||
len--;
|
||||
}
|
||||
bool lastBlock = (finish && lookahead == 0 && !prevAvailable);
|
||||
huffman.FlushBlock(window, blockStart, len, lastBlock);
|
||||
blockStart += len;
|
||||
return !lastBlock;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deflate(bool flush, bool finish)
|
||||
{
|
||||
bool progress;
|
||||
do {
|
||||
FillWindow();
|
||||
bool canFlush = flush && inputOff == inputEnd;
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("window: ["+blockStart+","+strstart+","
|
||||
// +lookahead+"], "+comprFunc+","+canFlush);
|
||||
// }
|
||||
switch (comprFunc) {
|
||||
case DEFLATE_STORED:
|
||||
progress = DeflateStored(canFlush, finish);
|
||||
break;
|
||||
case DEFLATE_FAST:
|
||||
progress = DeflateFast(canFlush, finish);
|
||||
break;
|
||||
case DEFLATE_SLOW:
|
||||
progress = DeflateSlow(canFlush, finish);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("unknown comprFunc");
|
||||
}
|
||||
} while (pending.IsFlushed && progress); /* repeat while we have no pending output and progress was made */
|
||||
return progress;
|
||||
}
|
||||
|
||||
public void SetInput(byte[] buf, int off, int len)
|
||||
{
|
||||
if (inputOff < inputEnd) {
|
||||
throw new InvalidOperationException("Old input was not completely processed");
|
||||
}
|
||||
|
||||
int end = off + len;
|
||||
|
||||
/* We want to throw an ArrayIndexOutOfBoundsException early. The
|
||||
* check is very tricky: it also handles integer wrap around.
|
||||
*/
|
||||
if (0 > off || off > end || end > buf.Length) {
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
inputBuf = buf;
|
||||
inputOff = off;
|
||||
inputEnd = end;
|
||||
}
|
||||
|
||||
public bool NeedsInput()
|
||||
{
|
||||
return inputEnd == inputOff;
|
||||
}
|
||||
}
|
||||
}
|
||||
780
irc/TechBot/Compression/DeflaterHuffman.cs
Normal file
780
irc/TechBot/Compression/DeflaterHuffman.cs
Normal file
@@ -0,0 +1,780 @@
|
||||
// DeflaterHuffman.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This is the DeflaterHuffman class.
|
||||
///
|
||||
/// This class is <i>not</i> thread safe. This is inherent in the API, due
|
||||
/// to the split of deflate and setInput.
|
||||
///
|
||||
/// author of the original java version : Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class DeflaterHuffman
|
||||
{
|
||||
private static int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
|
||||
private static int LITERAL_NUM = 286;
|
||||
private static int DIST_NUM = 30;
|
||||
private static int BITLEN_NUM = 19;
|
||||
private static int REP_3_6 = 16;
|
||||
private static int REP_3_10 = 17;
|
||||
private static int REP_11_138 = 18;
|
||||
private static int EOF_SYMBOL = 256;
|
||||
private static int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
|
||||
private static byte[] bit4Reverse = {
|
||||
0,
|
||||
8,
|
||||
4,
|
||||
12,
|
||||
2,
|
||||
10,
|
||||
6,
|
||||
14,
|
||||
1,
|
||||
9,
|
||||
5,
|
||||
13,
|
||||
3,
|
||||
11,
|
||||
7,
|
||||
15
|
||||
};
|
||||
|
||||
public class Tree
|
||||
{
|
||||
public short[] freqs;
|
||||
public byte[] length;
|
||||
public int minNumCodes, numCodes;
|
||||
|
||||
short[] codes;
|
||||
int[] bl_counts;
|
||||
int maxLength;
|
||||
DeflaterHuffman dh;
|
||||
|
||||
public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength)
|
||||
{
|
||||
this.dh = dh;
|
||||
this.minNumCodes = minCodes;
|
||||
this.maxLength = maxLength;
|
||||
freqs = new short[elems];
|
||||
bl_counts = new int[maxLength];
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
for (int i = 0; i < freqs.Length; i++) {
|
||||
freqs[i] = 0;
|
||||
}
|
||||
codes = null;
|
||||
length = null;
|
||||
}
|
||||
|
||||
public void WriteSymbol(int code)
|
||||
{
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// freqs[code]--;
|
||||
// // Console.Write("writeSymbol("+freqs.length+","+code+"): ");
|
||||
// }
|
||||
dh.pending.WriteBits(codes[code] & 0xffff, length[code]);
|
||||
}
|
||||
|
||||
public void CheckEmpty()
|
||||
{
|
||||
bool empty = true;
|
||||
for (int i = 0; i < freqs.Length; i++) {
|
||||
if (freqs[i] != 0) {
|
||||
//Console.WriteLine("freqs["+i+"] == "+freqs[i]);
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
if (!empty) {
|
||||
throw new Exception();
|
||||
}
|
||||
//Console.WriteLine("checkEmpty suceeded!");
|
||||
}
|
||||
|
||||
public void SetStaticCodes(short[] stCodes, byte[] stLength)
|
||||
{
|
||||
codes = stCodes;
|
||||
length = stLength;
|
||||
}
|
||||
|
||||
public void BuildCodes()
|
||||
{
|
||||
int numSymbols = freqs.Length;
|
||||
int[] nextCode = new int[maxLength];
|
||||
int code = 0;
|
||||
codes = new short[freqs.Length];
|
||||
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("buildCodes: "+freqs.Length);
|
||||
// }
|
||||
|
||||
for (int bits = 0; bits < maxLength; bits++) {
|
||||
nextCode[bits] = code;
|
||||
code += bl_counts[bits] << (15 - bits);
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("bits: "+(bits+1)+" count: "+bl_counts[bits]
|
||||
// +" nextCode: "+code); // HACK : Integer.toHexString(
|
||||
// }
|
||||
}
|
||||
if (DeflaterConstants.DEBUGGING && code != 65536) {
|
||||
throw new Exception("Inconsistent bl_counts!");
|
||||
}
|
||||
|
||||
for (int i=0; i < numCodes; i++) {
|
||||
int bits = length[i];
|
||||
if (bits > 0) {
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+")," // HACK : Integer.toHexString(
|
||||
// +bits);
|
||||
// }
|
||||
codes[i] = BitReverse(nextCode[bits-1]);
|
||||
nextCode[bits-1] += 1 << (16 - bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildLength(int[] childs)
|
||||
{
|
||||
this.length = new byte [freqs.Length];
|
||||
int numNodes = childs.Length / 2;
|
||||
int numLeafs = (numNodes + 1) / 2;
|
||||
int overflow = 0;
|
||||
|
||||
for (int i = 0; i < maxLength; i++) {
|
||||
bl_counts[i] = 0;
|
||||
}
|
||||
|
||||
/* First calculate optimal bit lengths */
|
||||
int[] lengths = new int[numNodes];
|
||||
lengths[numNodes-1] = 0;
|
||||
|
||||
for (int i = numNodes - 1; i >= 0; i--) {
|
||||
if (childs[2*i+1] != -1) {
|
||||
int bitLength = lengths[i] + 1;
|
||||
if (bitLength > maxLength) {
|
||||
bitLength = maxLength;
|
||||
overflow++;
|
||||
}
|
||||
lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength;
|
||||
} else {
|
||||
/* A leaf node */
|
||||
int bitLength = lengths[i];
|
||||
bl_counts[bitLength - 1]++;
|
||||
this.length[childs[2*i]] = (byte) lengths[i];
|
||||
}
|
||||
}
|
||||
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("Tree "+freqs.Length+" lengths:");
|
||||
// for (int i=0; i < numLeafs; i++) {
|
||||
// //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
|
||||
// + " len: "+length[childs[2*i]]);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (overflow == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int incrBitLen = maxLength - 1;
|
||||
do {
|
||||
/* Find the first bit length which could increase: */
|
||||
while (bl_counts[--incrBitLen] == 0)
|
||||
;
|
||||
|
||||
/* Move this node one down and remove a corresponding
|
||||
* amount of overflow nodes.
|
||||
*/
|
||||
do {
|
||||
bl_counts[incrBitLen]--;
|
||||
bl_counts[++incrBitLen]++;
|
||||
overflow -= 1 << (maxLength - 1 - incrBitLen);
|
||||
} while (overflow > 0 && incrBitLen < maxLength - 1);
|
||||
} while (overflow > 0);
|
||||
|
||||
/* We may have overshot above. Move some nodes from maxLength to
|
||||
* maxLength-1 in that case.
|
||||
*/
|
||||
bl_counts[maxLength-1] += overflow;
|
||||
bl_counts[maxLength-2] -= overflow;
|
||||
|
||||
/* Now recompute all bit lengths, scanning in increasing
|
||||
* frequency. It is simpler to reconstruct all lengths instead of
|
||||
* fixing only the wrong ones. This idea is taken from 'ar'
|
||||
* written by Haruhiko Okumura.
|
||||
*
|
||||
* The nodes were inserted with decreasing frequency into the childs
|
||||
* array.
|
||||
*/
|
||||
int nodePtr = 2 * numLeafs;
|
||||
for (int bits = maxLength; bits != 0; bits--) {
|
||||
int n = bl_counts[bits-1];
|
||||
while (n > 0) {
|
||||
int childPtr = 2*childs[nodePtr++];
|
||||
if (childs[childPtr + 1] == -1) {
|
||||
/* We found another leaf */
|
||||
length[childs[childPtr]] = (byte) bits;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("*** After overflow elimination. ***");
|
||||
// for (int i=0; i < numLeafs; i++) {
|
||||
// //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
|
||||
// + " len: "+length[childs[2*i]]);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public void BuildTree()
|
||||
{
|
||||
int numSymbols = freqs.Length;
|
||||
|
||||
/* heap is a priority queue, sorted by frequency, least frequent
|
||||
* nodes first. The heap is a binary tree, with the property, that
|
||||
* the parent node is smaller than both child nodes. This assures
|
||||
* that the smallest node is the first parent.
|
||||
*
|
||||
* The binary tree is encoded in an array: 0 is root node and
|
||||
* the nodes 2*n+1, 2*n+2 are the child nodes of node n.
|
||||
*/
|
||||
int[] heap = new int[numSymbols];
|
||||
int heapLen = 0;
|
||||
int maxCode = 0;
|
||||
for (int n = 0; n < numSymbols; n++) {
|
||||
int freq = freqs[n];
|
||||
if (freq != 0) {
|
||||
/* Insert n into heap */
|
||||
int pos = heapLen++;
|
||||
int ppos;
|
||||
while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) {
|
||||
heap[pos] = heap[ppos];
|
||||
pos = ppos;
|
||||
}
|
||||
heap[pos] = n;
|
||||
|
||||
maxCode = n;
|
||||
}
|
||||
}
|
||||
|
||||
/* We could encode a single literal with 0 bits but then we
|
||||
* don't see the literals. Therefore we force at least two
|
||||
* literals to avoid this case. We don't care about order in
|
||||
* this case, both literals get a 1 bit code.
|
||||
*/
|
||||
while (heapLen < 2) {
|
||||
int node = maxCode < 2 ? ++maxCode : 0;
|
||||
heap[heapLen++] = node;
|
||||
}
|
||||
|
||||
numCodes = Math.Max(maxCode + 1, minNumCodes);
|
||||
|
||||
int numLeafs = heapLen;
|
||||
int[] childs = new int[4*heapLen - 2];
|
||||
int[] values = new int[2*heapLen - 1];
|
||||
int numNodes = numLeafs;
|
||||
for (int i = 0; i < heapLen; i++) {
|
||||
int node = heap[i];
|
||||
childs[2*i] = node;
|
||||
childs[2*i+1] = -1;
|
||||
values[i] = freqs[node] << 8;
|
||||
heap[i] = i;
|
||||
}
|
||||
|
||||
/* Construct the Huffman tree by repeatedly combining the least two
|
||||
* frequent nodes.
|
||||
*/
|
||||
do {
|
||||
int first = heap[0];
|
||||
int last = heap[--heapLen];
|
||||
|
||||
/* Propagate the hole to the leafs of the heap */
|
||||
int ppos = 0;
|
||||
int path = 1;
|
||||
|
||||
while (path < heapLen) {
|
||||
if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) {
|
||||
path++;
|
||||
}
|
||||
|
||||
heap[ppos] = heap[path];
|
||||
ppos = path;
|
||||
path = path * 2 + 1;
|
||||
}
|
||||
|
||||
/* Now propagate the last element down along path. Normally
|
||||
* it shouldn't go too deep.
|
||||
*/
|
||||
int lastVal = values[last];
|
||||
while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) {
|
||||
heap[path] = heap[ppos];
|
||||
}
|
||||
heap[path] = last;
|
||||
|
||||
|
||||
int second = heap[0];
|
||||
|
||||
/* Create a new node father of first and second */
|
||||
last = numNodes++;
|
||||
childs[2*last] = first;
|
||||
childs[2*last+1] = second;
|
||||
int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff);
|
||||
values[last] = lastVal = values[first] + values[second] - mindepth + 1;
|
||||
|
||||
/* Again, propagate the hole to the leafs */
|
||||
ppos = 0;
|
||||
path = 1;
|
||||
|
||||
while (path < heapLen) {
|
||||
if (path + 1 < heapLen && values[heap[path]] > values[heap[path+1]]) {
|
||||
path++;
|
||||
}
|
||||
|
||||
heap[ppos] = heap[path];
|
||||
ppos = path;
|
||||
path = ppos * 2 + 1;
|
||||
}
|
||||
|
||||
/* Now propagate the new element down along path */
|
||||
while ((path = ppos) > 0 && values[heap[ppos = (path - 1)/2]] > lastVal) {
|
||||
heap[path] = heap[ppos];
|
||||
}
|
||||
heap[path] = last;
|
||||
} while (heapLen > 1);
|
||||
|
||||
if (heap[0] != childs.Length / 2 - 1) {
|
||||
throw new Exception("Weird!");
|
||||
}
|
||||
BuildLength(childs);
|
||||
}
|
||||
|
||||
public int GetEncodedLength()
|
||||
{
|
||||
int len = 0;
|
||||
for (int i = 0; i < freqs.Length; i++) {
|
||||
len += freqs[i] * length[i];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
public void CalcBLFreq(Tree blTree)
|
||||
{
|
||||
int max_count; /* max repeat count */
|
||||
int min_count; /* min repeat count */
|
||||
int count; /* repeat count of the current code */
|
||||
int curlen = -1; /* length of current code */
|
||||
|
||||
int i = 0;
|
||||
while (i < numCodes) {
|
||||
count = 1;
|
||||
int nextlen = length[i];
|
||||
if (nextlen == 0) {
|
||||
max_count = 138;
|
||||
min_count = 3;
|
||||
} else {
|
||||
max_count = 6;
|
||||
min_count = 3;
|
||||
if (curlen != nextlen) {
|
||||
blTree.freqs[nextlen]++;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
curlen = nextlen;
|
||||
i++;
|
||||
|
||||
while (i < numCodes && curlen == length[i]) {
|
||||
i++;
|
||||
if (++count >= max_count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count < min_count) {
|
||||
blTree.freqs[curlen] += (short)count;
|
||||
} else if (curlen != 0) {
|
||||
blTree.freqs[REP_3_6]++;
|
||||
} else if (count <= 10) {
|
||||
blTree.freqs[REP_3_10]++;
|
||||
} else {
|
||||
blTree.freqs[REP_11_138]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteTree(Tree blTree)
|
||||
{
|
||||
int max_count; /* max repeat count */
|
||||
int min_count; /* min repeat count */
|
||||
int count; /* repeat count of the current code */
|
||||
int curlen = -1; /* length of current code */
|
||||
|
||||
int i = 0;
|
||||
while (i < numCodes) {
|
||||
count = 1;
|
||||
int nextlen = length[i];
|
||||
if (nextlen == 0) {
|
||||
max_count = 138;
|
||||
min_count = 3;
|
||||
} else {
|
||||
max_count = 6;
|
||||
min_count = 3;
|
||||
if (curlen != nextlen) {
|
||||
blTree.WriteSymbol(nextlen);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
curlen = nextlen;
|
||||
i++;
|
||||
|
||||
while (i < numCodes && curlen == length[i]) {
|
||||
i++;
|
||||
if (++count >= max_count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count < min_count) {
|
||||
while (count-- > 0) {
|
||||
blTree.WriteSymbol(curlen);
|
||||
}
|
||||
} else if (curlen != 0) {
|
||||
blTree.WriteSymbol(REP_3_6);
|
||||
dh.pending.WriteBits(count - 3, 2);
|
||||
} else if (count <= 10) {
|
||||
blTree.WriteSymbol(REP_3_10);
|
||||
dh.pending.WriteBits(count - 3, 3);
|
||||
} else {
|
||||
blTree.WriteSymbol(REP_11_138);
|
||||
dh.pending.WriteBits(count - 11, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DeflaterPending pending;
|
||||
private Tree literalTree, distTree, blTree;
|
||||
|
||||
private short[] d_buf;
|
||||
private byte[] l_buf;
|
||||
private int last_lit;
|
||||
private int extra_bits;
|
||||
|
||||
private static short[] staticLCodes;
|
||||
private static byte[] staticLLength;
|
||||
private static short[] staticDCodes;
|
||||
private static byte[] staticDLength;
|
||||
|
||||
/// <summary>
|
||||
/// Reverse the bits of a 16 bit value.
|
||||
/// </summary>
|
||||
public static short BitReverse(int value)
|
||||
{
|
||||
return (short) (bit4Reverse[value & 0xF] << 12 |
|
||||
bit4Reverse[(value >> 4) & 0xF] << 8 |
|
||||
bit4Reverse[(value >> 8) & 0xF] << 4 |
|
||||
bit4Reverse[value >> 12]);
|
||||
}
|
||||
|
||||
|
||||
static DeflaterHuffman()
|
||||
{
|
||||
/* See RFC 1951 3.2.6 */
|
||||
/* Literal codes */
|
||||
staticLCodes = new short[LITERAL_NUM];
|
||||
staticLLength = new byte[LITERAL_NUM];
|
||||
int i = 0;
|
||||
while (i < 144) {
|
||||
staticLCodes[i] = BitReverse((0x030 + i) << 8);
|
||||
staticLLength[i++] = 8;
|
||||
}
|
||||
while (i < 256) {
|
||||
staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);
|
||||
staticLLength[i++] = 9;
|
||||
}
|
||||
while (i < 280) {
|
||||
staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);
|
||||
staticLLength[i++] = 7;
|
||||
}
|
||||
while (i < LITERAL_NUM) {
|
||||
staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8);
|
||||
staticLLength[i++] = 8;
|
||||
}
|
||||
|
||||
/* Distant codes */
|
||||
staticDCodes = new short[DIST_NUM];
|
||||
staticDLength = new byte[DIST_NUM];
|
||||
for (i = 0; i < DIST_NUM; i++) {
|
||||
staticDCodes[i] = BitReverse(i << 11);
|
||||
staticDLength[i] = 5;
|
||||
}
|
||||
}
|
||||
|
||||
public DeflaterHuffman(DeflaterPending pending)
|
||||
{
|
||||
this.pending = pending;
|
||||
|
||||
literalTree = new Tree(this, LITERAL_NUM, 257, 15);
|
||||
distTree = new Tree(this, DIST_NUM, 1, 15);
|
||||
blTree = new Tree(this, BITLEN_NUM, 4, 7);
|
||||
|
||||
d_buf = new short[BUFSIZE];
|
||||
l_buf = new byte [BUFSIZE];
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
last_lit = 0;
|
||||
extra_bits = 0;
|
||||
literalTree.Reset();
|
||||
distTree.Reset();
|
||||
blTree.Reset();
|
||||
}
|
||||
|
||||
int Lcode(int len)
|
||||
{
|
||||
if (len == 255) {
|
||||
return 285;
|
||||
}
|
||||
|
||||
int code = 257;
|
||||
while (len >= 8) {
|
||||
code += 4;
|
||||
len >>= 1;
|
||||
}
|
||||
return code + len;
|
||||
}
|
||||
|
||||
int Dcode(int distance)
|
||||
{
|
||||
int code = 0;
|
||||
while (distance >= 4) {
|
||||
code += 2;
|
||||
distance >>= 1;
|
||||
}
|
||||
return code + distance;
|
||||
}
|
||||
|
||||
public void SendAllTrees(int blTreeCodes)
|
||||
{
|
||||
blTree.BuildCodes();
|
||||
literalTree.BuildCodes();
|
||||
distTree.BuildCodes();
|
||||
pending.WriteBits(literalTree.numCodes - 257, 5);
|
||||
pending.WriteBits(distTree.numCodes - 1, 5);
|
||||
pending.WriteBits(blTreeCodes - 4, 4);
|
||||
for (int rank = 0; rank < blTreeCodes; rank++) {
|
||||
pending.WriteBits(blTree.length[BL_ORDER[rank]], 3);
|
||||
}
|
||||
literalTree.WriteTree(blTree);
|
||||
distTree.WriteTree(blTree);
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// blTree.CheckEmpty();
|
||||
// }
|
||||
}
|
||||
|
||||
public void CompressBlock()
|
||||
{
|
||||
for (int i = 0; i < last_lit; i++) {
|
||||
int litlen = l_buf[i] & 0xff;
|
||||
int dist = d_buf[i];
|
||||
if (dist-- != 0) {
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// Console.Write("["+(dist+1)+","+(litlen+3)+"]: ");
|
||||
// }
|
||||
|
||||
int lc = Lcode(litlen);
|
||||
literalTree.WriteSymbol(lc);
|
||||
|
||||
int bits = (lc - 261) / 4;
|
||||
if (bits > 0 && bits <= 5) {
|
||||
pending.WriteBits(litlen & ((1 << bits) - 1), bits);
|
||||
}
|
||||
|
||||
int dc = Dcode(dist);
|
||||
distTree.WriteSymbol(dc);
|
||||
|
||||
bits = dc / 2 - 1;
|
||||
if (bits > 0) {
|
||||
pending.WriteBits(dist & ((1 << bits) - 1), bits);
|
||||
}
|
||||
} else {
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// if (litlen > 32 && litlen < 127) {
|
||||
// Console.Write("("+(char)litlen+"): ");
|
||||
// } else {
|
||||
// Console.Write("{"+litlen+"}: ");
|
||||
// }
|
||||
// }
|
||||
literalTree.WriteSymbol(litlen);
|
||||
}
|
||||
}
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// Console.Write("EOF: ");
|
||||
// }
|
||||
literalTree.WriteSymbol(EOF_SYMBOL);
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// literalTree.CheckEmpty();
|
||||
// distTree.CheckEmpty();
|
||||
// }
|
||||
}
|
||||
|
||||
public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
|
||||
{
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("Flushing stored block "+ storedLength);
|
||||
// }
|
||||
pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3);
|
||||
pending.AlignToByte();
|
||||
pending.WriteShort(storedLength);
|
||||
pending.WriteShort(~storedLength);
|
||||
pending.WriteBlock(stored, storedOffset, storedLength);
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
|
||||
{
|
||||
literalTree.freqs[EOF_SYMBOL]++;
|
||||
|
||||
/* Build trees */
|
||||
literalTree.BuildTree();
|
||||
distTree.BuildTree();
|
||||
|
||||
/* Calculate bitlen frequency */
|
||||
literalTree.CalcBLFreq(blTree);
|
||||
distTree.CalcBLFreq(blTree);
|
||||
|
||||
/* Build bitlen tree */
|
||||
blTree.BuildTree();
|
||||
|
||||
int blTreeCodes = 4;
|
||||
for (int i = 18; i > blTreeCodes; i--) {
|
||||
if (blTree.length[BL_ORDER[i]] > 0) {
|
||||
blTreeCodes = i+1;
|
||||
}
|
||||
}
|
||||
int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() +
|
||||
literalTree.GetEncodedLength() + distTree.GetEncodedLength() +
|
||||
extra_bits;
|
||||
|
||||
int static_len = extra_bits;
|
||||
for (int i = 0; i < LITERAL_NUM; i++) {
|
||||
static_len += literalTree.freqs[i] * staticLLength[i];
|
||||
}
|
||||
for (int i = 0; i < DIST_NUM; i++) {
|
||||
static_len += distTree.freqs[i] * staticDLength[i];
|
||||
}
|
||||
if (opt_len >= static_len) {
|
||||
/* Force static trees */
|
||||
opt_len = static_len;
|
||||
}
|
||||
|
||||
if (storedOffset >= 0 && storedLength+4 < opt_len >> 3) {
|
||||
/* Store Block */
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len
|
||||
// + " <= " + static_len);
|
||||
// }
|
||||
FlushStoredBlock(stored, storedOffset, storedLength, lastBlock);
|
||||
} else if (opt_len == static_len) {
|
||||
/* Encode with static tree */
|
||||
pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3);
|
||||
literalTree.SetStaticCodes(staticLCodes, staticLLength);
|
||||
distTree.SetStaticCodes(staticDCodes, staticDLength);
|
||||
CompressBlock();
|
||||
Reset();
|
||||
} else {
|
||||
/* Encode with dynamic tree */
|
||||
pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3);
|
||||
SendAllTrees(blTreeCodes);
|
||||
CompressBlock();
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFull()
|
||||
{
|
||||
// return last_lit + 16 >= BUFSIZE; // HACK: This was == 'last_lit == BUFSIZE', but errors occured with DeflateFast
|
||||
return last_lit >= BUFSIZE; // -jr- This is the correct form!
|
||||
}
|
||||
|
||||
public bool TallyLit(int lit)
|
||||
{
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// if (lit > 32 && lit < 127) {
|
||||
// //Console.WriteLine("("+(char)lit+")");
|
||||
// } else {
|
||||
// //Console.WriteLine("{"+lit+"}");
|
||||
// }
|
||||
// }
|
||||
d_buf[last_lit] = 0;
|
||||
l_buf[last_lit++] = (byte)lit;
|
||||
literalTree.freqs[lit]++;
|
||||
return IsFull();
|
||||
}
|
||||
|
||||
public bool TallyDist(int dist, int len)
|
||||
{
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("["+dist+","+len+"]");
|
||||
// }
|
||||
|
||||
d_buf[last_lit] = (short)dist;
|
||||
l_buf[last_lit++] = (byte)(len - 3);
|
||||
|
||||
int lc = Lcode(len - 3);
|
||||
literalTree.freqs[lc]++;
|
||||
if (lc >= 265 && lc < 285) {
|
||||
extra_bits += (lc - 261) / 4;
|
||||
}
|
||||
|
||||
int dc = Dcode(dist - 1);
|
||||
distTree.freqs[dc]++;
|
||||
if (dc >= 4) {
|
||||
extra_bits += dc / 2 - 1;
|
||||
}
|
||||
return IsFull();
|
||||
}
|
||||
}
|
||||
}
|
||||
52
irc/TechBot/Compression/DeflaterPending.cs
Normal file
52
irc/TechBot/Compression/DeflaterPending.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// DeflaterPending.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class stores the pending output of the Deflater.
|
||||
///
|
||||
/// author of the original java version : Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class DeflaterPending : PendingBuffer
|
||||
{
|
||||
public DeflaterPending() : base(DeflaterConstants.PENDING_BUF_SIZE)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
782
irc/TechBot/Compression/Inflater.cs
Normal file
782
irc/TechBot/Compression/Inflater.cs
Normal file
@@ -0,0 +1,782 @@
|
||||
// Inflater.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
using ICSharpCode.SharpZipLib.Checksums;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Inflater is used to decompress data that has been compressed according
|
||||
/// to the "deflate" standard described in rfc1950.
|
||||
///
|
||||
/// The usage is as following. First you have to set some input with
|
||||
/// <code>setInput()</code>, then inflate() it. If inflate doesn't
|
||||
/// inflate any bytes there may be three reasons:
|
||||
/// <ul>
|
||||
/// <li>needsInput() returns true because the input buffer is empty.
|
||||
/// You have to provide more input with <code>setInput()</code>.
|
||||
/// NOTE: needsInput() also returns true when, the stream is finished.
|
||||
/// </li>
|
||||
/// <li>needsDictionary() returns true, you have to provide a preset
|
||||
/// dictionary with <code>setDictionary()</code>.</li>
|
||||
/// <li>finished() returns true, the inflater has finished.</li>
|
||||
/// </ul>
|
||||
/// Once the first output byte is produced, a dictionary will not be
|
||||
/// needed at a later stage.
|
||||
///
|
||||
/// author of the original java version : John Leuner, Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class Inflater
|
||||
{
|
||||
/// <summary>
|
||||
/// Copy lengths for literal codes 257..285
|
||||
/// </summary>
|
||||
private static int[] CPLENS = {
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Extra bits for literal codes 257..285
|
||||
/// </summary>
|
||||
private static int[] CPLEXT = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Copy offsets for distance codes 0..29
|
||||
/// </summary>
|
||||
private static int[] CPDIST = {
|
||||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||
8193, 12289, 16385, 24577
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Extra bits for distance codes
|
||||
/// </summary>
|
||||
private static int[] CPDEXT = {
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||
12, 12, 13, 13
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// This are the state in which the inflater can be.
|
||||
/// </summary>
|
||||
private const int DECODE_HEADER = 0;
|
||||
private const int DECODE_DICT = 1;
|
||||
private const int DECODE_BLOCKS = 2;
|
||||
private const int DECODE_STORED_LEN1 = 3;
|
||||
private const int DECODE_STORED_LEN2 = 4;
|
||||
private const int DECODE_STORED = 5;
|
||||
private const int DECODE_DYN_HEADER = 6;
|
||||
private const int DECODE_HUFFMAN = 7;
|
||||
private const int DECODE_HUFFMAN_LENBITS = 8;
|
||||
private const int DECODE_HUFFMAN_DIST = 9;
|
||||
private const int DECODE_HUFFMAN_DISTBITS = 10;
|
||||
private const int DECODE_CHKSUM = 11;
|
||||
private const int FINISHED = 12;
|
||||
|
||||
/// <summary>
|
||||
/// This variable contains the current state.
|
||||
/// </summary>
|
||||
private int mode;
|
||||
|
||||
/// <summary>
|
||||
/// The adler checksum of the dictionary or of the decompressed
|
||||
/// stream, as it is written in the header resp. footer of the
|
||||
/// compressed stream.
|
||||
/// Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
|
||||
/// </summary>
|
||||
private int readAdler;
|
||||
|
||||
/// <summary>
|
||||
/// The number of bits needed to complete the current state. This
|
||||
/// is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
|
||||
/// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
|
||||
/// </summary>
|
||||
private int neededBits;
|
||||
private int repLength, repDist;
|
||||
private int uncomprLen;
|
||||
|
||||
/// <summary>
|
||||
/// True, if the last block flag was set in the last block of the
|
||||
/// inflated stream. This means that the stream ends after the
|
||||
/// current block.
|
||||
/// </summary>
|
||||
private bool isLastBlock;
|
||||
|
||||
/// <summary>
|
||||
/// The total number of inflated bytes.
|
||||
/// </summary>
|
||||
private int totalOut;
|
||||
|
||||
/// <summary>
|
||||
/// The total number of bytes set with setInput(). This is not the
|
||||
/// value returned by getTotalIn(), since this also includes the
|
||||
/// unprocessed input.
|
||||
/// </summary>
|
||||
private int totalIn;
|
||||
|
||||
/// <summary>
|
||||
/// This variable stores the nowrap flag that was given to the constructor.
|
||||
/// True means, that the inflated stream doesn't contain a header nor the
|
||||
/// checksum in the footer.
|
||||
/// </summary>
|
||||
private bool nowrap;
|
||||
|
||||
private StreamManipulator input;
|
||||
private OutputWindow outputWindow;
|
||||
private InflaterDynHeader dynHeader;
|
||||
private InflaterHuffmanTree litlenTree, distTree;
|
||||
private Adler32 adler;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new inflater.
|
||||
/// </summary>
|
||||
public Inflater() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new inflater.
|
||||
/// </summary>
|
||||
/// <param name="nowrap">
|
||||
/// true if no header and checksum field appears in the
|
||||
/// stream. This is used for GZIPed input. For compatibility with
|
||||
/// Sun JDK you should provide one byte of input more than needed in
|
||||
/// this case.
|
||||
/// </param>
|
||||
public Inflater(bool nowrap)
|
||||
{
|
||||
this.nowrap = nowrap;
|
||||
this.adler = new Adler32();
|
||||
input = new StreamManipulator();
|
||||
outputWindow = new OutputWindow();
|
||||
mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the inflater so that a new stream can be decompressed. All
|
||||
/// pending input and output will be discarded.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
|
||||
totalIn = totalOut = 0;
|
||||
input.Reset();
|
||||
outputWindow.Reset();
|
||||
dynHeader = null;
|
||||
litlenTree = null;
|
||||
distTree = null;
|
||||
isLastBlock = false;
|
||||
adler.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the deflate header.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// false if more input is needed.
|
||||
/// </returns>
|
||||
/// <exception cref="System.FormatException">
|
||||
/// if header is invalid.
|
||||
/// </exception>
|
||||
private bool DecodeHeader()
|
||||
{
|
||||
int header = input.PeekBits(16);
|
||||
if (header < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(16);
|
||||
/* The header is written in "wrong" byte order */
|
||||
header = ((header << 8) | (header >> 8)) & 0xffff;
|
||||
if (header % 31 != 0) {
|
||||
throw new FormatException("Header checksum illegal");
|
||||
}
|
||||
|
||||
if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) {
|
||||
throw new FormatException("Compression Method unknown");
|
||||
}
|
||||
|
||||
/* Maximum size of the backwards window in bits.
|
||||
* We currently ignore this, but we could use it to make the
|
||||
* inflater window more space efficient. On the other hand the
|
||||
* full window (15 bits) is needed most times, anyway.
|
||||
int max_wbits = ((header & 0x7000) >> 12) + 8;
|
||||
*/
|
||||
|
||||
if ((header & 0x0020) == 0) { // Dictionary flag?
|
||||
mode = DECODE_BLOCKS;
|
||||
} else {
|
||||
mode = DECODE_DICT;
|
||||
neededBits = 32;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the dictionary checksum after the deflate header.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// false if more input is needed.
|
||||
/// </returns>
|
||||
private bool DecodeDict()
|
||||
{
|
||||
while (neededBits > 0) {
|
||||
int dictByte = input.PeekBits(8);
|
||||
if (dictByte < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(8);
|
||||
readAdler = (readAdler << 8) | dictByte;
|
||||
neededBits -= 8;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the huffman encoded symbols in the input stream.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// false if more input is needed, true if output window is
|
||||
/// full or the current block ends.
|
||||
/// </returns>
|
||||
/// <exception cref="System.FormatException">
|
||||
/// if deflated stream is invalid.
|
||||
/// </exception>
|
||||
private bool DecodeHuffman()
|
||||
{
|
||||
int free = outputWindow.GetFreeSpace();
|
||||
while (free >= 258) {
|
||||
int symbol;
|
||||
switch (mode) {
|
||||
case DECODE_HUFFMAN:
|
||||
/* This is the inner loop so it is optimized a bit */
|
||||
while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) {
|
||||
outputWindow.Write(symbol);
|
||||
if (--free < 258) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (symbol < 257) {
|
||||
if (symbol < 0) {
|
||||
return false;
|
||||
} else {
|
||||
/* symbol == 256: end of block */
|
||||
distTree = null;
|
||||
litlenTree = null;
|
||||
mode = DECODE_BLOCKS;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
repLength = CPLENS[symbol - 257];
|
||||
neededBits = CPLEXT[symbol - 257];
|
||||
} catch (Exception) {
|
||||
throw new FormatException("Illegal rep length code");
|
||||
}
|
||||
goto case DECODE_HUFFMAN_LENBITS;/* fall through */
|
||||
case DECODE_HUFFMAN_LENBITS:
|
||||
if (neededBits > 0) {
|
||||
mode = DECODE_HUFFMAN_LENBITS;
|
||||
int i = input.PeekBits(neededBits);
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(neededBits);
|
||||
repLength += i;
|
||||
}
|
||||
mode = DECODE_HUFFMAN_DIST;
|
||||
goto case DECODE_HUFFMAN_DIST;/* fall through */
|
||||
case DECODE_HUFFMAN_DIST:
|
||||
symbol = distTree.GetSymbol(input);
|
||||
if (symbol < 0) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
repDist = CPDIST[symbol];
|
||||
neededBits = CPDEXT[symbol];
|
||||
} catch (Exception) {
|
||||
throw new FormatException("Illegal rep dist code");
|
||||
}
|
||||
|
||||
goto case DECODE_HUFFMAN_DISTBITS;/* fall through */
|
||||
case DECODE_HUFFMAN_DISTBITS:
|
||||
if (neededBits > 0) {
|
||||
mode = DECODE_HUFFMAN_DISTBITS;
|
||||
int i = input.PeekBits(neededBits);
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(neededBits);
|
||||
repDist += i;
|
||||
}
|
||||
outputWindow.Repeat(repLength, repDist);
|
||||
free -= repLength;
|
||||
mode = DECODE_HUFFMAN;
|
||||
break;
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the adler checksum after the deflate stream.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// false if more input is needed.
|
||||
/// </returns>
|
||||
/// <exception cref="System.FormatException">
|
||||
/// DataFormatException, if checksum doesn't match.
|
||||
/// </exception>
|
||||
private bool DecodeChksum()
|
||||
{
|
||||
while (neededBits > 0) {
|
||||
int chkByte = input.PeekBits(8);
|
||||
if (chkByte < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(8);
|
||||
readAdler = (readAdler << 8) | chkByte;
|
||||
neededBits -= 8;
|
||||
}
|
||||
if ((int) adler.Value != readAdler) {
|
||||
throw new FormatException("Adler chksum doesn't match: " + (int)adler.Value + " vs. " + readAdler);
|
||||
}
|
||||
mode = FINISHED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the deflated stream.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// false if more input is needed, or if finished.
|
||||
/// </returns>
|
||||
/// <exception cref="System.FormatException">
|
||||
/// DataFormatException, if deflated stream is invalid.
|
||||
/// </exception>
|
||||
private bool Decode()
|
||||
{
|
||||
switch (mode) {
|
||||
case DECODE_HEADER:
|
||||
return DecodeHeader();
|
||||
case DECODE_DICT:
|
||||
return DecodeDict();
|
||||
case DECODE_CHKSUM:
|
||||
return DecodeChksum();
|
||||
|
||||
case DECODE_BLOCKS:
|
||||
if (isLastBlock) {
|
||||
if (nowrap) {
|
||||
mode = FINISHED;
|
||||
return false;
|
||||
} else {
|
||||
input.SkipToByteBoundary();
|
||||
neededBits = 32;
|
||||
mode = DECODE_CHKSUM;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int type = input.PeekBits(3);
|
||||
if (type < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(3);
|
||||
|
||||
if ((type & 1) != 0) {
|
||||
isLastBlock = true;
|
||||
}
|
||||
switch (type >> 1){
|
||||
case DeflaterConstants.STORED_BLOCK:
|
||||
input.SkipToByteBoundary();
|
||||
mode = DECODE_STORED_LEN1;
|
||||
break;
|
||||
case DeflaterConstants.STATIC_TREES:
|
||||
litlenTree = InflaterHuffmanTree.defLitLenTree;
|
||||
distTree = InflaterHuffmanTree.defDistTree;
|
||||
mode = DECODE_HUFFMAN;
|
||||
break;
|
||||
case DeflaterConstants.DYN_TREES:
|
||||
dynHeader = new InflaterDynHeader();
|
||||
mode = DECODE_DYN_HEADER;
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unknown block type "+type);
|
||||
}
|
||||
return true;
|
||||
|
||||
case DECODE_STORED_LEN1:
|
||||
{
|
||||
if ((uncomprLen = input.PeekBits(16)) < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(16);
|
||||
mode = DECODE_STORED_LEN2;
|
||||
}
|
||||
goto case DECODE_STORED_LEN2; /* fall through */
|
||||
case DECODE_STORED_LEN2:
|
||||
{
|
||||
int nlen = input.PeekBits(16);
|
||||
if (nlen < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(16);
|
||||
if (nlen != (uncomprLen ^ 0xffff)) {
|
||||
throw new FormatException("broken uncompressed block");
|
||||
}
|
||||
mode = DECODE_STORED;
|
||||
}
|
||||
goto case DECODE_STORED;/* fall through */
|
||||
case DECODE_STORED:
|
||||
{
|
||||
int more = outputWindow.CopyStored(input, uncomprLen);
|
||||
uncomprLen -= more;
|
||||
if (uncomprLen == 0) {
|
||||
mode = DECODE_BLOCKS;
|
||||
return true;
|
||||
}
|
||||
return !input.IsNeedingInput;
|
||||
}
|
||||
|
||||
case DECODE_DYN_HEADER:
|
||||
if (!dynHeader.Decode(input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
litlenTree = dynHeader.BuildLitLenTree();
|
||||
distTree = dynHeader.BuildDistTree();
|
||||
mode = DECODE_HUFFMAN;
|
||||
goto case DECODE_HUFFMAN; /* fall through */
|
||||
case DECODE_HUFFMAN:
|
||||
case DECODE_HUFFMAN_LENBITS:
|
||||
case DECODE_HUFFMAN_DIST:
|
||||
case DECODE_HUFFMAN_DISTBITS:
|
||||
return DecodeHuffman();
|
||||
case FINISHED:
|
||||
return false;
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the preset dictionary. This should only be called, if
|
||||
/// needsDictionary() returns true and it should set the same
|
||||
/// dictionary, that was used for deflating. The getAdler()
|
||||
/// function returns the checksum of the dictionary needed.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// the dictionary.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if no dictionary is needed.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// if the dictionary checksum is wrong.
|
||||
/// </exception>
|
||||
public void SetDictionary(byte[] buffer)
|
||||
{
|
||||
SetDictionary(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the preset dictionary. This should only be called, if
|
||||
/// needsDictionary() returns true and it should set the same
|
||||
/// dictionary, that was used for deflating. The getAdler()
|
||||
/// function returns the checksum of the dictionary needed.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// the dictionary.
|
||||
/// </param>
|
||||
/// <param name="off">
|
||||
/// the offset into buffer where the dictionary starts.
|
||||
/// </param>
|
||||
/// <param name="len">
|
||||
/// the length of the dictionary.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if no dictionary is needed.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// if the dictionary checksum is wrong.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// if the off and/or len are wrong.
|
||||
/// </exception>
|
||||
public void SetDictionary(byte[] buffer, int off, int len)
|
||||
{
|
||||
if (!IsNeedingDictionary) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
adler.Update(buffer, off, len);
|
||||
if ((int)adler.Value != readAdler) {
|
||||
throw new ArgumentException("Wrong adler checksum");
|
||||
}
|
||||
adler.Reset();
|
||||
outputWindow.CopyDict(buffer, off, len);
|
||||
mode = DECODE_BLOCKS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the input. This should only be called, if needsInput()
|
||||
/// returns true.
|
||||
/// </summary>
|
||||
/// <param name="buf">
|
||||
/// the input.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if no input is needed.
|
||||
/// </exception>
|
||||
public void SetInput(byte[] buf)
|
||||
{
|
||||
SetInput(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the input. This should only be called, if needsInput()
|
||||
/// returns true.
|
||||
/// </summary>
|
||||
/// <param name="buf">
|
||||
/// the input.
|
||||
/// </param>
|
||||
/// <param name="off">
|
||||
/// the offset into buffer where the input starts.
|
||||
/// </param>
|
||||
/// <param name="len">
|
||||
/// the length of the input.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if no input is needed.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// if the off and/or len are wrong.
|
||||
/// </exception>
|
||||
public void SetInput(byte[] buf, int off, int len)
|
||||
{
|
||||
input.SetInput(buf, off, len);
|
||||
totalIn += len;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inflates the compressed stream to the output buffer. If this
|
||||
/// returns 0, you should check, whether needsDictionary(),
|
||||
/// needsInput() or finished() returns true, to determine why no
|
||||
/// further output is produced.
|
||||
/// </summary>
|
||||
/// <param name = "buf">
|
||||
/// the output buffer.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// the number of bytes written to the buffer, 0 if no further
|
||||
/// output can be produced.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// if buf has length 0.
|
||||
/// </exception>
|
||||
/// <exception cref="System.FormatException">
|
||||
/// if deflated stream is invalid.
|
||||
/// </exception>
|
||||
public int Inflate(byte[] buf)
|
||||
{
|
||||
return Inflate(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inflates the compressed stream to the output buffer. If this
|
||||
/// returns 0, you should check, whether needsDictionary(),
|
||||
/// needsInput() or finished() returns true, to determine why no
|
||||
/// further output is produced.
|
||||
/// </summary>
|
||||
/// <param name = "buf">
|
||||
/// the output buffer.
|
||||
/// </param>
|
||||
/// <param name = "off">
|
||||
/// the offset into buffer where the output should start.
|
||||
/// </param>
|
||||
/// <param name = "len">
|
||||
/// the maximum length of the output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// the number of bytes written to the buffer, 0 if no further output can be produced.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// if len is <= 0.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// if the off and/or len are wrong.
|
||||
/// </exception>
|
||||
/// <exception cref="System.FormatException">
|
||||
/// if deflated stream is invalid.
|
||||
/// </exception>
|
||||
public int Inflate(byte[] buf, int off, int len)
|
||||
{
|
||||
if (len < 0) {
|
||||
throw new ArgumentOutOfRangeException("len < 0");
|
||||
}
|
||||
// Special case: len may be zero
|
||||
if (len == 0) {
|
||||
if (IsFinished == false) {// -jr- 08-Nov-2003 INFLATE_BUG fix..
|
||||
Decode();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* // Check for correct buff, off, len triple
|
||||
if (off < 0 || off + len >= buf.Length) {
|
||||
throw new ArgumentException("off/len outside buf bounds");
|
||||
}*/
|
||||
int count = 0;
|
||||
int more;
|
||||
do {
|
||||
if (mode != DECODE_CHKSUM) {
|
||||
/* Don't give away any output, if we are waiting for the
|
||||
* checksum in the input stream.
|
||||
*
|
||||
* With this trick we have always:
|
||||
* needsInput() and not finished()
|
||||
* implies more output can be produced.
|
||||
*/
|
||||
more = outputWindow.CopyOutput(buf, off, len);
|
||||
adler.Update(buf, off, more);
|
||||
off += more;
|
||||
count += more;
|
||||
totalOut += more;
|
||||
len -= more;
|
||||
if (len == 0) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
} while (Decode() || (outputWindow.GetAvailable() > 0 && mode != DECODE_CHKSUM));
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true, if the input buffer is empty.
|
||||
/// You should then call setInput().
|
||||
/// NOTE: This method also returns true when the stream is finished.
|
||||
/// </summary>
|
||||
public bool IsNeedingInput {
|
||||
get {
|
||||
return input.IsNeedingInput;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true, if a preset dictionary is needed to inflate the input.
|
||||
/// </summary>
|
||||
public bool IsNeedingDictionary {
|
||||
get {
|
||||
return mode == DECODE_DICT && neededBits == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true, if the inflater has finished. This means, that no
|
||||
/// input is needed and no output can be produced.
|
||||
/// </summary>
|
||||
public bool IsFinished {
|
||||
get {
|
||||
return mode == FINISHED && outputWindow.GetAvailable() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the adler checksum. This is either the checksum of all
|
||||
/// uncompressed bytes returned by inflate(), or if needsDictionary()
|
||||
/// returns true (and thus no output was yet produced) this is the
|
||||
/// adler checksum of the expected dictionary.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the adler checksum.
|
||||
/// </returns>
|
||||
public int Adler {
|
||||
get {
|
||||
return IsNeedingDictionary ? readAdler : (int) adler.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of output bytes returned by inflate().
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the total number of output bytes.
|
||||
/// </returns>
|
||||
public int TotalOut {
|
||||
get {
|
||||
return totalOut;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of processed compressed input bytes.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the total number of bytes of processed input bytes.
|
||||
/// </returns>
|
||||
public int TotalIn {
|
||||
get {
|
||||
return totalIn - RemainingInput;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of unprocessed input. Useful, if the end of the
|
||||
/// stream is reached and you want to further process the bytes after
|
||||
/// the deflate stream.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the number of bytes of the input which were not processed.
|
||||
/// </returns>
|
||||
public int RemainingInput {
|
||||
get {
|
||||
return input.AvailableBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
207
irc/TechBot/Compression/InflaterDynHeader.cs
Normal file
207
irc/TechBot/Compression/InflaterDynHeader.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
// InflaterDynHeader.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
class InflaterDynHeader
|
||||
{
|
||||
const int LNUM = 0;
|
||||
const int DNUM = 1;
|
||||
const int BLNUM = 2;
|
||||
const int BLLENS = 3;
|
||||
const int LENS = 4;
|
||||
const int REPS = 5;
|
||||
|
||||
static readonly int[] repMin = { 3, 3, 11 };
|
||||
static readonly int[] repBits = { 2, 3, 7 };
|
||||
|
||||
byte[] blLens;
|
||||
byte[] litdistLens;
|
||||
|
||||
InflaterHuffmanTree blTree;
|
||||
|
||||
int mode;
|
||||
int lnum, dnum, blnum, num;
|
||||
int repSymbol;
|
||||
byte lastLen;
|
||||
int ptr;
|
||||
|
||||
static readonly int[] BL_ORDER =
|
||||
{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
|
||||
public InflaterDynHeader()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Decode(StreamManipulator input)
|
||||
{
|
||||
decode_loop:
|
||||
for (;;) {
|
||||
switch (mode) {
|
||||
case LNUM:
|
||||
lnum = input.PeekBits(5);
|
||||
if (lnum < 0) {
|
||||
return false;
|
||||
}
|
||||
lnum += 257;
|
||||
input.DropBits(5);
|
||||
// System.err.println("LNUM: "+lnum);
|
||||
mode = DNUM;
|
||||
goto case DNUM; // fall through
|
||||
case DNUM:
|
||||
dnum = input.PeekBits(5);
|
||||
if (dnum < 0) {
|
||||
return false;
|
||||
}
|
||||
dnum++;
|
||||
input.DropBits(5);
|
||||
// System.err.println("DNUM: "+dnum);
|
||||
num = lnum+dnum;
|
||||
litdistLens = new byte[num];
|
||||
mode = BLNUM;
|
||||
goto case BLNUM; // fall through
|
||||
case BLNUM:
|
||||
blnum = input.PeekBits(4);
|
||||
if (blnum < 0) {
|
||||
return false;
|
||||
}
|
||||
blnum += 4;
|
||||
input.DropBits(4);
|
||||
blLens = new byte[19];
|
||||
ptr = 0;
|
||||
// System.err.println("BLNUM: "+blnum);
|
||||
mode = BLLENS;
|
||||
goto case BLLENS; // fall through
|
||||
case BLLENS:
|
||||
while (ptr < blnum) {
|
||||
int len = input.PeekBits(3);
|
||||
if (len < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(3);
|
||||
// System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
|
||||
blLens[BL_ORDER[ptr]] = (byte) len;
|
||||
ptr++;
|
||||
}
|
||||
blTree = new InflaterHuffmanTree(blLens);
|
||||
blLens = null;
|
||||
ptr = 0;
|
||||
mode = LENS;
|
||||
goto case LENS; // fall through
|
||||
case LENS:
|
||||
{
|
||||
int symbol;
|
||||
while (((symbol = blTree.GetSymbol(input)) & ~15) == 0) {
|
||||
/* Normal case: symbol in [0..15] */
|
||||
|
||||
// System.err.println("litdistLens["+ptr+"]: "+symbol);
|
||||
litdistLens[ptr++] = lastLen = (byte)symbol;
|
||||
|
||||
if (ptr == num) {
|
||||
/* Finished */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* need more input ? */
|
||||
if (symbol < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* otherwise repeat code */
|
||||
if (symbol >= 17) {
|
||||
/* repeat zero */
|
||||
// System.err.println("repeating zero");
|
||||
lastLen = 0;
|
||||
} else {
|
||||
if (ptr == 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
repSymbol = symbol-16;
|
||||
}
|
||||
mode = REPS;
|
||||
goto case REPS; // fall through
|
||||
case REPS:
|
||||
{
|
||||
int bits = repBits[repSymbol];
|
||||
int count = input.PeekBits(bits);
|
||||
if (count < 0) {
|
||||
return false;
|
||||
}
|
||||
input.DropBits(bits);
|
||||
count += repMin[repSymbol];
|
||||
// System.err.println("litdistLens repeated: "+count);
|
||||
|
||||
if (ptr + count > num) {
|
||||
throw new Exception();
|
||||
}
|
||||
while (count-- > 0) {
|
||||
litdistLens[ptr++] = lastLen;
|
||||
}
|
||||
|
||||
if (ptr == num) {
|
||||
/* Finished */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mode = LENS;
|
||||
goto decode_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InflaterHuffmanTree BuildLitLenTree()
|
||||
{
|
||||
byte[] litlenLens = new byte[lnum];
|
||||
Array.Copy(litdistLens, 0, litlenLens, 0, lnum);
|
||||
return new InflaterHuffmanTree(litlenLens);
|
||||
}
|
||||
|
||||
public InflaterHuffmanTree BuildDistTree()
|
||||
{
|
||||
byte[] distLens = new byte[dnum];
|
||||
Array.Copy(litdistLens, lnum, distLens, 0, dnum);
|
||||
return new InflaterHuffmanTree(distLens);
|
||||
}
|
||||
}
|
||||
}
|
||||
213
irc/TechBot/Compression/InflaterHuffmanTree.cs
Normal file
213
irc/TechBot/Compression/InflaterHuffmanTree.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
// InflaterHuffmanTree.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
public class InflaterHuffmanTree
|
||||
{
|
||||
private static int MAX_BITLEN = 15;
|
||||
private short[] tree;
|
||||
|
||||
public static InflaterHuffmanTree defLitLenTree, defDistTree;
|
||||
|
||||
static InflaterHuffmanTree()
|
||||
{
|
||||
try {
|
||||
byte[] codeLengths = new byte[288];
|
||||
int i = 0;
|
||||
while (i < 144) {
|
||||
codeLengths[i++] = 8;
|
||||
}
|
||||
while (i < 256) {
|
||||
codeLengths[i++] = 9;
|
||||
}
|
||||
while (i < 280) {
|
||||
codeLengths[i++] = 7;
|
||||
}
|
||||
while (i < 288) {
|
||||
codeLengths[i++] = 8;
|
||||
}
|
||||
defLitLenTree = new InflaterHuffmanTree(codeLengths);
|
||||
|
||||
codeLengths = new byte[32];
|
||||
i = 0;
|
||||
while (i < 32) {
|
||||
codeLengths[i++] = 5;
|
||||
}
|
||||
defDistTree = new InflaterHuffmanTree(codeLengths);
|
||||
} catch (Exception) {
|
||||
throw new ApplicationException("InflaterHuffmanTree: static tree length illegal");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a Huffman tree from the array of code lengths.
|
||||
/// </summary>
|
||||
/// <param name = "codeLengths">
|
||||
/// the array of code lengths
|
||||
/// </param>
|
||||
public InflaterHuffmanTree(byte[] codeLengths)
|
||||
{
|
||||
BuildTree(codeLengths);
|
||||
}
|
||||
|
||||
private void BuildTree(byte[] codeLengths)
|
||||
{
|
||||
int[] blCount = new int[MAX_BITLEN + 1];
|
||||
int[] nextCode = new int[MAX_BITLEN + 1];
|
||||
|
||||
for (int i = 0; i < codeLengths.Length; i++) {
|
||||
int bits = codeLengths[i];
|
||||
if (bits > 0) {
|
||||
blCount[bits]++;
|
||||
}
|
||||
}
|
||||
|
||||
int code = 0;
|
||||
int treeSize = 512;
|
||||
for (int bits = 1; bits <= MAX_BITLEN; bits++) {
|
||||
nextCode[bits] = code;
|
||||
code += blCount[bits] << (16 - bits);
|
||||
if (bits >= 10) {
|
||||
/* We need an extra table for bit lengths >= 10. */
|
||||
int start = nextCode[bits] & 0x1ff80;
|
||||
int end = code & 0x1ff80;
|
||||
treeSize += (end - start) >> (16 - bits);
|
||||
}
|
||||
}
|
||||
/* -jr comment this out! doesnt work for dynamic trees and pkzip 2.04g
|
||||
if (code != 65536)
|
||||
{
|
||||
throw new Exception("Code lengths don't add up properly.");
|
||||
}
|
||||
*/
|
||||
/* Now create and fill the extra tables from longest to shortest
|
||||
* bit len. This way the sub trees will be aligned.
|
||||
*/
|
||||
tree = new short[treeSize];
|
||||
int treePtr = 512;
|
||||
for (int bits = MAX_BITLEN; bits >= 10; bits--) {
|
||||
int end = code & 0x1ff80;
|
||||
code -= blCount[bits] << (16 - bits);
|
||||
int start = code & 0x1ff80;
|
||||
for (int i = start; i < end; i += 1 << 7) {
|
||||
tree[DeflaterHuffman.BitReverse(i)] = (short) ((-treePtr << 4) | bits);
|
||||
treePtr += 1 << (bits-9);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < codeLengths.Length; i++) {
|
||||
int bits = codeLengths[i];
|
||||
if (bits == 0) {
|
||||
continue;
|
||||
}
|
||||
code = nextCode[bits];
|
||||
int revcode = DeflaterHuffman.BitReverse(code);
|
||||
if (bits <= 9) {
|
||||
do {
|
||||
tree[revcode] = (short) ((i << 4) | bits);
|
||||
revcode += 1 << bits;
|
||||
} while (revcode < 512);
|
||||
} else {
|
||||
int subTree = tree[revcode & 511];
|
||||
int treeLen = 1 << (subTree & 15);
|
||||
subTree = -(subTree >> 4);
|
||||
do {
|
||||
tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
|
||||
revcode += 1 << bits;
|
||||
} while (revcode < treeLen);
|
||||
}
|
||||
nextCode[bits] = code + (1 << (16 - bits));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the next symbol from input. The symbol is encoded using the
|
||||
/// huffman tree.
|
||||
/// </summary>
|
||||
/// <param name="input">
|
||||
/// input the input source.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// the next symbol, or -1 if not enough input is available.
|
||||
/// </returns>
|
||||
public int GetSymbol(StreamManipulator input)
|
||||
{
|
||||
int lookahead, symbol;
|
||||
if ((lookahead = input.PeekBits(9)) >= 0) {
|
||||
if ((symbol = tree[lookahead]) >= 0) {
|
||||
input.DropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
}
|
||||
int subtree = -(symbol >> 4);
|
||||
int bitlen = symbol & 15;
|
||||
if ((lookahead = input.PeekBits(bitlen)) >= 0) {
|
||||
symbol = tree[subtree | (lookahead >> 9)];
|
||||
input.DropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
} else {
|
||||
int bits = input.AvailableBits;
|
||||
lookahead = input.PeekBits(bits);
|
||||
symbol = tree[subtree | (lookahead >> 9)];
|
||||
if ((symbol & 15) <= bits) {
|
||||
input.DropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int bits = input.AvailableBits;
|
||||
lookahead = input.PeekBits(bits);
|
||||
symbol = tree[lookahead];
|
||||
if (symbol >= 0 && (symbol & 15) <= bits) {
|
||||
input.DropBits(symbol & 15);
|
||||
return symbol >> 4;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
210
irc/TechBot/Compression/PendingBuffer.cs
Normal file
210
irc/TechBot/Compression/PendingBuffer.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
// PendingBuffer.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class is general purpose class for writing data to a buffer.
|
||||
///
|
||||
/// It allows you to write bits as well as bytes
|
||||
/// Based on DeflaterPending.java
|
||||
///
|
||||
/// author of the original java version : Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class PendingBuffer
|
||||
{
|
||||
protected byte[] buf;
|
||||
int start;
|
||||
int end;
|
||||
|
||||
uint bits;
|
||||
int bitCount;
|
||||
|
||||
public PendingBuffer() : this( 4096 )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public PendingBuffer(int bufsize)
|
||||
{
|
||||
buf = new byte[bufsize];
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
start = end = bitCount = 0;
|
||||
}
|
||||
|
||||
public void WriteByte(int b)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
buf[end++] = (byte) b;
|
||||
}
|
||||
|
||||
public void WriteShort(int s)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
buf[end++] = (byte) s;
|
||||
buf[end++] = (byte) (s >> 8);
|
||||
}
|
||||
|
||||
public void WriteInt(int s)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
buf[end++] = (byte) s;
|
||||
buf[end++] = (byte) (s >> 8);
|
||||
buf[end++] = (byte) (s >> 16);
|
||||
buf[end++] = (byte) (s >> 24);
|
||||
}
|
||||
|
||||
public void WriteBlock(byte[] block, int offset, int len)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
System.Array.Copy(block, offset, buf, end, len);
|
||||
end += len;
|
||||
}
|
||||
|
||||
public int BitCount {
|
||||
get {
|
||||
return bitCount;
|
||||
}
|
||||
}
|
||||
|
||||
public void AlignToByte()
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
if (bitCount > 0) {
|
||||
buf[end++] = (byte) bits;
|
||||
if (bitCount > 8) {
|
||||
buf[end++] = (byte) (bits >> 8);
|
||||
}
|
||||
}
|
||||
bits = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
|
||||
public void WriteBits(int b, int count)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
// if (DeflaterConstants.DEBUGGING) {
|
||||
// //Console.WriteLine("writeBits("+b+","+count+")");
|
||||
// }
|
||||
bits |= (uint)(b << bitCount);
|
||||
bitCount += count;
|
||||
if (bitCount >= 16) {
|
||||
buf[end++] = (byte) bits;
|
||||
buf[end++] = (byte) (bits >> 8);
|
||||
bits >>= 16;
|
||||
bitCount -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteShortMSB(int s)
|
||||
{
|
||||
if (DeflaterConstants.DEBUGGING && start != 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
buf[end++] = (byte) (s >> 8);
|
||||
buf[end++] = (byte) s;
|
||||
}
|
||||
|
||||
public bool IsFlushed {
|
||||
get {
|
||||
return end == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the pending buffer into the given output array. If the
|
||||
/// output array is to small, only a partial flush is done.
|
||||
/// </summary>
|
||||
/// <param name="output">
|
||||
/// the output array;
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// the offset into output array;
|
||||
/// </param>
|
||||
/// <param name="length">
|
||||
/// length the maximum number of bytes to store;
|
||||
/// </param>
|
||||
/// <exception name="ArgumentOutOfRangeException">
|
||||
/// IndexOutOfBoundsException if offset or length are invalid.
|
||||
/// </exception>
|
||||
public int Flush(byte[] output, int offset, int length)
|
||||
{
|
||||
if (bitCount >= 8) {
|
||||
buf[end++] = (byte) bits;
|
||||
bits >>= 8;
|
||||
bitCount -= 8;
|
||||
}
|
||||
if (length > end - start) {
|
||||
length = end - start;
|
||||
System.Array.Copy(buf, start, output, offset, length);
|
||||
start = 0;
|
||||
end = 0;
|
||||
} else {
|
||||
System.Array.Copy(buf, start, output, offset, length);
|
||||
start += length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
byte[] ret = new byte[end - start];
|
||||
System.Array.Copy(buf, start, ret, 0, ret.Length);
|
||||
start = 0;
|
||||
end = 0;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
379
irc/TechBot/Compression/Streams/DeflaterOutputStream.cs
Normal file
379
irc/TechBot/Compression/Streams/DeflaterOutputStream.cs
Normal file
@@ -0,0 +1,379 @@
|
||||
// DeflaterOutputStream.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Checksums;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This is a special FilterOutputStream deflating the bytes that are
|
||||
/// written through it. It uses the Deflater for deflating.
|
||||
///
|
||||
/// authors of the original java version : Tom Tromey, Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class DeflaterOutputStream : Stream
|
||||
{
|
||||
/// <summary>
|
||||
/// This buffer is used temporarily to retrieve the bytes from the
|
||||
/// deflater and write them to the underlying output stream.
|
||||
/// </summary>
|
||||
protected byte[] buf;
|
||||
|
||||
/// <summary>
|
||||
/// The deflater which is used to deflate the stream.
|
||||
/// </summary>
|
||||
protected Deflater def;
|
||||
|
||||
/// <summary>
|
||||
/// base stream the deflater depends on.
|
||||
/// </summary>
|
||||
protected Stream baseOutputStream;
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return baseOutputStream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
// return baseOutputStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return baseOutputStream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override long Length {
|
||||
get {
|
||||
return baseOutputStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override long Position {
|
||||
get {
|
||||
return baseOutputStream.Position;
|
||||
}
|
||||
set {
|
||||
baseOutputStream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException("Seek not supported"); // -jr- 01-Dec-2003
|
||||
// return baseOutputStream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override void SetLength(long val)
|
||||
{
|
||||
baseOutputStream.SetLength(val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override int ReadByte()
|
||||
{
|
||||
return baseOutputStream.ReadByte();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override int Read(byte[] b, int off, int len)
|
||||
{
|
||||
return baseOutputStream.Read(b, off, len);
|
||||
}
|
||||
// -jr- 01-Dec-2003
|
||||
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
throw new NotSupportedException("Asynch read not currently supported");
|
||||
}
|
||||
|
||||
// -jr- 01-Dec-2003
|
||||
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
throw new NotSupportedException("Asynch write not currently supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deflates everything in the def's input buffers. This will call
|
||||
/// <code>def.deflate()</code> until all bytes from the input buffers
|
||||
/// are processed.
|
||||
/// </summary>
|
||||
protected void Deflate()
|
||||
{
|
||||
while (!def.IsNeedingInput) {
|
||||
int len = def.Deflate(buf, 0, buf.Length);
|
||||
|
||||
// System.err.println("DOS deflated " + len + " baseOutputStream of " + buf.length);
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
baseOutputStream.Write(buf, 0, len);
|
||||
}
|
||||
|
||||
if (!def.IsNeedingInput) {
|
||||
throw new ApplicationException("Can't deflate all input?");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
|
||||
/// </summary>
|
||||
/// <param name="baseOutputStream">
|
||||
/// the output stream where deflated output should be written.
|
||||
/// </param>
|
||||
public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new DeflaterOutputStream with the given Deflater and
|
||||
/// default buffer size.
|
||||
/// </summary>
|
||||
/// <param name="baseOutputStream">
|
||||
/// the output stream where deflated output should be written.
|
||||
/// </param>
|
||||
/// <param name="defl">
|
||||
/// the underlying deflater.
|
||||
/// </param>
|
||||
public DeflaterOutputStream(Stream baseOutputStream, Deflater defl) :this(baseOutputStream, defl, 512)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new DeflaterOutputStream with the given Deflater and
|
||||
/// buffer size.
|
||||
/// </summary>
|
||||
/// <param name="baseOutputStream">
|
||||
/// the output stream where deflated output should be written.
|
||||
/// </param>
|
||||
/// <param name="defl">
|
||||
/// the underlying deflater.
|
||||
/// </param>
|
||||
/// <param name="bufsize">
|
||||
/// the buffer size.
|
||||
/// </param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// if bufsize isn't positive.
|
||||
/// </exception>
|
||||
public DeflaterOutputStream(Stream baseOutputStream, Deflater defl, int bufsize)
|
||||
{
|
||||
this.baseOutputStream = baseOutputStream;
|
||||
if (bufsize <= 0) {
|
||||
throw new InvalidOperationException("bufsize <= 0");
|
||||
}
|
||||
buf = new byte[bufsize];
|
||||
def = defl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the stream by calling flush() on the deflater and then
|
||||
/// on the underlying stream. This ensures that all bytes are
|
||||
/// flushed.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
def.Flush();
|
||||
Deflate();
|
||||
baseOutputStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the stream by calling finish() on the deflater.
|
||||
/// </summary>
|
||||
public virtual void Finish()
|
||||
{
|
||||
def.Finish();
|
||||
while (!def.IsFinished) {
|
||||
int len = def.Deflate(buf, 0, buf.Length);
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// kidnthrain encryption alteration
|
||||
if (this.Password != null) {
|
||||
// plain data has been deflated. Now encrypt result
|
||||
this.EncryptBlock(buf, 0, len);
|
||||
}
|
||||
|
||||
baseOutputStream.Write(buf, 0, len);
|
||||
}
|
||||
if (!def.IsFinished) {
|
||||
throw new ApplicationException("Can't deflate all input?");
|
||||
}
|
||||
baseOutputStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls finish () and closes the stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
Finish();
|
||||
baseOutputStream.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a single byte to the compressed output stream.
|
||||
/// </summary>
|
||||
/// <param name="bval">
|
||||
/// the byte value.
|
||||
/// </param>
|
||||
public override void WriteByte(byte bval)
|
||||
{
|
||||
byte[] b = new byte[1];
|
||||
b[0] = (byte) bval;
|
||||
Write(b, 0, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a len bytes from an array to the compressed stream.
|
||||
/// </summary>
|
||||
/// <param name="buf">
|
||||
/// the byte array.
|
||||
/// </param>
|
||||
/// <param name="off">
|
||||
/// the offset into the byte array where to start.
|
||||
/// </param>
|
||||
/// <param name="len">
|
||||
/// the number of bytes to write.
|
||||
/// </param>
|
||||
public override void Write(byte[] buf, int off, int len)
|
||||
{
|
||||
// System.err.println("DOS with off " + off + " and len " + len);
|
||||
def.SetInput(buf, off, len);
|
||||
Deflate();
|
||||
}
|
||||
|
||||
#region Encryption
|
||||
string password = null;
|
||||
uint[] keys = null;
|
||||
|
||||
public string Password {
|
||||
get {
|
||||
return password;
|
||||
}
|
||||
set {
|
||||
password = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//The beauty of xor-ing bits is that
|
||||
//plain ^ key = enc
|
||||
//and enc ^ key = plain
|
||||
//accordingly, this is the exact same as the decrypt byte
|
||||
//function in InflaterInputStream
|
||||
protected byte EncryptByte()
|
||||
{
|
||||
uint temp = ((keys[2] & 0xFFFF) | 2);
|
||||
return (byte)((temp * (temp ^ 1)) >> 8);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Takes a buffer of data and uses the keys
|
||||
/// that have been previously initialized from a
|
||||
/// password and then updated via a random encryption header
|
||||
/// to encrypt that data
|
||||
/// </summary>
|
||||
protected void EncryptBlock(byte[] buf, int off, int len)
|
||||
{
|
||||
for (int i = off; i < off + len; ++i) {
|
||||
byte oldbyte = buf[i];
|
||||
buf[i] ^= EncryptByte();
|
||||
UpdateKeys(oldbyte);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes our encryption keys using a given password
|
||||
/// </summary>
|
||||
protected void InitializePassword(string password) {
|
||||
keys = new uint[] {
|
||||
0x12345678,
|
||||
0x23456789,
|
||||
0x34567890
|
||||
};
|
||||
|
||||
for (int i = 0; i < password.Length; ++i) {
|
||||
UpdateKeys((byte)password[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateKeys(byte ch)
|
||||
{
|
||||
keys[0] = Crc32.ComputeCrc32(keys[0], ch);
|
||||
keys[1] = keys[1] + (byte)keys[0];
|
||||
keys[1] = keys[1] * 134775813 + 1;
|
||||
keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
386
irc/TechBot/Compression/Streams/InflaterInputStream.cs
Normal file
386
irc/TechBot/Compression/Streams/InflaterInputStream.cs
Normal file
@@ -0,0 +1,386 @@
|
||||
// InflaterInputStream.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using ICSharpCode.SharpZipLib.Checksums;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This filter stream is used to decompress data compressed baseInputStream the "deflate"
|
||||
/// format. The "deflate" format is described baseInputStream RFC 1951.
|
||||
///
|
||||
/// This stream may form the basis for other decompression filters, such
|
||||
/// as the <code>GzipInputStream</code>.
|
||||
///
|
||||
/// author of the original java version : John Leuner
|
||||
/// </summary>
|
||||
public class InflaterInputStream : Stream
|
||||
{
|
||||
//Variables
|
||||
|
||||
/// <summary>
|
||||
/// Decompressor for this filter
|
||||
/// </summary>
|
||||
protected Inflater inf;
|
||||
|
||||
/// <summary>
|
||||
/// Byte array used as a buffer
|
||||
/// </summary>
|
||||
protected byte[] buf;
|
||||
|
||||
/// <summary>
|
||||
/// Size of buffer
|
||||
/// </summary>
|
||||
protected int len;
|
||||
|
||||
//We just use this if we are decoding one byte at a time with the read() call
|
||||
private byte[] onebytebuffer = new byte[1];
|
||||
|
||||
/// <summary>
|
||||
/// base stream the inflater depends on.
|
||||
/// </summary>
|
||||
protected Stream baseInputStream;
|
||||
|
||||
protected long csize;
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override bool CanRead {
|
||||
get {
|
||||
return baseInputStream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override bool CanSeek {
|
||||
get {
|
||||
return false;
|
||||
// return baseInputStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override bool CanWrite {
|
||||
get {
|
||||
return baseInputStream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override long Length {
|
||||
get {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override long Position {
|
||||
get {
|
||||
return baseInputStream.Position;
|
||||
}
|
||||
set {
|
||||
baseInputStream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the baseInputStream
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
baseInputStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException("Seek not supported"); // -jr- 01-Dec-2003
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override void SetLength(long val)
|
||||
{
|
||||
baseInputStream.SetLength(val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override void Write(byte[] array, int offset, int count)
|
||||
{
|
||||
baseInputStream.Write(array, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I needed to implement the abstract member.
|
||||
/// </summary>
|
||||
public override void WriteByte(byte val)
|
||||
{
|
||||
baseInputStream.WriteByte(val);
|
||||
}
|
||||
|
||||
// -jr- 01-Dec-2003 This may be flawed for some base streams? Depends on implementation of BeginWrite
|
||||
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
throw new NotSupportedException("Asynch write not currently supported");
|
||||
}
|
||||
|
||||
//Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create an InflaterInputStream with the default decompresseor
|
||||
/// and a default buffer size.
|
||||
/// </summary>
|
||||
/// <param name = "baseInputStream">
|
||||
/// the InputStream to read bytes from
|
||||
/// </param>
|
||||
public InflaterInputStream(Stream baseInputStream) : this(baseInputStream, new Inflater(), 4096)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an InflaterInputStream with the specified decompresseor
|
||||
/// and a default buffer size.
|
||||
/// </summary>
|
||||
/// <param name = "baseInputStream">
|
||||
/// the InputStream to read bytes from
|
||||
/// </param>
|
||||
/// <param name = "inf">
|
||||
/// the decompressor used to decompress data read from baseInputStream
|
||||
/// </param>
|
||||
public InflaterInputStream(Stream baseInputStream, Inflater inf) : this(baseInputStream, inf, 4096)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an InflaterInputStream with the specified decompresseor
|
||||
/// and a specified buffer size.
|
||||
/// </summary>
|
||||
/// <param name = "baseInputStream">
|
||||
/// the InputStream to read bytes from
|
||||
/// </param>
|
||||
/// <param name = "inf">
|
||||
/// the decompressor used to decompress data read from baseInputStream
|
||||
/// </param>
|
||||
/// <param name = "size">
|
||||
/// size of the buffer to use
|
||||
/// </param>
|
||||
public InflaterInputStream(Stream baseInputStream, Inflater inf, int size)
|
||||
{
|
||||
this.baseInputStream = baseInputStream;
|
||||
this.inf = inf;
|
||||
try {
|
||||
this.len = (int)baseInputStream.Length;
|
||||
} catch (Exception) {
|
||||
// the stream may not support .Length
|
||||
this.len = 0;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
throw new ArgumentOutOfRangeException("size <= 0");
|
||||
}
|
||||
|
||||
buf = new byte[size]; //Create the buffer
|
||||
}
|
||||
|
||||
//Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns 0 once the end of the stream (EOF) has been reached.
|
||||
/// Otherwise returns 1.
|
||||
/// </summary>
|
||||
public virtual int Available {
|
||||
get {
|
||||
return inf.IsFinished ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the input stream
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
baseInputStream.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the buffer with more data to decompress.
|
||||
/// </summary>
|
||||
protected void Fill()
|
||||
{
|
||||
len = baseInputStream.Read(buf, 0, buf.Length);
|
||||
// decrypting crypted data
|
||||
if (cryptbuffer != null) {
|
||||
DecryptBlock(buf, 0, System.Math.Min((int)(csize - inf.TotalIn), buf.Length));
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
throw new ApplicationException("Deflated stream ends early.");
|
||||
}
|
||||
inf.SetInput(buf, 0, len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads one byte of decompressed data.
|
||||
///
|
||||
/// The byte is baseInputStream the lower 8 bits of the int.
|
||||
/// </summary>
|
||||
public override int ReadByte()
|
||||
{
|
||||
int nread = Read(onebytebuffer, 0, 1); //read one byte
|
||||
if (nread > 0) {
|
||||
return onebytebuffer[0] & 0xff;
|
||||
}
|
||||
return -1; // ok
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses data into the byte array
|
||||
/// </summary>
|
||||
/// <param name ="b">
|
||||
/// the array to read and decompress data into
|
||||
/// </param>
|
||||
/// <param name ="off">
|
||||
/// the offset indicating where the data should be placed
|
||||
/// </param>
|
||||
/// <param name ="len">
|
||||
/// the number of bytes to decompress
|
||||
/// </param>
|
||||
public override int Read(byte[] b, int off, int len)
|
||||
{
|
||||
for (;;) {
|
||||
int count;
|
||||
try {
|
||||
count = inf.Inflate(b, off, len);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e.ToString());
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
if (inf.IsNeedingDictionary) {
|
||||
throw new ZipException("Need a dictionary");
|
||||
} else if (inf.IsFinished) {
|
||||
return 0;
|
||||
} else if (inf.IsNeedingInput) {
|
||||
Fill();
|
||||
} else {
|
||||
throw new InvalidOperationException("Don't know what to do");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip specified number of bytes of uncompressed data
|
||||
/// </summary>
|
||||
/// <param name ="n">
|
||||
/// number of bytes to skip
|
||||
/// </param>
|
||||
public long Skip(long n)
|
||||
{
|
||||
if (n < 0) {
|
||||
throw new ArgumentOutOfRangeException("n");
|
||||
}
|
||||
int len = 2048;
|
||||
if (n < len) {
|
||||
len = (int) n;
|
||||
}
|
||||
byte[] tmp = new byte[len];
|
||||
return (long)baseInputStream.Read(tmp, 0, tmp.Length);
|
||||
}
|
||||
|
||||
#region Encryption stuff
|
||||
protected byte[] cryptbuffer = null;
|
||||
|
||||
uint[] keys = null;
|
||||
protected byte DecryptByte()
|
||||
{
|
||||
uint temp = ((keys[2] & 0xFFFF) | 2);
|
||||
return (byte)((temp * (temp ^ 1)) >> 8);
|
||||
}
|
||||
|
||||
protected void DecryptBlock(byte[] buf, int off, int len)
|
||||
{
|
||||
for (int i = off; i < off + len; ++i) {
|
||||
buf[i] ^= DecryptByte();
|
||||
UpdateKeys(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected void InitializePassword(string password)
|
||||
{
|
||||
keys = new uint[] {
|
||||
0x12345678,
|
||||
0x23456789,
|
||||
0x34567890
|
||||
};
|
||||
for (int i = 0; i < password.Length; ++i) {
|
||||
UpdateKeys((byte)password[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateKeys(byte ch)
|
||||
{
|
||||
keys[0] = Crc32.ComputeCrc32(keys[0], ch);
|
||||
keys[1] = keys[1] + (byte)keys[0];
|
||||
keys[1] = keys[1] * 134775813 + 1;
|
||||
keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
176
irc/TechBot/Compression/Streams/OutputWindow.cs
Normal file
176
irc/TechBot/Compression/Streams/OutputWindow.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
// OutputWindow.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Contains the output from the Inflation process.
|
||||
/// We need to have a window so that we can refer backwards into the output stream
|
||||
/// to repeat stuff.
|
||||
///
|
||||
/// author of the original java version : John Leuner
|
||||
/// </summary>
|
||||
public class OutputWindow
|
||||
{
|
||||
private static int WINDOW_SIZE = 1 << 15;
|
||||
private static int WINDOW_MASK = WINDOW_SIZE - 1;
|
||||
|
||||
private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
|
||||
private int windowEnd = 0;
|
||||
private int windowFilled = 0;
|
||||
|
||||
public void Write(int abyte)
|
||||
{
|
||||
if (windowFilled++ == WINDOW_SIZE) {
|
||||
throw new InvalidOperationException("Window full");
|
||||
}
|
||||
window[windowEnd++] = (byte) abyte;
|
||||
windowEnd &= WINDOW_MASK;
|
||||
}
|
||||
|
||||
|
||||
private void SlowRepeat(int repStart, int len, int dist)
|
||||
{
|
||||
while (len-- > 0) {
|
||||
window[windowEnd++] = window[repStart++];
|
||||
windowEnd &= WINDOW_MASK;
|
||||
repStart &= WINDOW_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
public void Repeat(int len, int dist)
|
||||
{
|
||||
if ((windowFilled += len) > WINDOW_SIZE) {
|
||||
throw new InvalidOperationException("Window full");
|
||||
}
|
||||
|
||||
int rep_start = (windowEnd - dist) & WINDOW_MASK;
|
||||
int border = WINDOW_SIZE - len;
|
||||
if (rep_start <= border && windowEnd < border) {
|
||||
if (len <= dist) {
|
||||
System.Array.Copy(window, rep_start, window, windowEnd, len);
|
||||
windowEnd += len;
|
||||
} else {
|
||||
/* We have to copy manually, since the repeat pattern overlaps.
|
||||
*/
|
||||
while (len-- > 0) {
|
||||
window[windowEnd++] = window[rep_start++];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SlowRepeat(rep_start, len, dist);
|
||||
}
|
||||
}
|
||||
|
||||
public int CopyStored(StreamManipulator input, int len)
|
||||
{
|
||||
len = Math.Min(Math.Min(len, WINDOW_SIZE - windowFilled), input.AvailableBytes);
|
||||
int copied;
|
||||
|
||||
int tailLen = WINDOW_SIZE - windowEnd;
|
||||
if (len > tailLen) {
|
||||
copied = input.CopyBytes(window, windowEnd, tailLen);
|
||||
if (copied == tailLen) {
|
||||
copied += input.CopyBytes(window, 0, len - tailLen);
|
||||
}
|
||||
} else {
|
||||
copied = input.CopyBytes(window, windowEnd, len);
|
||||
}
|
||||
|
||||
windowEnd = (windowEnd + copied) & WINDOW_MASK;
|
||||
windowFilled += copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
public void CopyDict(byte[] dict, int offset, int len)
|
||||
{
|
||||
if (windowFilled > 0) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (len > WINDOW_SIZE) {
|
||||
offset += len - WINDOW_SIZE;
|
||||
len = WINDOW_SIZE;
|
||||
}
|
||||
System.Array.Copy(dict, offset, window, 0, len);
|
||||
windowEnd = len & WINDOW_MASK;
|
||||
}
|
||||
|
||||
public int GetFreeSpace()
|
||||
{
|
||||
return WINDOW_SIZE - windowFilled;
|
||||
}
|
||||
|
||||
public int GetAvailable()
|
||||
{
|
||||
return windowFilled;
|
||||
}
|
||||
|
||||
public int CopyOutput(byte[] output, int offset, int len)
|
||||
{
|
||||
int copy_end = windowEnd;
|
||||
if (len > windowFilled) {
|
||||
len = windowFilled;
|
||||
} else {
|
||||
copy_end = (windowEnd - windowFilled + len) & WINDOW_MASK;
|
||||
}
|
||||
|
||||
int copied = len;
|
||||
int tailLen = len - copy_end;
|
||||
|
||||
if (tailLen > 0) {
|
||||
System.Array.Copy(window, WINDOW_SIZE - tailLen, output, offset, tailLen);
|
||||
offset += tailLen;
|
||||
len = copy_end;
|
||||
}
|
||||
System.Array.Copy(window, copy_end - len, output, offset, len);
|
||||
windowFilled -= copied;
|
||||
if (windowFilled < 0) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
windowFilled = windowEnd = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
245
irc/TechBot/Compression/Streams/StreamManipulator.cs
Normal file
245
irc/TechBot/Compression/Streams/StreamManipulator.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
// StreamManipulator.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class allows us to retrieve a specified amount of bits from
|
||||
/// the input buffer, as well as copy big byte blocks.
|
||||
///
|
||||
/// It uses an int buffer to store up to 31 bits for direct
|
||||
/// manipulation. This guarantees that we can get at least 16 bits,
|
||||
/// but we only need at most 15, so this is all safe.
|
||||
///
|
||||
/// There are some optimizations in this class, for example, you must
|
||||
/// never peek more then 8 bits more than needed, and you must first
|
||||
/// peek bits before you may drop them. This is not a general purpose
|
||||
/// class but optimized for the behaviour of the Inflater.
|
||||
///
|
||||
/// authors of the original java version : John Leuner, Jochen Hoenicke
|
||||
/// </summary>
|
||||
public class StreamManipulator
|
||||
{
|
||||
private byte[] window;
|
||||
private int window_start = 0;
|
||||
private int window_end = 0;
|
||||
|
||||
private uint buffer = 0;
|
||||
private int bits_in_buffer = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Get the next n bits but don't increase input pointer. n must be
|
||||
/// less or equal 16 and if you if this call succeeds, you must drop
|
||||
/// at least n-8 bits in the next call.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the value of the bits, or -1 if not enough bits available. */
|
||||
/// </returns>
|
||||
public int PeekBits(int n)
|
||||
{
|
||||
if (bits_in_buffer < n) {
|
||||
if (window_start == window_end) {
|
||||
return -1; // ok
|
||||
}
|
||||
buffer |= (uint)((window[window_start++] & 0xff |
|
||||
(window[window_start++] & 0xff) << 8) << bits_in_buffer);
|
||||
bits_in_buffer += 16;
|
||||
}
|
||||
return (int)(buffer & ((1 << n) - 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops the next n bits from the input. You should have called peekBits
|
||||
/// with a bigger or equal n before, to make sure that enough bits are in
|
||||
/// the bit buffer.
|
||||
/// </summary>
|
||||
public void DropBits(int n)
|
||||
{
|
||||
buffer >>= n;
|
||||
bits_in_buffer -= n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next n bits and increases input pointer. This is equivalent
|
||||
/// to peekBits followed by dropBits, except for correct error handling.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the value of the bits, or -1 if not enough bits available.
|
||||
/// </returns>
|
||||
public int GetBits(int n)
|
||||
{
|
||||
int bits = PeekBits(n);
|
||||
if (bits >= 0) {
|
||||
DropBits(n);
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bits available in the bit buffer. This must be
|
||||
/// only called when a previous peekBits() returned -1.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the number of bits available.
|
||||
/// </returns>
|
||||
public int AvailableBits {
|
||||
get {
|
||||
return bits_in_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes available.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the number of bytes available.
|
||||
/// </returns>
|
||||
public int AvailableBytes {
|
||||
get {
|
||||
return window_end - window_start + (bits_in_buffer >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips to the next byte boundary.
|
||||
/// </summary>
|
||||
public void SkipToByteBoundary()
|
||||
{
|
||||
buffer >>= (bits_in_buffer & 7);
|
||||
bits_in_buffer &= ~7;
|
||||
}
|
||||
|
||||
public bool IsNeedingInput {
|
||||
get {
|
||||
return window_start == window_end;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies length bytes from input buffer to output buffer starting
|
||||
/// at output[offset]. You have to make sure, that the buffer is
|
||||
/// byte aligned. If not enough bytes are available, copies fewer
|
||||
/// bytes.
|
||||
/// </summary>
|
||||
/// <param name="output">
|
||||
/// the buffer.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// the offset in the buffer.
|
||||
/// </param>
|
||||
/// <param name="length">
|
||||
/// the length to copy, 0 is allowed.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// the number of bytes copied, 0 if no byte is available.
|
||||
/// </returns>
|
||||
public int CopyBytes(byte[] output, int offset, int length)
|
||||
{
|
||||
if (length < 0) {
|
||||
throw new ArgumentOutOfRangeException("length negative");
|
||||
}
|
||||
if ((bits_in_buffer & 7) != 0) {
|
||||
/* bits_in_buffer may only be 0 or 8 */
|
||||
throw new InvalidOperationException("Bit buffer is not aligned!");
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
while (bits_in_buffer > 0 && length > 0) {
|
||||
output[offset++] = (byte) buffer;
|
||||
buffer >>= 8;
|
||||
bits_in_buffer -= 8;
|
||||
length--;
|
||||
count++;
|
||||
}
|
||||
if (length == 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
int avail = window_end - window_start;
|
||||
if (length > avail) {
|
||||
length = avail;
|
||||
}
|
||||
System.Array.Copy(window, window_start, output, offset, length);
|
||||
window_start += length;
|
||||
|
||||
if (((window_start - window_end) & 1) != 0) {
|
||||
/* We always want an even number of bytes in input, see peekBits */
|
||||
buffer = (uint)(window[window_start++] & 0xff);
|
||||
bits_in_buffer = 8;
|
||||
}
|
||||
return count + length;
|
||||
}
|
||||
|
||||
public StreamManipulator()
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
buffer = (uint)(window_start = window_end = bits_in_buffer = 0);
|
||||
}
|
||||
|
||||
public void SetInput(byte[] buf, int off, int len)
|
||||
{
|
||||
if (window_start < window_end) {
|
||||
throw new InvalidOperationException("Old input was not completely processed");
|
||||
}
|
||||
|
||||
int end = off + len;
|
||||
|
||||
/* We want to throw an ArrayIndexOutOfBoundsException early. The
|
||||
* check is very tricky: it also handles integer wrap around.
|
||||
*/
|
||||
if (0 > off || off > end || end > buf.Length) {
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if ((len & 1) != 0) {
|
||||
/* We always want an even number of bytes in input, see peekBits */
|
||||
buffer |= (uint)((buf[off++] & 0xff) << bits_in_buffer);
|
||||
bits_in_buffer += 8;
|
||||
}
|
||||
|
||||
window = buf;
|
||||
window_start = off;
|
||||
window_end = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
irc/TechBot/Compression/ZipException.cs
Normal file
62
irc/TechBot/Compression/ZipException.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// ZipException.cs
|
||||
// Copyright (C) 2001 Mike Krueger
|
||||
//
|
||||
// This file was translated from java, it was part of the GNU Classpath
|
||||
// Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
// Linking this library statically or dynamically with other modules is
|
||||
// making a combined work based on this library. Thus, the terms and
|
||||
// conditions of the GNU General Public License cover the whole
|
||||
// combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of this library give you
|
||||
// permission to link this library with independent modules to produce an
|
||||
// executable, regardless of the license terms of these independent
|
||||
// modules, and to copy and distribute the resulting executable under
|
||||
// terms of your choice, provided that you also meet, for each linked
|
||||
// independent module, the terms and conditions of the license of that
|
||||
// module. An independent module is a module which is not derived from
|
||||
// or based on this library. If you modify this library, you may extend
|
||||
// this exception to your version of the library, but you are not
|
||||
// obligated to do so. If you do not wish to do so, delete this
|
||||
// exception statement from your version.
|
||||
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.SharpZipLib
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Is thrown during the creation or input of a zip file.
|
||||
/// </summary>
|
||||
public class ZipException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ZipException class with default properties.
|
||||
/// </summary>
|
||||
public ZipException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ZipException class with a specified error message.
|
||||
/// </summary>
|
||||
public ZipException(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
14
irc/TechBot/Default.build
Normal file
14
irc/TechBot/Default.build
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="TechBot" default="build">
|
||||
|
||||
<target name="build" description="Build components">
|
||||
<delete dir="bin" failonerror="false" />
|
||||
<nant buildfile="Compression/Default.build" />
|
||||
<nant buildfile="CHMLibrary/Default.build" />
|
||||
<nant buildfile="TechBot.IRCLibrary/Default.build" />
|
||||
<nant buildfile="TechBot.Library/Default.build" />
|
||||
<nant buildfile="TechBot.Console/Default.build" />
|
||||
<nant buildfile="TechBot/Default.build" />
|
||||
</target>
|
||||
|
||||
</project>
|
||||
15
irc/TechBot/TechBot.Console/App.config
Normal file
15
irc/TechBot/TechBot.Console/App.config
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="IRCServerHostName" value="irc.eu.freenode.net" />
|
||||
<add key="IRCServerHostPort" value="6667" />
|
||||
<add key="IRCChannelName" value="channel" />
|
||||
<add key="IRCBotName" value="MyBot" />
|
||||
<add key="ChmPath" value="C:\IRC\TechBot\CHM" />
|
||||
<add key="MainChm" value="kmarch.chm" />
|
||||
<add key="NtstatusXml" value="C:\IRC\TechBot\ntstatus.xml" />
|
||||
<add key="WinerrorXml" value="C:\IRC\TechBot\winerror.xml" />
|
||||
<add key="HresultXml" value="C:\IRC\TechBot\hresult.xml" />
|
||||
<add key="SvnCommand" value="svn co svn://svn.reactos.com/trunk/reactos" />
|
||||
</appSettings>
|
||||
</configuration>
|
||||
32
irc/TechBot/TechBot.Console/AssemblyInfo.cs
Normal file
32
irc/TechBot/TechBot.Console/AssemblyInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
23
irc/TechBot/TechBot.Console/Default.build
Normal file
23
irc/TechBot/TechBot.Console/Default.build
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="TechBot.Console" default="build">
|
||||
|
||||
<property name="output.dir" value="..\bin" />
|
||||
|
||||
<target name="build" description="Build component">
|
||||
<mkdir dir="${output.dir}" />
|
||||
<csc target="exe"
|
||||
output="${output.dir}\TechBot.Console.exe"
|
||||
optimize="true"
|
||||
debug="true"
|
||||
doc="${output.dir}\TechBot.Console.xml"
|
||||
warninglevel="0">
|
||||
<sources>
|
||||
<include name="*.cs" />
|
||||
</sources>
|
||||
<references>
|
||||
<include name="${output.dir}\TechBot.Library.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
187
irc/TechBot/TechBot.Console/Main.cs
Normal file
187
irc/TechBot/TechBot.Console/Main.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using TechBot.Library;
|
||||
|
||||
namespace TechBot.Console
|
||||
{
|
||||
public class ConsoleServiceOutput : IServiceOutput
|
||||
{
|
||||
public void WriteLine(string message)
|
||||
{
|
||||
System.Console.WriteLine(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MainClass
|
||||
{
|
||||
private static void VerifyRequiredOption(string optionName,
|
||||
string optionValue)
|
||||
{
|
||||
if (optionValue == null)
|
||||
{
|
||||
throw new Exception(String.Format("Option '{0}' not set.",
|
||||
optionName));
|
||||
}
|
||||
}
|
||||
|
||||
private static string IRCServerHostName
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "IRCServerHostName";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static int IRCServerHostPort
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "IRCServerHostPort";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return Int32.Parse(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static string IRCChannelName
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "IRCChannelName";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string IRCBotName
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "IRCBotName";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ChmPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "ChmPath";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string MainChm
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "MainChm";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NtstatusXml
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "NtstatusXml";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string WinerrorXml
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "WinerrorXml";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string HresultXml
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "HresultXml";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static string SvnCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
string optionName = "SvnCommand";
|
||||
string s = ConfigurationSettings.AppSettings[optionName];
|
||||
VerifyRequiredOption(optionName,
|
||||
s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunIrcService()
|
||||
{
|
||||
IrcService ircService = new IrcService(IRCServerHostName,
|
||||
IRCServerHostPort,
|
||||
IRCChannelName,
|
||||
IRCBotName,
|
||||
ChmPath,
|
||||
MainChm,
|
||||
NtstatusXml,
|
||||
WinerrorXml,
|
||||
HresultXml,
|
||||
SvnCommand);
|
||||
ircService.Run();
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && args[0].ToLower().Equals("irc"))
|
||||
{
|
||||
RunIrcService();
|
||||
return;
|
||||
}
|
||||
|
||||
System.Console.WriteLine("TechBot running console service...");
|
||||
TechBotService service = new TechBotService(new ConsoleServiceOutput(),
|
||||
ChmPath,
|
||||
MainChm,
|
||||
NtstatusXml,
|
||||
WinerrorXml,
|
||||
HresultXml,
|
||||
SvnCommand);
|
||||
service.Run();
|
||||
while (true)
|
||||
{
|
||||
string s = System.Console.ReadLine();
|
||||
service.InjectMessage(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
irc/TechBot/TechBot.Console/TechBot.Console.cmbx
Normal file
16
irc/TechBot/TechBot.Console/TechBot.Console.cmbx
Normal file
@@ -0,0 +1,16 @@
|
||||
<Combine fileversion="1.0" name="TechBot.Console" description="">
|
||||
<StartMode startupentry="TechBot.Console" single="True">
|
||||
<Execute entry="TechBot.Console" type="None" />
|
||||
</StartMode>
|
||||
<Entries>
|
||||
<Entry filename=".\.\TechBot.Console.prjx" />
|
||||
</Entries>
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release">
|
||||
<Entry name="TechBot.Console" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug">
|
||||
<Entry name="TechBot.Console" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Combine>
|
||||
29
irc/TechBot/TechBot.Console/TechBot.Console.prjx
Normal file
29
irc/TechBot/TechBot.Console/TechBot.Console.prjx
Normal file
@@ -0,0 +1,29 @@
|
||||
<Project name="TechBot.Console" standardNamespace="TechBot.Console" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
|
||||
<Contents>
|
||||
<File name=".\Main.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
<File name=".\App.config" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
</Contents>
|
||||
<References>
|
||||
<Reference type="Project" refto="TechBot.Library" localcopy="True" />
|
||||
</References>
|
||||
<DeploymentInformation target="" script="" strategy="File" />
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="True" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot.Console" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configurations active="Debug">
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="True" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot.Console" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configuration runwithwarnings="True" name="Release">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="True" />
|
||||
<Output directory="..\bin\Release" assembly="TechBot.Console" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Project>
|
||||
32
irc/TechBot/TechBot.IRCLibrary/AssemblyInfo.cs
Normal file
32
irc/TechBot/TechBot.IRCLibrary/AssemblyInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
20
irc/TechBot/TechBot.IRCLibrary/Default.build
Normal file
20
irc/TechBot/TechBot.IRCLibrary/Default.build
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="TechBot.IRCLibrary" default="build">
|
||||
|
||||
<property name="output.dir" value="..\bin" />
|
||||
|
||||
<target name="build" description="Build component">
|
||||
<mkdir dir="${output.dir}" />
|
||||
<csc target="library"
|
||||
output="${output.dir}\TechBot.IRCLibrary.dll"
|
||||
optimize="true"
|
||||
debug="true"
|
||||
doc="${output.dir}\TechBot.IRCLibrary.xml"
|
||||
warninglevel="0">
|
||||
<sources>
|
||||
<include name="*.cs" />
|
||||
</sources>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
26
irc/TechBot/TechBot.IRCLibrary/IRC.cs
Normal file
26
irc/TechBot/TechBot.IRCLibrary/IRC.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// IRC constants and helper methods.
|
||||
/// </summary>
|
||||
public class IRC
|
||||
{
|
||||
#region IRC commands
|
||||
|
||||
public const string JOIN = "JOIN";
|
||||
public const string NICK = "NICK";
|
||||
public const string PART = "PART";
|
||||
public const string PING = "PING";
|
||||
public const string PONG = "PONG";
|
||||
public const string PRIVMSG = "PRIVMSG";
|
||||
public const string USER = "USER";
|
||||
|
||||
public const string RPL_NAMREPLY = "353";
|
||||
public const string RPL_ENDOFNAMES = "366";
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
134
irc/TechBot/TechBot.IRCLibrary/IrcChannel.cs
Normal file
134
irc/TechBot/TechBot.IRCLibrary/IrcChannel.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
Channels names are strings (beginning with a '&' or '#' character) of
|
||||
length up to 200 characters. Apart from the the requirement that the
|
||||
first character being either '&' or '#'; the only restriction on a
|
||||
channel name is that it may not contain any spaces (' '), a control G
|
||||
(^G or ASCII 7), or a comma (',' which is used as a list item
|
||||
separator by the protocol).
|
||||
*/
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// IRC channel type.
|
||||
/// </summary>
|
||||
public enum IrcChannelType
|
||||
{
|
||||
Public,
|
||||
Private,
|
||||
Secret
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// IRC channel.
|
||||
/// </summary>
|
||||
public class IrcChannel
|
||||
{
|
||||
#region Private fields
|
||||
|
||||
private IrcClient owner;
|
||||
private string name;
|
||||
private IrcChannelType type = IrcChannelType.Public;
|
||||
private ArrayList users = new ArrayList();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Owner of this channel.
|
||||
/// </summary>
|
||||
public IrcClient Owner
|
||||
{
|
||||
get
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of channel (no leading #).
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of channel.
|
||||
/// </summary>
|
||||
public IrcChannelType Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Users in this channel.
|
||||
/// </summary>
|
||||
public ArrayList Users
|
||||
{
|
||||
get
|
||||
{
|
||||
return users;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="owner">Owner of this channel.</param>
|
||||
/// <param name="name">Name of channel.</param>
|
||||
public IrcChannel(IrcClient owner, string name)
|
||||
{
|
||||
if (owner == null)
|
||||
{
|
||||
throw new ArgumentNullException("owner", "Owner cannot be null.");
|
||||
}
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException("name", "Name cannot be null.");
|
||||
}
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locate a user.
|
||||
/// </summary>
|
||||
/// <param name="nickname">Nickname of user (no decorations).</param>
|
||||
/// <returns>User or null if not found.</returns>
|
||||
public IrcUser LocateUser(string nickname)
|
||||
{
|
||||
foreach (IrcUser user in Users)
|
||||
{
|
||||
/* FIXME: There are special cases for nickname comparison */
|
||||
if (nickname.ToLower().Equals(user.Nickname.ToLower()))
|
||||
{
|
||||
return user;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Talk to the channel.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to send to the channel.</param>
|
||||
public void Talk(string text)
|
||||
{
|
||||
owner.SendMessage(new IrcMessage(IRC.PRIVMSG, String.Format("#{0} :{1}", name, text)));
|
||||
}
|
||||
}
|
||||
}
|
||||
654
irc/TechBot/TechBot.IRCLibrary/IrcClient.cs
Normal file
654
irc/TechBot/TechBot.IRCLibrary/IrcClient.cs
Normal file
@@ -0,0 +1,654 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate that delivers an IRC message.
|
||||
/// </summary>
|
||||
public delegate void MessageReceivedHandler(IrcMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate that notifies if the user database for a channel has changed.
|
||||
/// </summary>
|
||||
public delegate void ChannelUserDatabaseChangedHandler(IrcChannel channel);
|
||||
|
||||
/// <summary>
|
||||
/// An IRC client.
|
||||
/// </summary>
|
||||
public class IrcClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Monitor when an IRC command is received.
|
||||
/// </summary>
|
||||
private class IrcCommandEventRegistration
|
||||
{
|
||||
/// <summary>
|
||||
/// IRC command to monitor.
|
||||
/// </summary>
|
||||
private string command;
|
||||
public string Command
|
||||
{
|
||||
get
|
||||
{
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler to call when command is received.
|
||||
/// </summary>
|
||||
private MessageReceivedHandler handler;
|
||||
public MessageReceivedHandler Handler
|
||||
{
|
||||
get
|
||||
{
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="command">IRC command to monitor.</param>
|
||||
/// <param name="handler">Handler to call when command is received.</param>
|
||||
public IrcCommandEventRegistration(string command,
|
||||
MessageReceivedHandler handler)
|
||||
{
|
||||
this.command = command;
|
||||
this.handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A buffer to store lines of text.
|
||||
/// </summary>
|
||||
private class LineBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Full lines of text in buffer.
|
||||
/// </summary>
|
||||
private ArrayList strings;
|
||||
|
||||
/// <summary>
|
||||
/// Part of the last line of text in buffer.
|
||||
/// </summary>
|
||||
private string left = "";
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor.
|
||||
/// </summary>
|
||||
public LineBuffer()
|
||||
{
|
||||
strings = new ArrayList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if there is a complete line in the buffer or false if there is not.
|
||||
/// </summary>
|
||||
public bool DataAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
return (strings.Count > 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return next complete line in buffer or null if none exists.
|
||||
/// </summary>
|
||||
/// <returns>Next complete line in buffer or null if none exists.</returns>
|
||||
public string Read()
|
||||
{
|
||||
if (DataAvailable)
|
||||
{
|
||||
string line = strings[0] as string;
|
||||
strings.RemoveAt(0);
|
||||
return line;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a string to buffer splitting it into lines.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void Write(string data)
|
||||
{
|
||||
data = left + data;
|
||||
left = "";
|
||||
string[] sa = data.Split(new char[] { '\n' });
|
||||
if (sa.Length <= 0)
|
||||
{
|
||||
left = data;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = "";
|
||||
}
|
||||
for (int i = 0; i < sa.Length; i++)
|
||||
{
|
||||
if (i < sa.Length - 1)
|
||||
{
|
||||
/* This is a complete line. Remove any \r characters at the end of the line. */
|
||||
string line = sa[i].TrimEnd(new char[] { '\r', '\n'});
|
||||
/* Silently ignore empty lines */
|
||||
if (!line.Equals(String.Empty))
|
||||
{
|
||||
strings.Add(line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This may be a partial line. */
|
||||
left = sa[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// State for asynchronous reads.
|
||||
/// </summary>
|
||||
private class StateObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Network stream where data is read from.
|
||||
/// </summary>
|
||||
public NetworkStream Stream;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer where data is put.
|
||||
/// </summary>
|
||||
public byte[] Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="stream">Network stream where data is read from.</param>
|
||||
/// <param name="buffer">Buffer where data is put.</param>
|
||||
public StateObject(NetworkStream stream, byte[] buffer)
|
||||
{
|
||||
this.Stream = stream;
|
||||
this.Buffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Private fields
|
||||
private bool firstPingReceived = false;
|
||||
private System.Text.Encoding encoding = System.Text.Encoding.UTF8;
|
||||
private TcpClient tcpClient;
|
||||
private NetworkStream networkStream;
|
||||
private bool connected = false;
|
||||
private LineBuffer messageStream;
|
||||
private ArrayList ircCommandEventRegistrations = new ArrayList();
|
||||
private ArrayList channels = new ArrayList();
|
||||
#endregion
|
||||
|
||||
#region Public events
|
||||
|
||||
public event MessageReceivedHandler MessageReceived;
|
||||
|
||||
public event ChannelUserDatabaseChangedHandler ChannelUserDatabaseChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Encoding used.
|
||||
/// </summary>
|
||||
public System.Text.Encoding Encoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return encoding;
|
||||
}
|
||||
set
|
||||
{
|
||||
encoding = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of joined channels.
|
||||
/// </summary>
|
||||
public ArrayList Channels
|
||||
{
|
||||
get
|
||||
{
|
||||
return channels;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
/// <summary>
|
||||
/// Signal MessageReceived event.
|
||||
/// </summary>
|
||||
/// <param name="message">Message that was received.</param>
|
||||
private void OnMessageReceived(IrcMessage message)
|
||||
{
|
||||
foreach (IrcCommandEventRegistration icre in ircCommandEventRegistrations)
|
||||
{
|
||||
if (message.Command.ToLower().Equals(icre.Command.ToLower()))
|
||||
{
|
||||
icre.Handler(message);
|
||||
}
|
||||
}
|
||||
if (MessageReceived != null)
|
||||
{
|
||||
MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signal ChannelUserDatabaseChanged event.
|
||||
/// </summary>
|
||||
/// <param name="channel">Message that was received.</param>
|
||||
private void OnChannelUserDatabaseChanged(IrcChannel channel)
|
||||
{
|
||||
if (ChannelUserDatabaseChanged != null)
|
||||
{
|
||||
ChannelUserDatabaseChanged(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start an asynchronous read.
|
||||
/// </summary>
|
||||
private void Receive()
|
||||
{
|
||||
if ((networkStream != null) && (networkStream.CanRead))
|
||||
{
|
||||
byte[] buffer = new byte[1024];
|
||||
networkStream.BeginRead(buffer, 0, buffer.Length,
|
||||
new AsyncCallback(ReadComplete),
|
||||
new StateObject(networkStream, buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Socket is closed.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous read has completed.
|
||||
/// </summary>
|
||||
/// <param name="ar">IAsyncResult object.</param>
|
||||
private void ReadComplete(IAsyncResult ar)
|
||||
{
|
||||
StateObject stateObject = (StateObject) ar.AsyncState;
|
||||
if (stateObject.Stream.CanRead)
|
||||
{
|
||||
int bytesReceived = stateObject.Stream.EndRead(ar);
|
||||
if (bytesReceived > 0)
|
||||
{
|
||||
messageStream.Write(Encoding.GetString(stateObject.Buffer, 0, bytesReceived));
|
||||
while (messageStream.DataAvailable)
|
||||
{
|
||||
OnMessageReceived(new IrcMessage(messageStream.Read()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Receive();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locate channel.
|
||||
/// </summary>
|
||||
/// <param name="name">Channel name.</param>
|
||||
/// <returns>Channel or null if none was found.</returns>
|
||||
private IrcChannel LocateChannel(string name)
|
||||
{
|
||||
foreach (IrcChannel channel in Channels)
|
||||
{
|
||||
if (name.ToLower().Equals(channel.Name.ToLower()))
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a PONG message when a PING message is received.
|
||||
/// </summary>
|
||||
/// <param name="message">Received IRC message.</param>
|
||||
private void PingMessageReceived(IrcMessage message)
|
||||
{
|
||||
SendMessage(new IrcMessage(IRC.PONG, message.Parameters));
|
||||
firstPingReceived = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process RPL_NAMREPLY message.
|
||||
/// </summary>
|
||||
/// <param name="message">Received IRC message.</param>
|
||||
private void RPL_NAMREPLYMessageReceived(IrcMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
// :Oslo2.NO.EU.undernet.org 353 E101 = #E101 :E101 KongFu_uK @Exception
|
||||
/* "( "=" / "*" / "@" ) <channel>
|
||||
:[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
|
||||
- "@" is used for secret channels, "*" for private
|
||||
channels, and "=" for others (public channels). */
|
||||
if (message.Parameters == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("Message has no parameters."));
|
||||
return;
|
||||
}
|
||||
string[] parameters = message.Parameters.Split(new char[] { ' '});
|
||||
if (parameters.Length < 5)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("{0} is two few parameters.", parameters.Length));
|
||||
return;
|
||||
}
|
||||
IrcChannelType type;
|
||||
switch (parameters[1])
|
||||
{
|
||||
case "=":
|
||||
type = IrcChannelType.Public;
|
||||
break;
|
||||
case "*":
|
||||
type = IrcChannelType.Private;
|
||||
break;
|
||||
case "@":
|
||||
type = IrcChannelType.Secret;
|
||||
break;
|
||||
default:
|
||||
type = IrcChannelType.Public;
|
||||
break;
|
||||
}
|
||||
IrcChannel channel = LocateChannel(parameters[2].Substring(1));
|
||||
if (channel == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("Channel not found '{0}'.",
|
||||
parameters[2].Substring(1)));
|
||||
return;
|
||||
}
|
||||
string nickname = parameters[3];
|
||||
if (nickname[0] != ':')
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("String should start with : and not {0}.", nickname[0]));
|
||||
return;
|
||||
}
|
||||
/* Skip : */
|
||||
IrcUser user = channel.LocateUser(nickname.Substring(1));
|
||||
if (user == null)
|
||||
{
|
||||
user = new IrcUser(nickname.Substring(1));
|
||||
channel.Users.Add(user);
|
||||
}
|
||||
for (int i = 4; i < parameters.Length; i++)
|
||||
{
|
||||
nickname = parameters[i];
|
||||
user = channel.LocateUser(nickname);
|
||||
if (user == null)
|
||||
{
|
||||
user = new IrcUser(nickname);
|
||||
channel.Users.Add(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("Ex. {0}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process RPL_ENDOFNAMES message.
|
||||
/// </summary>
|
||||
/// <param name="message">Received IRC message.</param>
|
||||
private void RPL_ENDOFNAMESMessageReceived(IrcMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
/* <channel> :End of NAMES list */
|
||||
if (message.Parameters == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("Message has no parameters."));
|
||||
return;
|
||||
}
|
||||
|
||||
string[] parameters = message.Parameters.Split(new char[] { ' ' });
|
||||
IrcChannel channel = LocateChannel(parameters[1].Substring(1));
|
||||
if (channel == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("Channel not found '{0}'.",
|
||||
parameters[0].Substring(1)));
|
||||
return;
|
||||
}
|
||||
|
||||
OnChannelUserDatabaseChanged(channel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("Ex. {0}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Connect to the specified IRC server on the specified port.
|
||||
/// </summary>
|
||||
/// <param name="server">Address of IRC server.</param>
|
||||
/// <param name="port">Port of IRC server.</param>
|
||||
public void Connect(string server, int port)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
throw new AlreadyConnectedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
messageStream = new LineBuffer();
|
||||
tcpClient = new TcpClient();
|
||||
tcpClient.Connect(server, port);
|
||||
tcpClient.NoDelay = true;
|
||||
tcpClient.LingerState = new LingerOption(false, 0);
|
||||
networkStream = tcpClient.GetStream();
|
||||
connected = networkStream.CanRead && networkStream.CanWrite;
|
||||
if (!connected)
|
||||
{
|
||||
throw new Exception("Cannot read and write from socket.");
|
||||
}
|
||||
/* Install PING message handler */
|
||||
MonitorCommand(IRC.PING, new MessageReceivedHandler(PingMessageReceived));
|
||||
/* Install RPL_NAMREPLY message handler */
|
||||
MonitorCommand(IRC.RPL_NAMREPLY, new MessageReceivedHandler(RPL_NAMREPLYMessageReceived));
|
||||
/* Install RPL_ENDOFNAMES message handler */
|
||||
MonitorCommand(IRC.RPL_ENDOFNAMES, new MessageReceivedHandler(RPL_ENDOFNAMESMessageReceived));
|
||||
/* Start receiving data */
|
||||
Receive();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from IRC server.
|
||||
/// </summary>
|
||||
public void Diconnect()
|
||||
{
|
||||
if (!connected)
|
||||
{
|
||||
throw new NotConnectedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
connected = false;
|
||||
tcpClient.Close();
|
||||
tcpClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send an IRC message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be sent.</param>
|
||||
public void SendMessage(IrcMessage message)
|
||||
{
|
||||
if (!connected)
|
||||
{
|
||||
throw new NotConnectedException();
|
||||
}
|
||||
|
||||
/* Serialize sending messages */
|
||||
lock (typeof(IrcClient))
|
||||
{
|
||||
NetworkStream networkStream = tcpClient.GetStream();
|
||||
byte[] bytes = Encoding.GetBytes(message.Line);
|
||||
networkStream.Write(bytes, 0, bytes.Length);
|
||||
networkStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monitor when a message with an IRC command is received.
|
||||
/// </summary>
|
||||
/// <param name="command">IRC command to monitor.</param>
|
||||
/// <param name="handler">Handler to call when command is received.</param>
|
||||
public void MonitorCommand(string command, MessageReceivedHandler handler)
|
||||
{
|
||||
if (command == null)
|
||||
{
|
||||
throw new ArgumentNullException("command", "Command cannot be null.");
|
||||
}
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ArgumentNullException("handler", "Handler cannot be null.");
|
||||
}
|
||||
ircCommandEventRegistrations.Add(new IrcCommandEventRegistration(command, handler));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Talk to the channel.
|
||||
/// </summary>
|
||||
/// <param name="nickname">Nickname of user to talk to.</param>
|
||||
/// <param name="text">Text to send to the channel.</param>
|
||||
public void TalkTo(string nickname, string text)
|
||||
{
|
||||
if (nickname == null)
|
||||
{
|
||||
throw new ArgumentNullException("nickname", "Nickname cannot be null.");
|
||||
}
|
||||
if (text == null)
|
||||
{
|
||||
throw new ArgumentNullException("text", "Text cannot be null.");
|
||||
}
|
||||
|
||||
SendMessage(new IrcMessage(IRC.PRIVMSG, String.Format("{0} :{1}", nickname, text)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change nickname.
|
||||
/// </summary>
|
||||
/// <param name="nickname">New nickname.</param>
|
||||
public void ChangeNick(string nickname)
|
||||
{
|
||||
if (nickname == null)
|
||||
{
|
||||
throw new ArgumentNullException("nickname", "Nickname cannot be null.");
|
||||
}
|
||||
|
||||
/* NICK <nickname> [ <hopcount> ] */
|
||||
SendMessage(new IrcMessage(IRC.NICK, nickname));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register.
|
||||
/// </summary>
|
||||
/// <param name="nickname">New nickname.</param>
|
||||
/// <param name="realname">Real name. Can be null.</param>
|
||||
public void Register(string nickname, string realname)
|
||||
{
|
||||
if (nickname == null)
|
||||
{
|
||||
throw new ArgumentNullException("nickname", "Nickname cannot be null.");
|
||||
}
|
||||
firstPingReceived = false;
|
||||
ChangeNick(nickname);
|
||||
/* OLD: USER <username> <hostname> <servername> <realname> */
|
||||
/* NEW: USER <user> <mode> <unused> <realname> */
|
||||
SendMessage(new IrcMessage(IRC.USER, String.Format("{0} 0 * :{1}",
|
||||
nickname, realname != null ? realname : "Anonymous")));
|
||||
|
||||
/* Wait for PING for up til 10 seconds */
|
||||
int timer = 0;
|
||||
while (!firstPingReceived && timer < 200)
|
||||
{
|
||||
System.Threading.Thread.Sleep(50);
|
||||
timer++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Join an IRC channel.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of channel (without leading #).</param>
|
||||
/// <returns>New channel.</returns>
|
||||
public IrcChannel JoinChannel(string name)
|
||||
{
|
||||
IrcChannel channel = new IrcChannel(this, name);
|
||||
channels.Add(channel);
|
||||
/* JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0" */
|
||||
SendMessage(new IrcMessage(IRC.JOIN, String.Format("#{0}", name)));
|
||||
return channel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Part an IRC channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">IRC channel. If null, the user parts from all channels.</param>
|
||||
/// <param name="message">Part message. Can be null.</param>
|
||||
public void PartChannel(IrcChannel channel, string message)
|
||||
{
|
||||
/* PART <channel> *( "," <channel> ) [ <Part Message> ] */
|
||||
if (channel != null)
|
||||
{
|
||||
SendMessage(new IrcMessage(IRC.PART, String.Format("#{0}{1}",
|
||||
channel.Name, message != null ? String.Format(" :{0}", message) : "")));
|
||||
channels.Remove(channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
string channelList = null;
|
||||
foreach (IrcChannel myChannel in Channels)
|
||||
{
|
||||
if (channelList == null)
|
||||
{
|
||||
channelList = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
channelList += ",";
|
||||
}
|
||||
channelList += myChannel.Name;
|
||||
}
|
||||
if (channelList != null)
|
||||
{
|
||||
SendMessage(new IrcMessage(IRC.PART, String.Format("#{0}{1}",
|
||||
channelList, message != null ? String.Format(" :{0}", message) : "")));
|
||||
Channels.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
irc/TechBot/TechBot.IRCLibrary/IrcException.cs
Normal file
50
irc/TechBot/TechBot.IRCLibrary/IrcException.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all IRC exceptions.
|
||||
/// </summary>
|
||||
public class IrcException : Exception
|
||||
{
|
||||
public IrcException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public IrcException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public IrcException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown when there is no connection to an IRC server.
|
||||
/// </summary>
|
||||
public class NotConnectedException : IrcException
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown when there is an attempt to connect to an IRC server and there is already a connection.
|
||||
/// </summary>
|
||||
public class AlreadyConnectedException : IrcException
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown when there is attempted to parse a malformed or invalid IRC message.
|
||||
/// </summary>
|
||||
public class MalformedMessageException : IrcException
|
||||
{
|
||||
public MalformedMessageException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public MalformedMessageException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
503
irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs
Normal file
503
irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs
Normal file
@@ -0,0 +1,503 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/*
|
||||
<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
|
||||
<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
|
||||
<command> ::= <letter> { <letter> } | <number> <number> <number>
|
||||
<SPACE> ::= ' ' { ' ' }
|
||||
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
|
||||
|
||||
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
|
||||
or NUL or CR or LF, the first of which may not be ':'>
|
||||
<trailing> ::= <Any, possibly *empty*, sequence of octets not including
|
||||
NUL or CR or LF>
|
||||
|
||||
<crlf> ::= CR LF
|
||||
|
||||
NOTES:
|
||||
|
||||
1) <SPACE> is consists only of SPACE character(s) (0x20).
|
||||
Specially notice that TABULATION, and all other control
|
||||
characters are considered NON-WHITE-SPACE.
|
||||
|
||||
2) After extracting the parameter list, all parameters are equal,
|
||||
whether matched by <middle> or <trailing>. <Trailing> is just
|
||||
a syntactic trick to allow SPACE within parameter.
|
||||
|
||||
3) The fact that CR and LF cannot appear in parameter strings is
|
||||
just artifact of the message framing. This might change later.
|
||||
|
||||
4) The NUL character is not special in message framing, and
|
||||
basically could end up inside a parameter, but as it would
|
||||
cause extra complexities in normal C string handling. Therefore
|
||||
NUL is not allowed within messages.
|
||||
|
||||
5) The last parameter may be an empty string.
|
||||
|
||||
6) Use of the extended prefix (['!' <user> ] ['@' <host> ]) must
|
||||
not be used in server to server communications and is only
|
||||
intended for server to client messages in order to provide
|
||||
clients with more useful information about who a message is
|
||||
from without the need for additional queries.
|
||||
*/
|
||||
/*
|
||||
NOTICE AUTH :*** Looking up your hostname
|
||||
NOTICE AUTH :*** Checking Ident
|
||||
NOTICE AUTH :*** Found your hostname
|
||||
NOTICE AUTH :*** No ident response
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// IRC message.
|
||||
/// </summary>
|
||||
public class IrcMessage
|
||||
{
|
||||
#region Private fields
|
||||
private string line;
|
||||
private string prefix;
|
||||
private string prefixServername;
|
||||
private string prefixNickname;
|
||||
private string prefixUser;
|
||||
private string prefixHost;
|
||||
private string command;
|
||||
private string parameters;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Line of text that is to be parsed as an IRC message.
|
||||
/// </summary>
|
||||
public string Line
|
||||
{
|
||||
get
|
||||
{
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the message have a prefix?
|
||||
/// </summary>
|
||||
public bool HasPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefix != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix or null if none exists.
|
||||
/// </summary>
|
||||
public string Prefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Servername part of prefix or null if no prefix or servername exists.
|
||||
/// </summary>
|
||||
public string PrefixServername
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixServername;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nickname part of prefix or null if no prefix or nick exists.
|
||||
/// </summary>
|
||||
public string PrefixNickname
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixNickname;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User part of (extended) prefix or null if no (extended) prefix exists.
|
||||
/// </summary>
|
||||
public string PrefixUser
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixUser;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Host part of (extended) prefix or null if no (extended) prefix exists.
|
||||
/// </summary>
|
||||
public string PrefixHost
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixHost;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command part of message.
|
||||
/// </summary>
|
||||
public string Command
|
||||
{
|
||||
get
|
||||
{
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is command numeric?
|
||||
/// </summary>
|
||||
public bool IsCommandNumeric
|
||||
{
|
||||
get
|
||||
{
|
||||
if (command == null || command.Length != 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
Int32.Parse(command);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command part of message as text.
|
||||
/// </summary>
|
||||
public string CommandText
|
||||
{
|
||||
get
|
||||
{
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command part of message as a number.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if IsCommandNumeric returns false.</exception>
|
||||
public int CommandNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsCommandNumeric)
|
||||
{
|
||||
return Int32.Parse(command);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters part of message.
|
||||
/// </summary>
|
||||
public string Parameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="line">Line of text that is to be parsed as an IRC message.</param>
|
||||
public IrcMessage(string line)
|
||||
{
|
||||
/*
|
||||
* <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
|
||||
* <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
|
||||
* :Oslo1.NO.EU.undernet.org 461 MYNICK USER :Not enough parameters
|
||||
*/
|
||||
try
|
||||
{
|
||||
this.line = line;
|
||||
int i = 0;
|
||||
|
||||
#region Prefix
|
||||
if (line[i].Equals(':'))
|
||||
{
|
||||
i++;
|
||||
prefix = "";
|
||||
/* This message has a prefix */
|
||||
string s = "";
|
||||
while (i < line.Length && line[i] != ' ' && line[i] != '!' && line[i] != '@')
|
||||
{
|
||||
s += line[i++];
|
||||
}
|
||||
if (IsValidIrcNickname(s))
|
||||
{
|
||||
prefixNickname = s;
|
||||
prefix += prefixNickname;
|
||||
if (line[i] == '!')
|
||||
{
|
||||
/* This message has an extended prefix */
|
||||
i++;
|
||||
s = "";
|
||||
while (i < line.Length && line[i] != ' ' && line[i] != '@')
|
||||
{
|
||||
s += line[i];
|
||||
i++;
|
||||
}
|
||||
prefixUser = s;
|
||||
prefix += "!" + prefixUser;
|
||||
}
|
||||
if (line[i] == '@')
|
||||
{
|
||||
/* This message has a host prefix */
|
||||
s = "";
|
||||
do
|
||||
{
|
||||
s += line[++i];
|
||||
}
|
||||
while (i < line.Length && line[i] != ' ');
|
||||
prefixHost = s;
|
||||
prefix += "@" + prefixHost;
|
||||
}
|
||||
}
|
||||
else /* Assume it is a servername */
|
||||
{
|
||||
prefixServername = s;
|
||||
prefix += prefixServername;
|
||||
}
|
||||
|
||||
/* Skip spaces */
|
||||
while (i < line.Length && line[i] == ' ')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prefix = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Command
|
||||
if (Char.IsDigit(line[i]))
|
||||
{
|
||||
if (!Char.IsDigit(line, i + 1) || !Char.IsDigit(line, i + 2))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
command = String.Format("{0}{1}{2}", line[i++], line[i++], line[i++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
command = "";
|
||||
while (i < line.Length && Char.IsLetter(line[i]))
|
||||
{
|
||||
command += line[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Parameters
|
||||
while (true)
|
||||
{
|
||||
/* Skip spaces */
|
||||
while (i < line.Length && line[i] == ' ')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
if (i < line.Length && line[i].Equals(':'))
|
||||
{
|
||||
i++;
|
||||
|
||||
/* Trailing */
|
||||
while (i < line.Length && line[i] != ' ' && line[i] != '\r' && line[i] != '\n' && line[i] != 0)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
parameters = "";
|
||||
}
|
||||
parameters += line[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Middle */
|
||||
while (i < line.Length && line[i] != '\r' && line[i] != '\n' && line[i] != 0)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
parameters = "";
|
||||
}
|
||||
parameters += line[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (i >= line.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new MalformedMessageException("The message is malformed.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="prefixServername"></param>
|
||||
/// <param name="prefixNickname"></param>
|
||||
/// <param name="prefixUser"></param>
|
||||
/// <param name="prefixHost"></param>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public IrcMessage(string prefixServername,
|
||||
string prefixNickname,
|
||||
string prefixUser,
|
||||
string prefixHost,
|
||||
string command,
|
||||
string parameters)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="command">IRC command.</param>
|
||||
/// <param name="parameters">IRC command parameters. May be null if there are no parameters.</param>
|
||||
public IrcMessage(string command,
|
||||
string parameters)
|
||||
{
|
||||
if (command == null || !IsValidIrcCommand(command))
|
||||
{
|
||||
throw new ArgumentException("Command is not a valid IRC command.", "command");
|
||||
}
|
||||
/* An IRC message must not be longer than 512 characters (including terminating CRLF) */
|
||||
int parametersLength = (parameters != null) ? 1 + parameters.Length : 0;
|
||||
if (command.Length + parametersLength > 510)
|
||||
{
|
||||
throw new MalformedMessageException("IRC message cannot be longer than 512 characters.");
|
||||
}
|
||||
this.command = command;
|
||||
this.parameters = parameters;
|
||||
if (parameters != null)
|
||||
{
|
||||
this.line = String.Format("{0} {1}\r\n", command, parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.line = String.Format("{0}\r\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns wether a string of text is a valid IRC command.
|
||||
/// </summary>
|
||||
/// <param name="command">The IRC command.</param>
|
||||
/// <returns>True, if <c ref="command">command</c> is a valid IRC command, false if not.</returns>
|
||||
private static bool IsValidIrcCommand(string command)
|
||||
{
|
||||
foreach (char c in command)
|
||||
{
|
||||
if (!Char.IsLetter(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private const string IrcSpecial = @"-[]\`^{}";
|
||||
private const string IrcSpecialNonSpecs = @"_";
|
||||
|
||||
/// <summary>
|
||||
/// Returns wether a character is an IRC special character.
|
||||
/// </summary>
|
||||
/// <param name="c">Character to test.</param>
|
||||
/// <returns>True if the character is an IRC special character, false if not.</returns>
|
||||
private static bool IsSpecial(char c)
|
||||
{
|
||||
foreach (char validCharacter in IrcSpecial)
|
||||
{
|
||||
if (c.Equals(validCharacter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach (char validCharacter in IrcSpecialNonSpecs)
|
||||
{
|
||||
if (c.Equals(validCharacter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns wether a string of text is a valid IRC nickname.
|
||||
/// </summary>
|
||||
/// <param name="nickname">The IRC nickname.</param>
|
||||
/// <returns>True, if <c ref="nickname">nickname</c> is a valid IRC nickname, false if not.</returns>
|
||||
private static bool IsValidIrcNickname(string nickname)
|
||||
{
|
||||
/*
|
||||
* <nick> ::= <letter> { <letter> | <number> | <special> }
|
||||
* <letter> ::= 'a' ... 'z' | 'A' ... 'Z'
|
||||
* <number> ::= '0' ... '9'
|
||||
* <special> ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'
|
||||
*/
|
||||
/* An IRC nicknmame must be 1 - 9 characters in length. We don't care so much if it is larger */
|
||||
if ((nickname.Length < 1) || (nickname.Length > 30))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/* First character must be a letter. */
|
||||
if (!Char.IsLetter(nickname[0]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/* Check the other valid characters for validity. */
|
||||
foreach (char c in nickname)
|
||||
{
|
||||
if (!Char.IsLetter(c) && !Char.IsDigit(c) && !IsSpecial(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write contents to a string.
|
||||
/// </summary>
|
||||
/// <returns>Contents as a string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("Line({0})Prefix({1})Command({2})Parameters({3})",
|
||||
line, prefix != null ? prefix : "(null)",
|
||||
command != null ? command : "(null)",
|
||||
parameters != null ? parameters : "(null)");
|
||||
}
|
||||
}
|
||||
}
|
||||
96
irc/TechBot/TechBot.IRCLibrary/IrcUser.cs
Normal file
96
irc/TechBot/TechBot.IRCLibrary/IrcUser.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// IRC user.
|
||||
/// </summary>
|
||||
public class IrcUser
|
||||
{
|
||||
#region Private fields
|
||||
|
||||
private string nickname;
|
||||
private string decoratedNickname;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Nickname of user.
|
||||
/// </summary>
|
||||
public string Nickname
|
||||
{
|
||||
get
|
||||
{
|
||||
return nickname;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decorated nickname of user.
|
||||
/// </summary>
|
||||
public string DecoratedNickname
|
||||
{
|
||||
get
|
||||
{
|
||||
return decoratedNickname;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wether user is channel operator.
|
||||
/// </summary>
|
||||
public bool Operator
|
||||
{
|
||||
get
|
||||
{
|
||||
return decoratedNickname.StartsWith("@");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wether user has voice.
|
||||
/// </summary>
|
||||
public bool Voice
|
||||
{
|
||||
get
|
||||
{
|
||||
return decoratedNickname.StartsWith("+");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="nickname">Nickname (possibly decorated) of user.</param>
|
||||
public IrcUser(string nickname)
|
||||
{
|
||||
this.decoratedNickname = nickname.Trim();
|
||||
this.nickname = StripDecoration(decoratedNickname);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strip docoration of nickname.
|
||||
/// </summary>
|
||||
/// <param name="nickname">Possible decorated nickname.</param>
|
||||
/// <returns>Undecorated nickname.</returns>
|
||||
public static string StripDecoration(string decoratedNickname)
|
||||
{
|
||||
if (decoratedNickname.StartsWith("@"))
|
||||
{
|
||||
return decoratedNickname.Substring(1);
|
||||
}
|
||||
else if (decoratedNickname.StartsWith("+"))
|
||||
{
|
||||
return decoratedNickname.Substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return decoratedNickname;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.cmbx
Normal file
16
irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.cmbx
Normal file
@@ -0,0 +1,16 @@
|
||||
<Combine fileversion="1.0" name="TechBot.IRCLibrary" description="">
|
||||
<StartMode startupentry="TechBot.IRCLibrary" single="True">
|
||||
<Execute entry="TechBot.IRCLibrary" type="None" />
|
||||
</StartMode>
|
||||
<Entries>
|
||||
<Entry filename=".\.\TechBot.IRCLibrary.prjx" />
|
||||
</Entries>
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release">
|
||||
<Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug">
|
||||
<Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Combine>
|
||||
31
irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.prjx
Normal file
31
irc/TechBot/TechBot.IRCLibrary/TechBot.IRCLibrary.prjx
Normal file
@@ -0,0 +1,31 @@
|
||||
<Project name="TechBot.IRCLibrary" standardNamespace="TechBot.IRCLibrary" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
|
||||
<Contents>
|
||||
<File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IrcException.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IRC.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IrcChannel.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IrcMessage.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IrcUser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IrcClient.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
</Contents>
|
||||
<References />
|
||||
<DeploymentInformation target="" script="" strategy="File" />
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot.IRCLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configurations active="Debug">
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot.IRCLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configuration runwithwarnings="True" name="Release">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Release" assembly="TechBot.IRCLibrary" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Project>
|
||||
319
irc/TechBot/TechBot.Library/ApiCommand.cs
Normal file
319
irc/TechBot/TechBot.Library/ApiCommand.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Data;
|
||||
using System.Text.RegularExpressions;
|
||||
using HtmlHelp;
|
||||
using HtmlHelp.ChmDecoding;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class ApiCommand : BaseCommand, ICommand
|
||||
{
|
||||
private const bool IsVerbose = false;
|
||||
|
||||
private HtmlHelpSystem chm;
|
||||
private IServiceOutput serviceOutput;
|
||||
private string chmPath;
|
||||
private string mainChm;
|
||||
|
||||
public ApiCommand(IServiceOutput serviceOutput,
|
||||
string chmPath,
|
||||
string mainChm)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.chmPath = chmPath;
|
||||
this.mainChm = mainChm;
|
||||
Run();
|
||||
}
|
||||
|
||||
private void WriteIfVerbose(string message)
|
||||
{
|
||||
if (IsVerbose)
|
||||
serviceOutput.WriteLine(message);
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
string CHMFilename = Path.Combine(chmPath, mainChm);
|
||||
chm = new HtmlHelpSystem();
|
||||
chm.OpenFile(CHMFilename, null);
|
||||
|
||||
Console.WriteLine(String.Format("Loaded main CHM: {0}",
|
||||
Path.GetFileName(CHMFilename)));
|
||||
foreach (string filename in Directory.GetFiles(chmPath))
|
||||
{
|
||||
if (!Path.GetExtension(filename).ToLower().Equals(".chm"))
|
||||
continue;
|
||||
if (Path.GetFileName(filename).ToLower().Equals(mainChm))
|
||||
continue;
|
||||
|
||||
Console.WriteLine(String.Format("Loading CHM: {0}",
|
||||
Path.GetFileName(filename)));
|
||||
try
|
||||
{
|
||||
chm.MergeFile(filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(String.Format("Could not load CHM: {0}. Exception {1}",
|
||||
Path.GetFileName(filename),
|
||||
ex));
|
||||
}
|
||||
}
|
||||
Console.WriteLine(String.Format("Loaded {0} CHMs",
|
||||
chm.FileList.Length));
|
||||
}
|
||||
|
||||
public bool CanHandle(string commandName)
|
||||
{
|
||||
return CanHandle(commandName,
|
||||
new string[] { "api" });
|
||||
}
|
||||
|
||||
public void Handle(string commandName,
|
||||
string parameters)
|
||||
{
|
||||
if (parameters.Trim().Equals(String.Empty))
|
||||
DisplayNoKeyword();
|
||||
else
|
||||
Search(parameters);
|
||||
}
|
||||
|
||||
public string Help()
|
||||
{
|
||||
return "!api <apiname>";
|
||||
}
|
||||
|
||||
private bool SearchIndex(string keyword)
|
||||
{
|
||||
if (chm.HasIndex)
|
||||
{
|
||||
IndexItem item = chm.Index.SearchIndex(keyword,
|
||||
IndexType.KeywordLinks);
|
||||
if (item != null && item.Topics.Count > 0)
|
||||
{
|
||||
WriteIfVerbose(String.Format("Keyword {0} found in index",
|
||||
item.KeyWord));
|
||||
IndexTopic indexTopic = item.Topics[0] as IndexTopic;
|
||||
return DisplayResult(keyword, indexTopic);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteIfVerbose(String.Format("Keyword {0} not found in index",
|
||||
keyword));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SearchFullText(string keyword)
|
||||
{
|
||||
string sort = "Rating ASC";
|
||||
/*
|
||||
sort = "Location ASC");
|
||||
sort = "Title ASC");
|
||||
*/
|
||||
WriteIfVerbose(String.Format("Searching fulltext database for {0}",
|
||||
keyword));
|
||||
|
||||
bool partialMatches = false;
|
||||
bool titlesOnly = true;
|
||||
int maxResults = 100;
|
||||
DataTable results = chm.PerformSearch(keyword,
|
||||
maxResults,
|
||||
partialMatches,
|
||||
titlesOnly);
|
||||
WriteIfVerbose(String.Format("results.Rows.Count = {0}",
|
||||
results != null ?
|
||||
results.Rows.Count.ToString() : "(none)"));
|
||||
if (results != null && results.Rows.Count > 0)
|
||||
{
|
||||
results.DefaultView.Sort = sort;
|
||||
if (!DisplayResult(keyword, results))
|
||||
{
|
||||
DisplayNoResult(keyword);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayNoResult(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
private void Search(string keyword)
|
||||
{
|
||||
if (!SearchIndex(keyword))
|
||||
{
|
||||
SearchFullText(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
private bool DisplayResult(string keyword,
|
||||
IndexTopic indexTopic)
|
||||
{
|
||||
keyword = keyword.Trim().ToLower();
|
||||
string url = indexTopic.URL;
|
||||
WriteIfVerbose(String.Format("URL from index search {0}",
|
||||
url));
|
||||
string prototype = ExtractPrototype(url);
|
||||
if (prototype == null || prototype.Trim().Equals(String.Empty))
|
||||
return false;
|
||||
string formattedPrototype = FormatPrototype(prototype);
|
||||
serviceOutput.WriteLine(formattedPrototype);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool DisplayResult(string keyword,
|
||||
DataTable results)
|
||||
{
|
||||
keyword = keyword.Trim().ToLower();
|
||||
for (int i = 0; i < results.DefaultView.Count; i++)
|
||||
{
|
||||
DataRowView row = results.DefaultView[i];
|
||||
string title = row["Title"].ToString();
|
||||
WriteIfVerbose(String.Format("Examining {0}", title));
|
||||
if (title.Trim().ToLower().Equals(keyword))
|
||||
{
|
||||
string location = row["Location"].ToString();
|
||||
string rating = row["Rating"].ToString();
|
||||
string url = row["Url"].ToString();
|
||||
string prototype = ExtractPrototype(url);
|
||||
if (prototype == null || prototype.Trim().Equals(String.Empty))
|
||||
continue;
|
||||
string formattedPrototype = FormatPrototype(prototype);
|
||||
serviceOutput.WriteLine(formattedPrototype);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DisplayNoResult(string keyword)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("I don't know about keyword {0}", keyword));
|
||||
}
|
||||
|
||||
private void DisplayNoKeyword()
|
||||
{
|
||||
serviceOutput.WriteLine("Please give me a keyword.");
|
||||
}
|
||||
|
||||
private string ReplaceComments(string s)
|
||||
{
|
||||
return Regex.Replace(s, "//(.+)\r\n", "");
|
||||
}
|
||||
|
||||
private string ReplaceLineEndings(string s)
|
||||
{
|
||||
return Regex.Replace(s, "(\r\n)+", " ");
|
||||
}
|
||||
|
||||
private string ReplaceSpaces(string s)
|
||||
{
|
||||
return Regex.Replace(s, @" +", " ");
|
||||
}
|
||||
|
||||
private string ReplaceSpacesBeforeLeftParenthesis(string s)
|
||||
{
|
||||
return Regex.Replace(s, @"\( ", @"(");
|
||||
}
|
||||
|
||||
private string ReplaceSpacesBeforeRightParenthesis(string s)
|
||||
{
|
||||
return Regex.Replace(s, @" \)", @")");
|
||||
}
|
||||
|
||||
private string ReplaceSemicolon(string s)
|
||||
{
|
||||
return Regex.Replace(s, @";", @"");
|
||||
}
|
||||
|
||||
private string FormatPrototype(string prototype)
|
||||
{
|
||||
string s = ReplaceComments(prototype);
|
||||
s = ReplaceLineEndings(s);
|
||||
s = ReplaceSpaces(s);
|
||||
s = ReplaceSpacesBeforeLeftParenthesis(s);
|
||||
s = ReplaceSpacesBeforeRightParenthesis(s);
|
||||
s = ReplaceSemicolon(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private string ExtractPrototype(string url)
|
||||
{
|
||||
string page = GetPage(url);
|
||||
Match match = Regex.Match(page,
|
||||
"<PRE class=\"?syntax\"?>(.+)</PRE>",
|
||||
RegexOptions.Multiline |
|
||||
RegexOptions.Singleline);
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
string prototype = match.Groups[1].ToString();
|
||||
return StripHtml(StripAfterSlashPre(prototype));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private string StripAfterSlashPre(string html)
|
||||
{
|
||||
int index = html.IndexOf("</PRE>");
|
||||
if (index != -1)
|
||||
{
|
||||
return html.Substring(0, index);
|
||||
}
|
||||
else
|
||||
return html;
|
||||
}
|
||||
|
||||
private string StripHtml(string html)
|
||||
{
|
||||
return Regex.Replace(html, @"<(.|\n)*?>", String.Empty);
|
||||
}
|
||||
|
||||
private string GetPage(string url)
|
||||
{
|
||||
string CHMFileName = "";
|
||||
string topicName = "";
|
||||
string anchor = "";
|
||||
CHMStream.CHMStream baseStream;
|
||||
if (!chm.BaseStream.GetCHMParts(url, ref CHMFileName, ref topicName, ref anchor))
|
||||
{
|
||||
baseStream = chm.BaseStream;
|
||||
CHMFileName = baseStream.CHMFileName;
|
||||
topicName = url;
|
||||
anchor = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
baseStream = GetBaseStreamFromCHMFileName(CHMFileName);
|
||||
}
|
||||
|
||||
if ((topicName == "") || (CHMFileName == "") || (baseStream == null))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return baseStream.ExtractTextFile(topicName);
|
||||
}
|
||||
|
||||
private CHMStream.CHMStream GetBaseStreamFromCHMFileName(string CHMFileName)
|
||||
{
|
||||
foreach (CHMFile file in chm.FileList)
|
||||
{
|
||||
WriteIfVerbose(String.Format("Compare: {0} <> {1}",
|
||||
file.ChmFilePath,
|
||||
CHMFileName));
|
||||
if (file.ChmFilePath.ToLower().Equals(CHMFileName.ToLower()))
|
||||
{
|
||||
return file.BaseStream;
|
||||
}
|
||||
}
|
||||
WriteIfVerbose(String.Format("Could not find loaded CHM file in list: {0}",
|
||||
CHMFileName));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
irc/TechBot/TechBot.Library/AssemblyInfo.cs
Normal file
32
irc/TechBot/TechBot.Library/AssemblyInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
24
irc/TechBot/TechBot.Library/Default.build
Normal file
24
irc/TechBot/TechBot.Library/Default.build
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="TechBot.Library" default="build">
|
||||
|
||||
<property name="output.dir" value="..\bin" />
|
||||
|
||||
<target name="build" description="Build component">
|
||||
<mkdir dir="${output.dir}" />
|
||||
<csc target="library"
|
||||
output="${output.dir}\TechBot.Library.dll"
|
||||
optimize="true"
|
||||
debug="true"
|
||||
doc="${output.dir}\TechBot.Library.xml"
|
||||
warninglevel="0">
|
||||
<sources>
|
||||
<include name="*.cs" />
|
||||
</sources>
|
||||
<references>
|
||||
<include name="${output.dir}\CHMLibrary.dll" />
|
||||
<include name="${output.dir}\TechBot.IRCLibrary.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
37
irc/TechBot/TechBot.Library/HelpCommand.cs
Normal file
37
irc/TechBot/TechBot.Library/HelpCommand.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class HelpCommand : BaseCommand, ICommand
|
||||
{
|
||||
private IServiceOutput serviceOutput;
|
||||
private ArrayList commands;
|
||||
|
||||
public HelpCommand(IServiceOutput serviceOutput,
|
||||
ArrayList commands)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
public bool CanHandle(string commandName)
|
||||
{
|
||||
return CanHandle(commandName,
|
||||
new string[] { "help" });
|
||||
}
|
||||
|
||||
public void Handle(string commandName,
|
||||
string parameters)
|
||||
{
|
||||
serviceOutput.WriteLine("I support the following commands:");
|
||||
foreach (ICommand command in commands)
|
||||
serviceOutput.WriteLine(command.Help());
|
||||
}
|
||||
|
||||
public string Help()
|
||||
{
|
||||
return "!help";
|
||||
}
|
||||
}
|
||||
}
|
||||
81
irc/TechBot/TechBot.Library/HresultCommand.cs
Normal file
81
irc/TechBot/TechBot.Library/HresultCommand.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class HresultCommand : BaseCommand, ICommand
|
||||
{
|
||||
private IServiceOutput serviceOutput;
|
||||
private string hresultXml;
|
||||
private XmlDocument hresultXmlDocument;
|
||||
|
||||
public HresultCommand(IServiceOutput serviceOutput,
|
||||
string hresultXml)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.hresultXml = hresultXml;
|
||||
hresultXmlDocument = new XmlDocument();
|
||||
hresultXmlDocument.Load(hresultXml);
|
||||
}
|
||||
|
||||
public bool CanHandle(string commandName)
|
||||
{
|
||||
return CanHandle(commandName,
|
||||
new string[] { "hresult" });
|
||||
}
|
||||
|
||||
public void Handle(string commandName,
|
||||
string parameters)
|
||||
{
|
||||
string hresultText = parameters;
|
||||
if (hresultText.Equals(String.Empty))
|
||||
{
|
||||
serviceOutput.WriteLine("Please provide a valid HRESULT value.");
|
||||
return;
|
||||
}
|
||||
|
||||
NumberParser np = new NumberParser();
|
||||
long hresult = np.Parse(hresultText);
|
||||
if (np.Error)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("{0} is not a valid HRESULT value.",
|
||||
hresultText));
|
||||
return;
|
||||
}
|
||||
|
||||
string description = GetHresultDescription(hresult);
|
||||
if (description != null)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("{0} is {1}.",
|
||||
hresultText,
|
||||
description));
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("I don't know about HRESULT {0}.",
|
||||
hresultText));
|
||||
}
|
||||
}
|
||||
|
||||
public string Help()
|
||||
{
|
||||
return "!hresult <value>";
|
||||
}
|
||||
|
||||
private string GetHresultDescription(long hresult)
|
||||
{
|
||||
XmlElement root = hresultXmlDocument.DocumentElement;
|
||||
XmlNode node = root.SelectSingleNode(String.Format("Hresult[@value='{0}']",
|
||||
hresult.ToString("X8")));
|
||||
if (node != null)
|
||||
{
|
||||
XmlAttribute text = node.Attributes["text"];
|
||||
if (text == null)
|
||||
throw new Exception("Node has no text attribute.");
|
||||
return text.Value;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
irc/TechBot/TechBot.Library/ICommand.cs
Normal file
28
irc/TechBot/TechBot.Library/ICommand.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
bool CanHandle(string commandName);
|
||||
void Handle(string commandName,
|
||||
string parameters);
|
||||
string Help();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class BaseCommand
|
||||
{
|
||||
protected bool CanHandle(string commandName,
|
||||
string[] availableCommands)
|
||||
{
|
||||
foreach (string availableCommand in availableCommands)
|
||||
{
|
||||
if (String.Compare(availableCommand, commandName, true) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
142
irc/TechBot/TechBot.Library/IrcService.cs
Normal file
142
irc/TechBot/TechBot.Library/IrcService.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using TechBot.IRCLibrary;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class IrcService : IServiceOutput
|
||||
{
|
||||
private string hostname;
|
||||
private int port;
|
||||
private string channelname;
|
||||
private string botname;
|
||||
private string chmPath;
|
||||
private string mainChm;
|
||||
private string ntstatusXml;
|
||||
private string winerrorXml;
|
||||
private string hresultXml;
|
||||
private string svnCommand;
|
||||
private IrcClient client;
|
||||
private IrcChannel channel1;
|
||||
private TechBotService service;
|
||||
private bool isStopped = false;
|
||||
|
||||
public IrcService(string hostname,
|
||||
int port,
|
||||
string channelname,
|
||||
string botname,
|
||||
string chmPath,
|
||||
string mainChm,
|
||||
string ntstatusXml,
|
||||
string winerrorXml,
|
||||
string hresultXml,
|
||||
string svnCommand)
|
||||
{
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
this.channelname = channelname;
|
||||
this.botname = botname;
|
||||
this.chmPath = chmPath;
|
||||
this.mainChm = mainChm;
|
||||
this.ntstatusXml = ntstatusXml;
|
||||
this.winerrorXml = winerrorXml;
|
||||
this.hresultXml = hresultXml;
|
||||
this.svnCommand = svnCommand;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
service = new TechBotService(this,
|
||||
chmPath,
|
||||
mainChm,
|
||||
ntstatusXml,
|
||||
winerrorXml,
|
||||
hresultXml,
|
||||
svnCommand);
|
||||
service.Run();
|
||||
|
||||
client = new IrcClient();
|
||||
client.Encoding = System.Text.Encoding.GetEncoding("iso-8859-1");
|
||||
client.MessageReceived += new MessageReceivedHandler(client_MessageReceived);
|
||||
client.ChannelUserDatabaseChanged += new ChannelUserDatabaseChangedHandler(client_ChannelUserDatabaseChanged);
|
||||
System.Console.WriteLine(String.Format("Connecting to {0} port {1}",
|
||||
hostname, port));
|
||||
client.Connect(hostname, port);
|
||||
System.Console.WriteLine("Connected...");
|
||||
client.Register(botname, null);
|
||||
System.Console.WriteLine(String.Format("Registered as {0}...", botname));
|
||||
channel1 = client.JoinChannel(channelname);
|
||||
System.Console.WriteLine(String.Format("Joined channel {0}...", channelname));
|
||||
|
||||
while (!isStopped)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
client.PartChannel(channel1, "Caught in the bitstream...");
|
||||
client.Diconnect();
|
||||
System.Console.WriteLine("Disconnected...");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
isStopped = true;
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
{
|
||||
Console.WriteLine(String.Format("Sending: {0}", message));
|
||||
channel1.Talk(message);
|
||||
}
|
||||
|
||||
private void ExtractMessage(string parameters,
|
||||
out string message)
|
||||
{
|
||||
int startIndex = parameters.IndexOf(':');
|
||||
if (startIndex != -1)
|
||||
{
|
||||
message = parameters.Substring(startIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
message = parameters;
|
||||
}
|
||||
}
|
||||
|
||||
private void client_MessageReceived(IrcMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (channel1 != null &&
|
||||
channel1.Name != null &&
|
||||
message.Parameters != null)
|
||||
{
|
||||
string injectMessage;
|
||||
ExtractMessage(message.Parameters, out injectMessage);
|
||||
if ((message.Command.ToUpper().Equals("PRIVMSG")) &&
|
||||
(message.Parameters.ToLower().StartsWith("#" + channel1.Name.ToLower() + " ")))
|
||||
{
|
||||
Console.WriteLine("Injecting: " + injectMessage);
|
||||
service.InjectMessage(injectMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Received: " + message.Line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Received: " + message.Line);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(String.Format("Exception: {0}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
private void client_ChannelUserDatabaseChanged(IrcChannel channel)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
81
irc/TechBot/TechBot.Library/NtStatusCommand.cs
Normal file
81
irc/TechBot/TechBot.Library/NtStatusCommand.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class NtStatusCommand : BaseCommand, ICommand
|
||||
{
|
||||
private IServiceOutput serviceOutput;
|
||||
private string ntstatusXml;
|
||||
private XmlDocument ntstatusXmlDocument;
|
||||
|
||||
public NtStatusCommand(IServiceOutput serviceOutput,
|
||||
string ntstatusXml)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.ntstatusXml = ntstatusXml;
|
||||
ntstatusXmlDocument = new XmlDocument();
|
||||
ntstatusXmlDocument.Load(ntstatusXml);
|
||||
}
|
||||
|
||||
public bool CanHandle(string commandName)
|
||||
{
|
||||
return CanHandle(commandName,
|
||||
new string[] { "ntstatus" });
|
||||
}
|
||||
|
||||
public void Handle(string commandName,
|
||||
string parameters)
|
||||
{
|
||||
string ntstatusText = parameters;
|
||||
if (ntstatusText.Equals(String.Empty))
|
||||
{
|
||||
serviceOutput.WriteLine("Please provide a valid NTSTATUS value.");
|
||||
return;
|
||||
}
|
||||
|
||||
NumberParser np = new NumberParser();
|
||||
long ntstatus = np.Parse(ntstatusText);
|
||||
if (np.Error)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("{0} is not a valid NTSTATUS value.",
|
||||
ntstatusText));
|
||||
return;
|
||||
}
|
||||
|
||||
string description = GetNtstatusDescription(ntstatus);
|
||||
if (description != null)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("{0} is {1}.",
|
||||
ntstatusText,
|
||||
description));
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("I don't know about NTSTATUS {0}.",
|
||||
ntstatusText));
|
||||
}
|
||||
}
|
||||
|
||||
public string Help()
|
||||
{
|
||||
return "!ntstatus <value>";
|
||||
}
|
||||
|
||||
private string GetNtstatusDescription(long ntstatus)
|
||||
{
|
||||
XmlElement root = ntstatusXmlDocument.DocumentElement;
|
||||
XmlNode node = root.SelectSingleNode(String.Format("Ntstatus[@value='{0}']",
|
||||
ntstatus.ToString("X8")));
|
||||
if (node != null)
|
||||
{
|
||||
XmlAttribute text = node.Attributes["text"];
|
||||
if (text == null)
|
||||
throw new Exception("Node has no text attribute.");
|
||||
return text.Value;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
irc/TechBot/TechBot.Library/NumberParser.cs
Normal file
32
irc/TechBot/TechBot.Library/NumberParser.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class NumberParser
|
||||
{
|
||||
public bool Error = false;
|
||||
|
||||
public long Parse(string s)
|
||||
{
|
||||
try
|
||||
{
|
||||
Error = false;
|
||||
if (s.StartsWith("0x"))
|
||||
return Int64.Parse(s.Substring(2),
|
||||
NumberStyles.HexNumber);
|
||||
else
|
||||
return Int64.Parse(s);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
Error = true;
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
Error = true;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
irc/TechBot/TechBot.Library/ServiceOutput.cs
Normal file
9
irc/TechBot/TechBot.Library/ServiceOutput.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public interface IServiceOutput
|
||||
{
|
||||
void WriteLine(string message);
|
||||
}
|
||||
}
|
||||
34
irc/TechBot/TechBot.Library/SvnCommand.cs
Normal file
34
irc/TechBot/TechBot.Library/SvnCommand.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class SvnCommand : BaseCommand, ICommand
|
||||
{
|
||||
private IServiceOutput serviceOutput;
|
||||
private string svnCommand;
|
||||
|
||||
public SvnCommand(IServiceOutput serviceOutput,
|
||||
string svnCommand)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.svnCommand = svnCommand;
|
||||
}
|
||||
|
||||
public bool CanHandle(string commandName)
|
||||
{
|
||||
return CanHandle(commandName,
|
||||
new string[] { "svn" });
|
||||
}
|
||||
|
||||
public void Handle(string commandName,
|
||||
string parameters)
|
||||
{
|
||||
serviceOutput.WriteLine(svnCommand);
|
||||
}
|
||||
|
||||
public string Help()
|
||||
{
|
||||
return "!svn";
|
||||
}
|
||||
}
|
||||
}
|
||||
16
irc/TechBot/TechBot.Library/TechBot.Library.cmbx
Normal file
16
irc/TechBot/TechBot.Library/TechBot.Library.cmbx
Normal file
@@ -0,0 +1,16 @@
|
||||
<Combine fileversion="1.0" name="TechBot.Library" description="">
|
||||
<StartMode startupentry="TechBot.Library" single="True">
|
||||
<Execute entry="TechBot.Library" type="None" />
|
||||
</StartMode>
|
||||
<Entries>
|
||||
<Entry filename=".\.\TechBot.Library.prjx" />
|
||||
</Entries>
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release">
|
||||
<Entry name="TechBot.Library" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug">
|
||||
<Entry name="TechBot.Library" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Combine>
|
||||
39
irc/TechBot/TechBot.Library/TechBot.Library.prjx
Normal file
39
irc/TechBot/TechBot.Library/TechBot.Library.prjx
Normal file
@@ -0,0 +1,39 @@
|
||||
<Project name="TechBot.Library" standardNamespace="TechBot.Library" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
|
||||
<Contents>
|
||||
<File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
<File name=".\TechBotService.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\ServiceOutput.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\IrcService.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\ApiCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\ICommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\HelpCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\NtStatusCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\NumberParser.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\HresultCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\WinerrorCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\SvnCommand.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
</Contents>
|
||||
<References>
|
||||
<Reference type="Project" refto="CHMLibrary" localcopy="True" />
|
||||
<Reference type="Project" refto="TechBot.IRCLibrary" localcopy="True" />
|
||||
</References>
|
||||
<DeploymentInformation target="" script="" strategy="File" />
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot.Library" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configurations active="Debug">
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot.Library" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configuration runwithwarnings="True" name="Release">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Release" assembly="TechBot.Library" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Project>
|
||||
103
irc/TechBot/TechBot.Library/TechBotService.cs
Normal file
103
irc/TechBot/TechBot.Library/TechBotService.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Data;
|
||||
using System.Threading;
|
||||
using TechBot.IRCLibrary;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class TechBotService
|
||||
{
|
||||
private const bool IsVerbose = false;
|
||||
|
||||
private IServiceOutput serviceOutput;
|
||||
private string chmPath;
|
||||
private string mainChm;
|
||||
private string ntstatusXml;
|
||||
private string winerrorXml;
|
||||
private string hresultXml;
|
||||
private string svnCommand;
|
||||
private ArrayList commands = new ArrayList();
|
||||
|
||||
public TechBotService(IServiceOutput serviceOutput,
|
||||
string chmPath,
|
||||
string mainChm,
|
||||
string ntstatusXml,
|
||||
string winerrorXml,
|
||||
string hresultXml,
|
||||
string svnCommand)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.chmPath = chmPath;
|
||||
this.mainChm = mainChm;
|
||||
this.ntstatusXml = ntstatusXml;
|
||||
this.winerrorXml = winerrorXml;
|
||||
this.hresultXml = hresultXml;
|
||||
this.svnCommand = svnCommand;
|
||||
}
|
||||
|
||||
private void WriteIfVerbose(string message)
|
||||
{
|
||||
if (IsVerbose)
|
||||
serviceOutput.WriteLine(message);
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
commands.Add(new HelpCommand(serviceOutput,
|
||||
commands));
|
||||
commands.Add(new ApiCommand(serviceOutput,
|
||||
chmPath,
|
||||
mainChm));
|
||||
commands.Add(new NtStatusCommand(serviceOutput,
|
||||
ntstatusXml));
|
||||
commands.Add(new WinerrorCommand(serviceOutput,
|
||||
winerrorXml));
|
||||
commands.Add(new HresultCommand(serviceOutput,
|
||||
hresultXml));
|
||||
commands.Add(new SvnCommand(serviceOutput,
|
||||
svnCommand));
|
||||
}
|
||||
|
||||
public void InjectMessage(string message)
|
||||
{
|
||||
if (message.StartsWith("!"))
|
||||
{
|
||||
ParseCommandMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsCommandMessage(string message)
|
||||
{
|
||||
return message.StartsWith("!");
|
||||
}
|
||||
|
||||
public void ParseCommandMessage(string message)
|
||||
{
|
||||
if (!IsCommandMessage(message))
|
||||
return;
|
||||
|
||||
message = message.Substring(1).Trim();
|
||||
int index = message.IndexOf(' ');
|
||||
string commandName;
|
||||
string parameters = "";
|
||||
if (index != -1)
|
||||
{
|
||||
commandName = message.Substring(0, index).Trim();
|
||||
parameters = message.Substring(index).Trim();
|
||||
}
|
||||
else
|
||||
commandName = message.Trim();
|
||||
|
||||
foreach (ICommand command in commands)
|
||||
{
|
||||
if (command.CanHandle(commandName))
|
||||
{
|
||||
command.Handle(commandName, parameters);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
irc/TechBot/TechBot.Library/WinerrorCommand.cs
Normal file
81
irc/TechBot/TechBot.Library/WinerrorCommand.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace TechBot.Library
|
||||
{
|
||||
public class WinerrorCommand : BaseCommand, ICommand
|
||||
{
|
||||
private IServiceOutput serviceOutput;
|
||||
private string winerrorXml;
|
||||
private XmlDocument winerrorXmlDocument;
|
||||
|
||||
public WinerrorCommand(IServiceOutput serviceOutput,
|
||||
string winerrorXml)
|
||||
{
|
||||
this.serviceOutput = serviceOutput;
|
||||
this.winerrorXml = winerrorXml;
|
||||
winerrorXmlDocument = new XmlDocument();
|
||||
winerrorXmlDocument.Load(winerrorXml);
|
||||
}
|
||||
|
||||
public bool CanHandle(string commandName)
|
||||
{
|
||||
return CanHandle(commandName,
|
||||
new string[] { "winerror" });
|
||||
}
|
||||
|
||||
public void Handle(string commandName,
|
||||
string parameters)
|
||||
{
|
||||
string winerrorText = parameters;
|
||||
if (winerrorText.Equals(String.Empty))
|
||||
{
|
||||
serviceOutput.WriteLine("Please provide a valid System Error Code value.");
|
||||
return;
|
||||
}
|
||||
|
||||
NumberParser np = new NumberParser();
|
||||
long winerror = np.Parse(winerrorText);
|
||||
if (np.Error)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("{0} is not a valid System Error Code value.",
|
||||
winerrorText));
|
||||
return;
|
||||
}
|
||||
|
||||
string description = GetWinerrorDescription(winerror);
|
||||
if (description != null)
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("{0} is {1}.",
|
||||
winerrorText,
|
||||
description));
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceOutput.WriteLine(String.Format("I don't know about System Error Code {0}.",
|
||||
winerrorText));
|
||||
}
|
||||
}
|
||||
|
||||
public string Help()
|
||||
{
|
||||
return "!winerror <value>";
|
||||
}
|
||||
|
||||
private string GetWinerrorDescription(long winerror)
|
||||
{
|
||||
XmlElement root = winerrorXmlDocument.DocumentElement;
|
||||
XmlNode node = root.SelectSingleNode(String.Format("Winerror[@value='{0}']",
|
||||
winerror));
|
||||
if (node != null)
|
||||
{
|
||||
XmlAttribute text = node.Attributes["text"];
|
||||
if (text == null)
|
||||
throw new Exception("Node has no text attribute.");
|
||||
return text.Value;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
irc/TechBot/TechBot.cmbx
Normal file
36
irc/TechBot/TechBot.cmbx
Normal file
@@ -0,0 +1,36 @@
|
||||
<Combine fileversion="1.0" name="TechBot" description="">
|
||||
<StartMode startupentry="TechBot" single="True">
|
||||
<Execute entry="TechBot" type="None" />
|
||||
<Execute entry="TechBot.Library" type="None" />
|
||||
<Execute entry="CHMLibrary" type="None" />
|
||||
<Execute entry="Compression" type="None" />
|
||||
<Execute entry="TechBot.Console" type="None" />
|
||||
<Execute entry="TechBot.IRCLibrary" type="None" />
|
||||
</StartMode>
|
||||
<Entries>
|
||||
<Entry filename=".\TechBot\TechBot.prjx" />
|
||||
<Entry filename=".\TechBot.Library\TechBot.Library.prjx" />
|
||||
<Entry filename=".\CHMLibrary\CHMLibrary.prjx" />
|
||||
<Entry filename=".\Compression\Compression.prjx" />
|
||||
<Entry filename=".\TechBot.Console\TechBot.Console.prjx" />
|
||||
<Entry filename=".\TechBot.IRCLibrary\TechBot.IRCLibrary.prjx" />
|
||||
</Entries>
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release">
|
||||
<Entry name="TechBot" configurationname="Debug" build="False" />
|
||||
<Entry name="TechBot.Library" configurationname="Debug" build="False" />
|
||||
<Entry name="CHMLibrary" configurationname="Debug" build="False" />
|
||||
<Entry name="Compression" configurationname="Debug" build="False" />
|
||||
<Entry name="TechBot.Console" configurationname="Debug" build="False" />
|
||||
<Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug">
|
||||
<Entry name="TechBot" configurationname="Debug" build="False" />
|
||||
<Entry name="TechBot.Library" configurationname="Debug" build="False" />
|
||||
<Entry name="CHMLibrary" configurationname="Debug" build="False" />
|
||||
<Entry name="Compression" configurationname="Debug" build="False" />
|
||||
<Entry name="TechBot.Console" configurationname="Debug" build="False" />
|
||||
<Entry name="TechBot.IRCLibrary" configurationname="Debug" build="False" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Combine>
|
||||
15
irc/TechBot/TechBot/App.config
Normal file
15
irc/TechBot/TechBot/App.config
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="IRCServerHostName" value="irc.eu.freenode.net" />
|
||||
<add key="IRCServerHostPort" value="6667" />
|
||||
<add key="IRCChannelName" value="channel" />
|
||||
<add key="IRCBotName" value="MyBot" />
|
||||
<add key="ChmPath" value="C:\IRC\TechBot\CHM" />
|
||||
<add key="MainChm" value="kmarch.chm" />
|
||||
<add key="NtstatusXml" value="C:\IRC\TechBot\ntstatus.xml" />
|
||||
<add key="WinerrorXml" value="C:\IRC\TechBot\winerror.xml" />
|
||||
<add key="HresultXml" value="C:\IRC\TechBot\hresult.xml" />
|
||||
<add key="SvnCommand" value="svn co svn://svn.reactos.com/trunk/reactos" />
|
||||
</appSettings>
|
||||
</configuration>
|
||||
32
irc/TechBot/TechBot/AssemblyInfo.cs
Normal file
32
irc/TechBot/TechBot/AssemblyInfo.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following
|
||||
// attributes.
|
||||
//
|
||||
// change them to the information which is associated with the assembly
|
||||
// you compile.
|
||||
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all values by your own or you can build default build and revision
|
||||
// numbers with the '*' character (the default):
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes specify the key for the sign of your assembly. See the
|
||||
// .NET Framework documentation for more information about signing.
|
||||
// This is not required, if you don't want signing let these attributes like they're.
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
23
irc/TechBot/TechBot/Default.build
Normal file
23
irc/TechBot/TechBot/Default.build
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="TechBot" default="build">
|
||||
|
||||
<property name="output.dir" value="..\bin" />
|
||||
|
||||
<target name="build" description="Build component">
|
||||
<mkdir dir="${output.dir}" />
|
||||
<csc target="winexe"
|
||||
output="${output.dir}\TechBot.exe"
|
||||
optimize="true"
|
||||
debug="true"
|
||||
doc="${output.dir}\TechBot.xml"
|
||||
warninglevel="0">
|
||||
<sources>
|
||||
<include name="*.cs" />
|
||||
</sources>
|
||||
<references>
|
||||
<include name="${output.dir}\TechBot.Library.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
71
irc/TechBot/TechBot/ServiceThread.cs
Normal file
71
irc/TechBot/TechBot/ServiceThread.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using TechBot.Library;
|
||||
|
||||
namespace TechBot
|
||||
{
|
||||
public class ServiceThread
|
||||
{
|
||||
private string IRCServerHostName;
|
||||
private int IRCServerHostPort;
|
||||
private string IRCChannelName;
|
||||
private string IRCBotName;
|
||||
private string ChmPath;
|
||||
private string MainChm;
|
||||
private string NtstatusXml;
|
||||
private string HresultXml;
|
||||
private string WinerrorXml;
|
||||
private string SvnCommand;
|
||||
private EventLog eventLog;
|
||||
|
||||
public ServiceThread(EventLog eventLog)
|
||||
{
|
||||
this.eventLog = eventLog;
|
||||
}
|
||||
|
||||
private void SetupConfiguration()
|
||||
{
|
||||
IRCServerHostName = ConfigurationSettings.AppSettings["IRCServerHostName"];
|
||||
IRCServerHostPort = Int32.Parse(ConfigurationSettings.AppSettings["IRCServerHostPort"]);
|
||||
IRCChannelName = ConfigurationSettings.AppSettings["IRCChannelName"];
|
||||
IRCBotName = ConfigurationSettings.AppSettings["IRCBotName"];
|
||||
ChmPath = ConfigurationSettings.AppSettings["ChmPath"];
|
||||
MainChm = ConfigurationSettings.AppSettings["MainChm"];
|
||||
NtstatusXml = ConfigurationSettings.AppSettings["NtstatusXml"];
|
||||
HresultXml = ConfigurationSettings.AppSettings["HresultXml"];
|
||||
WinerrorXml = ConfigurationSettings.AppSettings["WinerrorXml"];
|
||||
SvnCommand = ConfigurationSettings.AppSettings["SvnCommand"];
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
SetupConfiguration();
|
||||
System.Console.WriteLine("TechBot irc service...");
|
||||
|
||||
IrcService ircService = new IrcService(IRCServerHostName,
|
||||
IRCServerHostPort,
|
||||
IRCChannelName,
|
||||
IRCBotName,
|
||||
ChmPath,
|
||||
MainChm,
|
||||
NtstatusXml,
|
||||
WinerrorXml,
|
||||
HresultXml,
|
||||
SvnCommand);
|
||||
ircService.Run();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
eventLog.WriteEntry(String.Format("Ex. {0}", ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
irc/TechBot/TechBot/TechBot.prjx
Normal file
30
irc/TechBot/TechBot/TechBot.prjx
Normal file
@@ -0,0 +1,30 @@
|
||||
<Project name="TechBot" standardNamespace="TechBot" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
|
||||
<Contents>
|
||||
<File name=".\TechBotService.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name=".\Default.build" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
<File name=".\App.config" subtype="Code" buildaction="Nothing" dependson="" data="" />
|
||||
<File name=".\ServiceThread.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
</Contents>
|
||||
<References>
|
||||
<Reference type="Project" refto="TechBot.Library" localcopy="True" />
|
||||
</References>
|
||||
<DeploymentInformation target="" script="" strategy="File" />
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configurations active="Debug">
|
||||
<Configuration runwithwarnings="True" name="Debug">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Debug" assembly="TechBot" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
<Configuration runwithwarnings="True" name="Release">
|
||||
<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" />
|
||||
<Execution commandlineparameters="" consolepause="False" />
|
||||
<Output directory="..\bin\Release" assembly="TechBot" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
</Project>
|
||||
97
irc/TechBot/TechBot/TechBotService.cs
Normal file
97
irc/TechBot/TechBot/TechBotService.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.ServiceProcess;
|
||||
using System.Configuration.Install;
|
||||
|
||||
namespace TechBot
|
||||
{
|
||||
public class TechBotService : System.ServiceProcess.ServiceBase
|
||||
{
|
||||
private Thread thread;
|
||||
private ServiceThread threadWorker;
|
||||
|
||||
public TechBotService()
|
||||
{
|
||||
InitializeComponents();
|
||||
}
|
||||
|
||||
private void InitializeComponents()
|
||||
{
|
||||
this.ServiceName = "TechBot";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method starts the service.
|
||||
/// </summary>
|
||||
public static void Main()
|
||||
{
|
||||
System.ServiceProcess.ServiceBase.Run(new System.ServiceProcess.ServiceBase[] {
|
||||
new TechBotService() // To run more than one service you have to add them here
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this service.
|
||||
/// </summary>
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
threadWorker = new ServiceThread(EventLog);
|
||||
thread = new Thread(new ThreadStart(threadWorker.Start));
|
||||
thread.Start();
|
||||
EventLog.WriteEntry(String.Format("TechBot service is running."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventLog.WriteEntry(String.Format("Ex. {0}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop this service.
|
||||
/// </summary>
|
||||
protected override void OnStop()
|
||||
{
|
||||
try
|
||||
{
|
||||
thread.Abort();
|
||||
thread.Join();
|
||||
thread = null;
|
||||
threadWorker = null;
|
||||
EventLog.WriteEntry(String.Format("TechBot service is stopped."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventLog.WriteEntry(String.Format("Ex. {0}", ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RunInstaller(true)]
|
||||
public class ProjectInstaller : Installer
|
||||
{
|
||||
public ProjectInstaller()
|
||||
{
|
||||
ServiceProcessInstaller spi = new ServiceProcessInstaller();
|
||||
spi.Account = ServiceAccount.LocalSystem;
|
||||
|
||||
ServiceInstaller si = new ServiceInstaller();
|
||||
si.ServiceName = "TechBot";
|
||||
si.StartType = ServiceStartMode.Automatic;
|
||||
Installers.AddRange(new Installer[] {spi, si});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user