[HEADERS]
[reactos.git] / irc / TechBot / CHMLibrary / CHMDecoding / CHMSystem.cs
1 using System;
2 using System.Collections;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Text;
6 using System.Globalization;
7
8 namespace HtmlHelp.ChmDecoding
9 {
10 /// <summary>
11 /// The class <c>CHMSystem</c> reads the #SYSTEM file of the chm and stores its settings
12 /// </summary>
13 internal sealed class CHMSystem : IDisposable
14 {
15 /// <summary>
16 /// Internal flag specifying if the object is going to be disposed
17 /// </summary>
18 private bool disposed = false;
19 /// <summary>
20 /// Internal member storing the binary file data
21 /// </summary>
22 private byte[] _binaryFileData = null;
23 /// <summary>
24 /// Internal member storing the file version
25 /// </summary>
26 private int _fileVersion = 0;
27 /// <summary>
28 /// Internal member storing the contents file path
29 /// </summary>
30 private string _contentsFile = "";
31 /// <summary>
32 /// Internal member storing the index file path
33 /// </summary>
34 private string _indexFile = "";
35 /// <summary>
36 /// Internal member storing the default help topic
37 /// </summary>
38 private string _defaultTopic = "";
39 /// <summary>
40 /// Internal member storing the help-window title
41 /// </summary>
42 private string _title = "";
43 /// <summary>
44 /// Internal flag if dbcs is on
45 /// </summary>
46 private bool _dbcs = false;
47 /// <summary>
48 /// Internal flag if fulltext search is enabled
49 /// </summary>
50 private bool _fullTextSearch = false;
51 /// <summary>
52 /// Internal flag if KLinks are in the file
53 /// </summary>
54 private bool _hasKLinks = false;
55 /// <summary>
56 /// Internal flag if ALinks are in the file
57 /// </summary>
58 private bool _hasALinks = false;
59 /// <summary>
60 /// Internal member storing the name of the default window
61 /// </summary>
62 private string _defaultWindow = "";
63 /// <summary>
64 /// Internal member storing the filename of the compiled file
65 /// </summary>
66 private string _compileFile = "";
67 /// <summary>
68 /// Internal flag storing the offset value of the binary index
69 /// </summary>
70 private uint _binaryIndexURLTableID = 0;
71 /// <summary>
72 /// Inernal member storing the compiler version this file was compiled
73 /// </summary>
74 private string _compilerVersion = "";
75 /// <summary>
76 /// Internal flag storing the offset value of the binary TOC
77 /// </summary>
78 private uint _binaryTOCURLTableID = 0;
79 /// <summary>
80 /// Internal member storing the associated chmfile object
81 /// </summary>
82 private CHMFile _associatedFile = null;
83 /// <summary>
84 /// Internal member storing the default fontface, size, charset
85 /// </summary>
86 private string _defaultFont = "";
87 /// <summary>
88 /// Internal member storing the culture info of the file
89 /// </summary>
90 private CultureInfo _culture;
91
92 /// <summary>
93 /// Constructor of the class
94 /// </summary>
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)
98 {
99 _binaryFileData = binaryFileData;
100 _associatedFile = associatedFile;
101 DecodeData();
102
103 if(_culture == null)
104 {
105 // Set the text encoder of the chm file to the read charset/codepage
106 _associatedFile.TextEncoding = Encoding.GetEncoding( this.CodePage );
107 }
108 }
109
110 /// <summary>
111 /// Decodes the binary file data and fills the internal properties
112 /// </summary>
113 /// <returns>true if succeeded</returns>
114 private bool DecodeData()
115 {
116 bool bRet = true;
117
118 MemoryStream memStream = new MemoryStream(_binaryFileData);
119 BinaryReader binReader = new BinaryReader(memStream);
120
121 // First entry = DWORD for version number
122 _fileVersion = (int) binReader.ReadInt32();
123
124 while( (memStream.Position < memStream.Length) && (bRet) )
125 {
126 bRet &= DecodeEntry(ref binReader);
127 }
128
129 return bRet;
130 }
131
132 /// <summary>
133 /// Decodes an #system file entry
134 /// </summary>
135 /// <param name="binReader">binary reader reference</param>
136 /// <returns>true if succeeded</returns>
137 private bool DecodeEntry(ref BinaryReader binReader)
138 {
139 bool bRet = true;
140
141 int code = (int) binReader.ReadInt16(); // entry code, WORD
142 int length = (int) binReader.ReadInt16(); // length of entry
143
144 switch(code)
145 {
146 case 0:
147 {
148 _contentsFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
149 };break;
150 case 1:
151 {
152 _indexFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
153 };break;
154 case 2:
155 {
156 _defaultTopic = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
157 };break;
158 case 3:
159 {
160 _title = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
161 };break;
162 case 4:
163 {
164 int nTemp = 0;
165 nTemp = binReader.ReadInt32(); // read DWORD LCID
166 _culture = new CultureInfo(nTemp);
167
168 if(_culture != null)
169 _associatedFile.TextEncoding = Encoding.GetEncoding(_culture.TextInfo.ANSICodePage);
170
171 nTemp = binReader.ReadInt32(); // read DWORD DBCS
172 _dbcs = (nTemp == 1);
173
174 nTemp = binReader.ReadInt32(); // read DWORD Fulltext search
175 _fullTextSearch = (nTemp == 1);
176
177 nTemp = binReader.ReadInt32(); // read DWORD has klinks
178 _hasKLinks = (nTemp != 0);
179
180 nTemp = binReader.ReadInt32(); // read DWORD has alinks
181 _hasALinks = (nTemp != 0);
182
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));
186 };break;
187 case 5:
188 {
189 _defaultWindow = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
190 };break;
191 case 6:
192 {
193 _compileFile = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
194 };break;
195 case 7:
196 {
197 if(_fileVersion > 2)
198 {
199 _binaryIndexURLTableID = (uint) binReader.ReadInt32();
200 }
201 else
202 {
203 byte[] read = binReader.ReadBytes(length);
204 int i=read.Length;
205 }
206 };break;
207 case 8:
208 {
209 // abbreviation (not interresting for us)
210 byte[] read = binReader.ReadBytes(length);
211 int i=read.Length;
212 };break;
213 case 9:
214 {
215 _compilerVersion = BinaryReaderHelp.ExtractString(ref binReader,length, 0, true, _associatedFile.TextEncoding);
216 };break;
217 case 10:
218 {
219 // timestamp of the file (not interresting for us)
220 byte[] read = binReader.ReadBytes(length);
221 int i=read.Length;
222 };break;
223 case 11:
224 {
225 if(_fileVersion > 2)
226 {
227 _binaryTOCURLTableID = (uint) binReader.ReadInt32();
228 }
229 else
230 {
231 byte[] read = binReader.ReadBytes(length);
232 int i=read.Length;
233 }
234 };break;
235 case 12:
236 {
237 // number of information bytes
238 byte[] read = binReader.ReadBytes(length);
239 int i=read.Length;
240 };break;
241 case 13:
242 {
243 // copy of file #idxhdr
244 byte[] read = binReader.ReadBytes(length);
245 int i=read.Length;
246 };break;
247 case 14:
248 {
249 // custom tabs for HH viewer
250 byte[] read = binReader.ReadBytes(length);
251 int i=read.Length;
252 };break;
253 case 15:
254 {
255 // a checksum
256 byte[] read = binReader.ReadBytes(length);
257 int i=read.Length;
258 };break;
259 case 16:
260 {
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);
269
270 };break;
271 default:
272 {
273 byte[] temp = new byte[length];
274 temp = binReader.ReadBytes(length);
275 //bRet = false;
276 int i=temp.Length;
277 };break;
278 }
279
280 return bRet;
281 }
282
283 /// <summary>
284 /// Reads all HHC files and checks which one has the global object tag.
285 /// This hhc file will be returned as master hhc
286 /// </summary>
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)
291 {
292 string sRet = "";
293
294 if( (hhcTopics!=null) && (hhcTopics.Count > 0) )
295 {
296 if( TopicItemArrayList )
297 {
298 if(hhcTopics.Count == 1)
299 {
300 sRet = ((TopicEntry)hhcTopics[0]).Locale;
301 }
302 else
303 {
304 foreach(TopicEntry curEntry in hhcTopics)
305 {
306 CHMStream.CHMStream iw=null;
307 MemoryStream fileObject=null;
308
309 if( _associatedFile.CurrentStorageWrapper == null)
310 {
311 iw=new CHMStream.CHMStream();
312 iw.OpenCHM(_associatedFile.ChmFilePath);
313 }
314 else
315 {
316 iw = _associatedFile.CurrentStorageWrapper;
317 }
318
319 fileObject = iw.OpenStream(curEntry.Locale);
320 if( fileObject != null)
321 {
322 string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
323 fileObject.Close();
324
325 if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
326 {
327 sRet = curEntry.Locale;
328 break;
329 }
330 }
331 }
332 }
333 }
334 else
335 {
336 if(hhcTopics.Count == 1)
337 {
338 sRet = ((string)hhcTopics[0]);
339 }
340 else
341 {
342 foreach(string curEntry in hhcTopics)
343 {
344 CHMStream.CHMStream iw=null;
345 MemoryStream fileObject=null;
346
347 if( _associatedFile.CurrentStorageWrapper == null)
348 {
349 iw=new CHMStream.CHMStream();
350 iw.OpenCHM(_associatedFile.ChmFilePath);
351 }
352 else
353 {
354 iw = _associatedFile.CurrentStorageWrapper;
355 }
356
357 fileObject = iw.OpenStream(curEntry);
358 if( fileObject != null)
359 {
360 string fileString =_associatedFile.TextEncoding.GetString(fileObject.ToArray(),0,(int)fileObject.Length);
361 fileObject.Close();
362
363 if( HHCParser.HasGlobalObjectTag(fileString, _associatedFile) )
364 {
365 sRet = curEntry;
366 break;
367 }
368 }
369 }
370 }
371 }
372 }
373
374 return sRet;
375 }
376
377 /// <summary>
378 /// Gets the file version of the chm file.
379 /// 2 for Compatibility=1.0, 3 for Compatibility=1.1
380 /// </summary>
381 public int FileVersion
382 {
383 get { return _fileVersion; }
384 }
385
386 /// <summary>
387 /// Gets the contents file name
388 /// </summary>
389 public string ContentsFile
390 {
391 get
392 {
393 if( BinaryTOC ) // if the file contains a binary TOC
394 {
395 // make sure the CHMFile instance exists and has loaded the file #URLTBL
396 if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
397 {
398 // Get an url-table entry by its unique id
399 UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryTOCURLTableID );
400 if(entry != null)
401 {
402 // entry found, return the url ( = filename )
403 return entry.URL;
404 }
405 }
406 }
407 else
408 {
409 if(_contentsFile.Length <= 0)
410 {
411 string sCheck = "Table of Contents.hhc"; // default HHP contents filename
412
413 if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
414 {
415 TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
416
417 if( te == null)
418 {
419 sCheck = "toc.hhc"; // default HHP contents filename
420
421 te = _associatedFile.TopicsFile.GetByLocale( sCheck );
422
423 if( te == null)
424 {
425 sCheck = CompileFile + ".hhc";
426
427 te = _associatedFile.TopicsFile.GetByLocale( sCheck );
428
429 if( te == null)
430 {
431 ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhc");
432
433 if( arrExt == null )
434 {
435 arrExt = _associatedFile.EnumFilesByExtension("hhc");
436
437 if( arrExt == null )
438 {
439 Debug.WriteLine("CHMSystem.ContentsFile - Failed, contents file not found !");
440 }
441 else
442 {
443 if(arrExt.Count > 1)
444 {
445 sCheck = GetMasterHHC(arrExt, false);
446 _contentsFile = sCheck;
447 }
448 else
449 {
450 _contentsFile = ((string)arrExt[0]);
451 sCheck = _contentsFile;
452 }
453 }
454 }
455 else
456 {
457 if(arrExt.Count > 1)
458 {
459 sCheck = GetMasterHHC(arrExt, true);
460 _contentsFile = sCheck;
461 }
462 else
463 {
464 _contentsFile = ((TopicEntry)arrExt[0]).Locale;
465 sCheck = _contentsFile;
466 }
467 }
468 }
469 else
470 {
471 _contentsFile = sCheck;
472 }
473 }
474 else
475 {
476 _contentsFile = sCheck;
477 }
478 }
479 else
480 {
481 _contentsFile = sCheck;
482 }
483 }
484
485 return sCheck;
486 }
487 }
488
489 return _contentsFile;
490 }
491 }
492
493 /// <summary>
494 /// Gets the index file name
495 /// </summary>
496 public string IndexFile
497 {
498 get
499 {
500 if( BinaryIndex ) // if the file contains a binary index
501 {
502 // make sure the CHMFile instance exists and has loaded the file #URLTBL
503 if( (_associatedFile != null) && (_associatedFile.UrltblFile != null ) )
504 {
505 // Get an url-table entry by its unique id
506 UrlTableEntry entry = _associatedFile.UrltblFile.GetByUniqueID( this.BinaryIndexURLTableID );
507 if(entry != null)
508 {
509 // entry found, return the url ( = filename )
510 return entry.URL;
511 }
512 }
513 }
514 else
515 {
516 if(_indexFile.Length <= 0)
517 {
518 string sCheck = "Index.hhk"; // default HHP index filename
519
520 if( (_associatedFile != null) && (_associatedFile.TopicsFile != null ) )
521 {
522 TopicEntry te = _associatedFile.TopicsFile.GetByLocale( sCheck );
523
524 if( te == null)
525 {
526 sCheck = CompileFile + ".hhk";
527
528 te = _associatedFile.TopicsFile.GetByLocale( sCheck );
529
530 if( te == null)
531 {
532 ArrayList arrExt = _associatedFile.TopicsFile.GetByExtension("hhk");
533
534 if( arrExt == null )
535 {
536 Debug.WriteLine("CHMSystem.IndexFile - Failed, index file not found !");
537 }
538 else
539 {
540 _indexFile = ((TopicEntry)arrExt[0]).Locale;
541 sCheck = _indexFile;
542 }
543 }
544 else
545 {
546 _indexFile = sCheck;
547 }
548 }
549 else
550 {
551 _indexFile = sCheck;
552 }
553 }
554
555 return sCheck;
556 }
557 }
558 return _indexFile;
559 }
560 }
561
562 /// <summary>
563 /// Sets the default topic of this file
564 /// </summary>
565 /// <param name="local">new local value of the topic</param>
566 internal void SetDefaultTopic(string local)
567 {
568 _defaultTopic = local;
569 }
570
571 /// <summary>
572 /// Gets the default help topic
573 /// </summary>
574 public string DefaultTopic
575 {
576 get { return _defaultTopic; }
577 }
578
579 /// <summary>
580 /// Gets the title of the help window
581 /// </summary>
582 public string Title
583 {
584 get { return _title; }
585 }
586
587 /// <summary>
588 /// Gets the flag if DBCS is in use
589 /// </summary>
590 public bool DBCS
591 {
592 get { return _dbcs; }
593 }
594
595 /// <summary>
596 /// Gets the flag if full-text-search is available
597 /// </summary>
598 public bool FullTextSearch
599 {
600 get { return _fullTextSearch; }
601 }
602
603 /// <summary>
604 /// Gets the flag if the file has ALinks
605 /// </summary>
606 public bool HasALinks
607 {
608 get { return _hasALinks; }
609 }
610
611 /// <summary>
612 /// Gets the flag if the file has KLinks
613 /// </summary>
614 public bool HasKLinks
615 {
616 get { return _hasKLinks; }
617 }
618
619 /// <summary>
620 /// Gets the default window name
621 /// </summary>
622 public string DefaultWindow
623 {
624 get { return _defaultWindow; }
625 }
626
627 /// <summary>
628 /// Gets the file name of the compile file
629 /// </summary>
630 public string CompileFile
631 {
632 get { return _compileFile; }
633 }
634
635 /// <summary>
636 /// Gets the id of the binary index in the url table
637 /// </summary>
638 public uint BinaryIndexURLTableID
639 {
640 get { return _binaryIndexURLTableID; }
641 }
642
643 /// <summary>
644 /// Gets the flag if the chm has a binary index file
645 /// </summary>
646 public bool BinaryIndex
647 {
648 get { return (_binaryIndexURLTableID>0); }
649 }
650
651 /// <summary>
652 /// Gets the flag if the chm has a binary index file
653 /// </summary>
654 public string CompilerVersion
655 {
656 get { return _compilerVersion; }
657 }
658
659 /// <summary>
660 /// Gets the id of the binary toc in the url table
661 /// </summary>
662 public uint BinaryTOCURLTableID
663 {
664 get { return _binaryTOCURLTableID; }
665 }
666
667 /// <summary>
668 /// Gets the flag if the chm has a binary toc file
669 /// </summary>
670 public bool BinaryTOC
671 {
672 get { return (_binaryTOCURLTableID>0); }
673 }
674
675 /// <summary>
676 /// Gets the font face of the read font property.
677 /// Empty string for default font.
678 /// </summary>
679 public string FontFace
680 {
681 get
682 {
683 if( _defaultFont.Length > 0)
684 {
685 string [] fontSplit = _defaultFont.Split( new char[]{','});
686 if(fontSplit.Length > 0)
687 return fontSplit[0].Trim();
688 }
689
690 return "";
691 }
692 }
693
694 /// <summary>
695 /// Gets the font size of the read font property.
696 /// 0 for default font size
697 /// </summary>
698 public double FontSize
699 {
700 get
701 {
702 if( _defaultFont.Length > 0)
703 {
704 string [] fontSplit = _defaultFont.Split( new char[]{','});
705 if(fontSplit.Length > 1)
706 return double.Parse(fontSplit[1].Trim());
707 }
708
709 return 0.0;
710 }
711 }
712
713 /// <summary>
714 /// Gets the character set of the read font property
715 /// 1 for default
716 /// </summary>
717 public int CharacterSet
718 {
719 get
720 {
721 if( _defaultFont.Length > 0)
722 {
723 string [] fontSplit = _defaultFont.Split( new char[]{','});
724 if(fontSplit.Length > 2)
725 return Int32.Parse(fontSplit[2].Trim());
726 }
727
728 return 0;
729 }
730 }
731
732 /// <summary>
733 /// Gets the codepage depending on the read font property
734 /// </summary>
735 public int CodePage
736 {
737 get
738 {
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
742 if(_culture != null)
743 {
744 return _culture.TextInfo.ANSICodePage;
745 }
746
747 int nRet = 1252; // default codepage windows-1252
748
749 int nCSet = CharacterSet;
750
751 switch(nCSet)
752 {
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
765 }
766
767 return nRet;
768 }
769 }
770
771 /// <summary>
772 /// Gets the assiciated culture info
773 /// </summary>
774 public CultureInfo Culture
775 {
776 get { return _culture; }
777 }
778
779 /// <summary>
780 /// Implement IDisposable.
781 /// </summary>
782 public void Dispose()
783 {
784 Dispose(true);
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);
791 }
792
793 /// <summary>
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
797 /// can be disposed.
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.
801 /// </summary>
802 /// <param name="disposing">disposing flag</param>
803 private void Dispose(bool disposing)
804 {
805 // Check to see if Dispose has already been called.
806 if(!this.disposed)
807 {
808 // If disposing equals true, dispose all managed
809 // and unmanaged resources.
810 if(disposing)
811 {
812 // Dispose managed resources.
813 _binaryFileData = null;
814 }
815
816
817 }
818 disposed = true;
819 }
820 }
821 }