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; } } }