2 using System.Collections;
3 using System.Diagnostics;
6 using System.Globalization;
8 namespace HtmlHelp.ChmDecoding
11 /// The class <c>CHMSystem</c> reads the #SYSTEM file of the chm and stores its settings
13 internal sealed class CHMSystem : IDisposable
16 /// Internal flag specifying if the object is going to be disposed
18 private bool disposed = false;
20 /// Internal member storing the binary file data
22 private byte[] _binaryFileData = null;
24 /// Internal member storing the file version
26 private int _fileVersion = 0;
28 /// Internal member storing the contents file path
30 private string _contentsFile = "";
32 /// Internal member storing the index file path
34 private string _indexFile = "";
36 /// Internal member storing the default help topic
38 private string _defaultTopic = "";
40 /// Internal member storing the help-window title
42 private string _title = "";
44 /// Internal flag if dbcs is on
46 private bool _dbcs = false;
48 /// Internal flag if fulltext search is enabled
50 private bool _fullTextSearch = false;
52 /// Internal flag if KLinks are in the file
54 private bool _hasKLinks = false;
56 /// Internal flag if ALinks are in the file
58 private bool _hasALinks = false;
60 /// Internal member storing the name of the default window
62 private string _defaultWindow = "";
64 /// Internal member storing the filename of the compiled file
66 private string _compileFile = "";
68 /// Internal flag storing the offset value of the binary index
70 private uint _binaryIndexURLTableID = 0;
72 /// Inernal member storing the compiler version this file was compiled
74 private string _compilerVersion = "";
76 /// Internal flag storing the offset value of the binary TOC
78 private uint _binaryTOCURLTableID = 0;
80 /// Internal member storing the associated chmfile object
82 private CHMFile _associatedFile = null;
84 /// Internal member storing the default fontface, size, charset
86 private string _defaultFont = "";
88 /// Internal member storing the culture info of the file
90 private CultureInfo _culture;
93 /// Constructor of the class
95 /// <param name="binaryFileData">binary file data of the #SYSTEM file</param>
96 /// <param name="associatedFile">associated chm file</param>
97 public CHMSystem(byte[] binaryFileData, CHMFile associatedFile)
99 _binaryFileData = binaryFileData;
100 _associatedFile = associatedFile;
105 // Set the text encoder of the chm file to the read charset/codepage
106 _associatedFile.TextEncoding = Encoding.GetEncoding( this.CodePage );
111 /// Decodes the binary file data and fills the internal properties
113 /// <returns>true if succeeded</returns>
114 private bool DecodeData()
118 MemoryStream memStream = new MemoryStream(_binaryFileData);
119 BinaryReader binReader = new BinaryReader(memStream);
121 // First entry = DWORD for version number
122 _fileVersion = (int) binReader.ReadInt32();
124 while( (memStream.Position < memStream.Length) && (bRet) )
126 bRet &= DecodeEntry(ref binReader);
133 /// Decodes an #system file entry
135 /// <param name="binReader">binary reader reference</param>
136 /// <returns>true if succeeded</returns>
137 private bool DecodeEntry(ref BinaryReader binReader)
141 int code = (int) binReader.ReadInt16(); // entry code, WORD
142 int length = (int) binReader.ReadInt16(); // length of entry
148 _contentsFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
152 _indexFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
156 _defaultTopic = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
160 _title = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
165 nTemp = binReader.ReadInt32(); // read DWORD LCID
166 _culture = new CultureInfo(nTemp);
169 _associatedFile.TextEncoding = Encoding.GetEncoding(_culture.TextInfo.ANSICodePage);
171 nTemp = binReader.ReadInt32(); // read DWORD DBCS
172 _dbcs = (nTemp == 1);
174 nTemp = binReader.ReadInt32(); // read DWORD Fulltext search
175 _fullTextSearch = (nTemp == 1);
177 nTemp = binReader.ReadInt32(); // read DWORD has klinks
178 _hasKLinks = (nTemp != 0);
180 nTemp = binReader.ReadInt32(); // read DWORD has alinks
181 _hasALinks = (nTemp != 0);
183 // read the rest of code 4 (not important for us)
184 byte[] temp = new byte[length-(5*4)];
185 temp = binReader.ReadBytes(length-(5*4));
189 _defaultWindow = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
193 _compileFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
199 _binaryIndexURLTableID = (uint) binReader.ReadInt32();
203 byte[] read = binReader.ReadBytes(length);
209 // abbreviation (not interresting for us)
210 byte[] read = binReader.ReadBytes(length);
215 _compilerVersion = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
219 // timestamp of the file (not interresting for us)
220 byte[] read = binReader.ReadBytes(length);
227 _binaryTOCURLTableID = (uint) binReader.ReadInt32();
231 byte[] read = binReader.ReadBytes(length);
237 // number of information bytes
238 byte[] read = binReader.ReadBytes(length);
243 // copy of file #idxhdr
244 byte[] read = binReader.ReadBytes(length);
249 // custom tabs for HH viewer
250 byte[] read = binReader.ReadBytes(length);
256 byte[] read = binReader.ReadBytes(length);
261 // Default Font=string,number,number
262 // The string is the name of the font, the first number is the
263 // point size & the last number is the character set used by the font.
264 // For acceptable values see *_CHARSET defines in wingdi.h from the
265 // Windows SDK or the same file in MinGW or Wine.
266 // Most of the time you will only want to use 0, which is the value for ANSI,
267 // which is the subset of ASCII used by Windows.
268 _defaultFont = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
273 byte[] temp = new byte[length];
274 temp = binReader.ReadBytes(length);
284 /// Reads all HHC files and checks which one has the global object tag.
285 /// This hhc file will be returned as master hhc
287 /// <param name="hhcTopics">list of topics containing the extension hhc</param>
288 /// <param name="TopicItemArrayList">true if the arraylist contains topic items</param>
289 /// <returns>the filename of the found master toc</returns>
290 private string GetMasterHHC(ArrayList hhcTopics, bool TopicItemArrayList)
294 if( (hhcTopics!=null) && (hhcTopics.Count > 0) )
296 if( TopicItemArrayList )
298 if(hhcTopics.Count == 1)
300 sRet = ((TopicEntry)hhcTopics[0]).Locale;
304 foreach(TopicEntry curEntry in hhcTopics)
306 CHMStream.CHMStream iw=null;
307 MemoryStream fileObject=null;
309 if( _associatedFile.CurrentStorageWrapper == null)
311 iw=new CHMStream.CHMStream();
312 iw.OpenCHM(_associatedFile.ChmFilePath);
316 iw = _associatedFile.CurrentStorageWrapper;
319 fileObject = iw.OpenStream(curEntry.Locale);
320 if( fileObject != null)
322 string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
325 if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
327 sRet = curEntry.Locale;
336 if(hhcTopics.Count == 1)
338 sRet = ((string)hhcTopics[0]);
342 foreach(string curEntry in hhcTopics)
344 CHMStream.CHMStream iw=null;
345 MemoryStream fileObject=null;
347 if( _associatedFile.CurrentStorageWrapper == null)
349 iw=new CHMStream.CHMStream();
350 iw.OpenCHM(_associatedFile.ChmFilePath);
354 iw = _associatedFile.CurrentStorageWrapper;
357 fileObject = iw.OpenStream(curEntry);
358 if( fileObject != null)
360 string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
363 if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
378 /// Gets the file version of the chm file.
379 /// 2 for Compatibility=1.0, 3 for Compatibility=1.1
381 public int FileVersion
383 get { return _fileVersion; }
387 /// Gets the contents file name
389 public string ContentsFile
393 if( BinaryTOC ) // if the file contains a binary TOC
395 // make sure the CHMFile instance exists and has loaded the file #URLTBL
396 if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
398 // Get an url-table entry by its unique id
399 UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryTOCURLTableID );
402 // entry found, return the url ( = filename )
409 if(_contentsFile.Length <= 0)
411 string sCheck = "Table of Contents.hhc"; // default HHP contents filename
413 if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
415 TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
419 sCheck = "toc.hhc"; // default HHP contents filename
421 te = _associatedFile.TopicsFile.GetByLocale( sCheck );
425 sCheck = CompileFile + ".hhc";
427 te = _associatedFile.TopicsFile.GetByLocale( sCheck );
431 ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhc");
435 arrExt = _associatedFile.EnumFilesByExtension("hhc");
439 Debug.WriteLine("CHMSystem.ContentsFile - Failed, contents file not found !");
445 sCheck = GetMasterHHC(arrExt, false);
446 _contentsFile = sCheck;
450 _contentsFile = ((string)arrExt[0]);
451 sCheck = _contentsFile;
459 sCheck = GetMasterHHC(arrExt, true);
460 _contentsFile = sCheck;
464 _contentsFile = ((TopicEntry)arrExt[0]).Locale;
465 sCheck = _contentsFile;
471 _contentsFile = sCheck;
476 _contentsFile = sCheck;
481 _contentsFile = sCheck;
489 return _contentsFile;
494 /// Gets the index file name
496 public string IndexFile
500 if( BinaryIndex ) // if the file contains a binary index
502 // make sure the CHMFile instance exists and has loaded the file #URLTBL
503 if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
505 // Get an url-table entry by its unique id
506 UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryIndexURLTableID );
509 // entry found, return the url ( = filename )
516 if(_indexFile.Length <= 0)
518 string sCheck = "Index.hhk"; // default HHP index filename
520 if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
522 TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
526 sCheck = CompileFile + ".hhk";
528 te = _associatedFile.TopicsFile.GetByLocale( sCheck );
532 ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhk");
536 Debug.WriteLine("CHMSystem.IndexFile - Failed, index file not found !");
540 _indexFile = ((TopicEntry)arrExt[0]).Locale;
563 /// Sets the default topic of this file
565 /// <param name="local">new local value of the topic</param>
566 internal void SetDefaultTopic(string local)
568 _defaultTopic = local;
572 /// Gets the default help topic
574 public string DefaultTopic
576 get { return _defaultTopic; }
580 /// Gets the title of the help window
584 get { return _title; }
588 /// Gets the flag if DBCS is in use
592 get { return _dbcs; }
596 /// Gets the flag if full-text-search is available
598 public bool FullTextSearch
600 get { return _fullTextSearch; }
604 /// Gets the flag if the file has ALinks
606 public bool HasALinks
608 get { return _hasALinks; }
612 /// Gets the flag if the file has KLinks
614 public bool HasKLinks
616 get { return _hasKLinks; }
620 /// Gets the default window name
622 public string DefaultWindow
624 get { return _defaultWindow; }
628 /// Gets the file name of the compile file
630 public string CompileFile
632 get { return _compileFile; }
636 /// Gets the id of the binary index in the url table
638 public uint BinaryIndexURLTableID
640 get { return _binaryIndexURLTableID; }
644 /// Gets the flag if the chm has a binary index file
646 public bool BinaryIndex
648 get { return (_binaryIndexURLTableID>0); }
652 /// Gets the flag if the chm has a binary index file
654 public string CompilerVersion
656 get { return _compilerVersion; }
660 /// Gets the id of the binary toc in the url table
662 public uint BinaryTOCURLTableID
664 get { return _binaryTOCURLTableID; }
668 /// Gets the flag if the chm has a binary toc file
670 public bool BinaryTOC
672 get { return (_binaryTOCURLTableID>0); }
676 /// Gets the font face of the read font property.
677 /// Empty string for default font.
679 public string FontFace
683 if( _defaultFont.Length > 0)
685 string [] fontSplit = _defaultFont.Split( new char[]{','});
686 if(fontSplit.Length > 0)
687 return fontSplit[0].Trim();
695 /// Gets the font size of the read font property.
696 /// 0 for default font size
698 public double FontSize
702 if( _defaultFont.Length > 0)
704 string [] fontSplit = _defaultFont.Split( new char[]{','});
705 if(fontSplit.Length > 1)
706 return double.Parse(fontSplit[1].Trim());
714 /// Gets the character set of the read font property
717 public int CharacterSet
721 if( _defaultFont.Length > 0)
723 string [] fontSplit = _defaultFont.Split( new char[]{','});
724 if(fontSplit.Length > 2)
725 return Int32.Parse(fontSplit[2].Trim());
733 /// Gets the codepage depending on the read font property
739 // if we've read a LCID from the system file
740 // ignore the font-property settings and return
741 // the codepage generated from the culture info
744 return _culture.TextInfo.ANSICodePage;
747 int nRet = 1252; // default codepage windows-1252
749 int nCSet = CharacterSet;
753 case 0x00: nRet = 1252;break; // ANSI_CHARSET
754 case 0xCC: nRet = 1251;break; // RUSSIAN_CHARSET
755 case 0xEE: nRet = 1250;break; // EE_CHARSET
756 case 0xA1: nRet = 1253;break; // GREEK_CHARSET
757 case 0xA2: nRet = 1254;break; // TURKISH_CHARSET
758 case 0xBA: nRet = 1257;break; // BALTIC_CHARSET
759 case 0xB1: nRet = 1255;break; // HEBREW_CHARSET
760 case 0xB2: nRet = 1256;break; // ARABIC_CHARSET
761 case 0x80: nRet = 932;break; // SHIFTJIS_CHARSET
762 case 0x81: nRet = 949;break; // HANGEUL_CHARSET
763 case 0x86: nRet = 936;break; // GB2313_CHARSET
764 case 0x88: nRet = 950;break; // CHINESEBIG5_CHARSET
772 /// Gets the assiciated culture info
774 public CultureInfo Culture
776 get { return _culture; }
780 /// Implement IDisposable.
782 public void Dispose()
785 // This object will be cleaned up by the Dispose method.
786 // Therefore, you should call GC.SupressFinalize to
787 // take this object off the finalization queue
788 // and prevent finalization code for this object
789 // from executing a second time.
790 GC.SuppressFinalize(this);
794 /// Dispose(bool disposing) executes in two distinct scenarios.
795 /// If disposing equals true, the method has been called directly
796 /// or indirectly by a user's code. Managed and unmanaged resources
798 /// If disposing equals false, the method has been called by the
799 /// runtime from inside the finalizer and you should not reference
800 /// other objects. Only unmanaged resources can be disposed.
802 /// <param name="disposing">disposing flag</param>
803 private void Dispose(bool disposing)
805 // Check to see if Dispose has already been called.
808 // If disposing equals true, dispose all managed
809 // and unmanaged resources.
812 // Dispose managed resources.
813 _binaryFileData = null;