mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 16:23:01 +00:00
Import TechBot
svn path=/trunk/; revision=13064
This commit is contained in:
parent
568b27baeb
commit
9dab4509fa
94 changed files with 24386 additions and 0 deletions
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue