mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 19:41:57 +00:00
289 lines
8.7 KiB
C#
289 lines
8.7 KiB
C#
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|