mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 19:41:57 +00:00
9dab4509fa
svn path=/trunk/; revision=13064
309 lines
9.1 KiB
C#
309 lines
9.1 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|