fix include file case
[reactos.git] / irc / TechBot / CHMLibrary / Storage / CHMStream.cs
1 using System;
2 using System.Diagnostics;
3 using System.Text;
4 using System.Data;
5 using System.Text.RegularExpressions;
6 using System.Collections;
7 using System.Collections.Specialized;
8 using System.IO;
9 using System.Runtime.InteropServices;
10
11 namespace CHMStream
12 {
13 /// <summary>
14 /// Summary description for CHMFile.
15 /// </summary>
16 ///
17 public class CHMStream : IDisposable
18 {
19 public MemoryStream OpenStream(chmUnitInfo Info)
20 {
21 if (Info==null)
22 return null;
23
24 MemoryStream st=new MemoryStream();
25 this.ExtractFile(Info,st);
26 return st;
27 }
28
29 public MemoryStream OpenStream(string FileName)
30 {
31 chmUnitInfo info=this.GetFileInfo(FileName);
32 if (info==null)
33 return null;
34 return OpenStream(info);
35 }
36
37 private string m_CHMFileName;
38 public string CHMFileName
39 {
40 get
41 {
42 return m_CHMFileName;
43 }
44 }
45
46 public CHMStream()
47 {
48 }
49
50 public CHMStream(string CHMFileName)
51 {
52 OpenCHM(CHMFileName);
53 }
54
55 public void OpenCHM(string CHMFileName)
56 {
57 m_CHMFileName=CHMFileName;
58 FileInfo fi=new FileInfo(m_CHMFileName);
59 Dir=fi.DirectoryName;
60 m_CHMName=fi.Name;
61 fi=null;
62 chm_open(m_CHMFileName);
63 }
64
65 private bool m_bCHMLoaded=false;
66 public bool CHMLoaded
67 {
68 get
69 {
70 return m_bCHMLoaded;
71 }
72 }
73
74 private string m_CHMName="";
75 public string CHMName
76 {
77 get
78 {
79 return m_CHMName;
80 }
81 }
82
83 private string Dir="";
84 private string m_FileFind="";
85 private string m_FileFindLastPart="";
86 private chmUnitInfo m_FileInfo=null;
87 private int m_FileCount=0;
88 public void FindFile(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
89 {
90 string LocalFile=Info.path;
91 LocalFile=LocalFile.Replace("/",@"\");
92 if (!LocalFile.StartsWith(@"\"))
93 LocalFile=@"\"+LocalFile;
94 LocalFile=LocalFile.ToLower();
95
96 if (m_FileFind.Length<=LocalFile.Length)
97 {
98 if (LocalFile.IndexOf(m_FileFind)==LocalFile.Length-m_FileFind.Length)
99 {
100 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS;
101 m_FileInfo=Info;
102 return;
103 }
104 }
105 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
106 }
107
108 public void FileCount(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
109 {
110 m_FileCount++;
111 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
112 }
113
114 private ArrayList m_FileList=null;
115 private string m_strByExt="";
116 public void FileList(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
117 {
118 m_FileList.Add(Info.path);
119 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
120 }
121
122 public void FileListByExtension(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
123 {
124 FileInfo fi=new FileInfo(Info.path);
125 if (fi.Extension.ToLower()==m_strByExt.ToLower())
126 m_FileList.Add(Info.path);
127 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
128 }
129
130 public void FindFileIndex(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
131 {
132 if (m_FileCount==m_FileFindIndex)
133 {
134 m_FileInfo=Info;
135 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS;
136 }
137 else
138 {
139 m_FileCount++;
140 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
141 }
142 }
143
144 public int GetFileCount()
145 {
146 if (!m_bCHMLoaded)
147 return 0;
148
149 m_FileCount=0;
150 this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileCount);
151 this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);
152 this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileCount);
153 return m_FileCount;
154 }
155
156 public ArrayList GetFileList()
157 {
158 if (!m_bCHMLoaded)
159 return null;
160
161 m_FileList=null;
162 m_FileList=new ArrayList(1000);
163 this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileList);
164 this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);
165 this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileList);
166 return m_FileList;
167 }
168
169 public ArrayList GetFileListByExtenstion(string Ext)
170 {
171 if (!m_bCHMLoaded)
172 return null;
173
174 m_FileList=null;
175 m_FileList=new ArrayList(1000);
176 m_strByExt=Ext;
177 this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileListByExtension);
178 this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);
179 this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileListByExtension);
180 return m_FileList;
181 }
182
183 public chmUnitInfo GetFileInfo(string FileName)
184 {
185 if (!m_bCHMLoaded)
186 return null;
187
188 m_FileFind=FileName.ToLower().Replace("/",@"\");
189
190 // Remove all leading '..\'
191 do
192 {
193 if (m_FileFind.StartsWith(@"..\"))
194 m_FileFind=m_FileFind.Substring(3);
195 else
196 break;
197 }
198 while(true);
199
200 if (!m_FileFind.StartsWith(@"\"))
201 m_FileFind=@"\"+m_FileFind;
202
203 string []parts=m_FileFind.Split('\\');
204 m_FileFindLastPart=@"\"+parts[parts.GetUpperBound(0)];
205
206 this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFile);
207 m_FileInfo=null;
208 this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);
209 this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFile);
210 return m_FileInfo;
211 }
212
213 private int m_FileFindIndex=0;
214 public chmUnitInfo GetFileInfo(int FileIndex)
215 {
216 if (!m_bCHMLoaded)
217 return null;
218
219 m_FileFindIndex=FileIndex;
220
221 this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFileIndex);
222 m_FileCount=0;
223 m_FileInfo=null;
224 this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL);
225 this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFileIndex);
226 return m_FileInfo;
227 }
228
229 public chmUnitInfo GetFileInfoByExtension(string Ext)
230 {
231 this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFileByExtension);
232 m_FileInfo=null;
233 m_FileFind=Ext.ToLower();
234 this.chm_enumerate(CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_ALL);
235 this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFileByExtension);
236 return m_FileInfo;
237 }
238
239 public bool ExtractFile(string FileName, System.IO.Stream st)
240 {
241 if (!m_bCHMLoaded)
242 return false;
243
244 chmUnitInfo Info=GetFileInfo(FileName);
245 return ExtractFile(Info,st);
246 }
247
248 public bool ExtractFile(chmUnitInfo Info, System.IO.Stream st)
249 {
250 if (!m_bCHMLoaded)
251 return false;
252
253 if (Info==null)
254 return false;
255 else
256 {
257 chm_retrieve_object(Info,st,0,Info.length);
258 }
259 return true;
260 }
261
262 public string ExtractTextFile(string FileName)
263 {
264 if (!m_bCHMLoaded)
265 return "CHM File not loaded";
266
267 chmUnitInfo Info=GetFileInfo(FileName);
268 return ExtractTextFile(Info);
269 }
270
271 public string ExtractTextFile(chmUnitInfo Info)
272 {
273 if (!m_bCHMLoaded)
274 return "CHM File not loaded";
275
276 if (Info==null)
277 return "";
278
279 if (Info.path.Length>=2)
280 {
281 if (Info.path.Substring(0,2).CompareTo("/#")==0)
282 return "";
283 if (Info.path.Substring(0,2).CompareTo("/$")==0)
284 return "";
285 }
286
287 MemoryStream st=new MemoryStream((int)Info.length);
288 this.chm_retrieve_object(Info,st,0,Info.length);
289
290 if (st.Length==0)
291 return "";
292
293 string Text="";
294
295 ASCIIEncoding ascii=new ASCIIEncoding();
296 Text=ascii.GetString(st.ToArray(),0,50);
297
298 // UTF Decoding
299 if (Text.IndexOf("UTF-8")!=-1)
300 {
301 UTF8Encoding utf8 = new UTF8Encoding();
302 Text=utf8.GetString(st.ToArray(),0,(int)st.Length);
303 }
304 else
305 Text=ascii.GetString(st.ToArray(),0,(int)st.Length);
306
307 return Text;
308 }
309
310 public void FindFileByExtension(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
311 {
312 if ((Info.path.StartsWith("::")) || (Info.path.StartsWith("#")) ||(Info.path.StartsWith("$")))
313 {
314 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
315 return;
316 }
317
318 FileInfo Fi=new FileInfo(Info.path);
319 if (Fi.Extension.ToLower()==m_FileFind)
320 {
321 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS;
322 m_FileInfo=Info;
323 }
324 else
325 Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
326 }
327
328 public bool GetCHMParts(string Url, ref string CHMFileName, ref string FileName, ref string Anchor)
329 {
330 Regex ParseURLRegEx= new Regex( @"ms-its:(?'CHMFile'.*)::(?'Topic'.*)", RegexOptions.IgnoreCase| RegexOptions.Singleline | RegexOptions.ExplicitCapture| RegexOptions.IgnorePatternWhitespace| RegexOptions.Compiled);
331
332 // Parse URL - Get CHM Filename & Page Name
333 // Format 'ms-its:file name.chm::/topic.htm'
334 if (ParseURLRegEx.IsMatch(Url))
335 {
336 Match m=ParseURLRegEx.Match(Url);
337 CHMFileName=m.Groups["CHMFile"].Value;
338 string Topic=m.Groups["Topic"].Value;
339 int idx=Topic.IndexOf("#");
340 if (idx>-1)
341 {
342 FileName=Topic.Substring(0,idx);
343 Anchor=Topic.Substring(idx+1);
344 }
345 else
346 FileName=Topic;
347 return true;
348 }
349 return false;
350 }
351
352 private string m_TempDir="";
353 string ReplaceFileName(Match m)
354 {
355 string strReplace = m.ToString();
356
357 // Process string.
358 if (m.Groups["FileName"]==null)
359 return strReplace;
360
361 string FileName=m.Groups["FileName"].Value;
362 string FileName2=FileName.Replace("/",@"\");
363 int idx=FileName2.IndexOf("::");
364 if (idx!=-1)
365 FileName2=FileName2.Substring(idx+2);
366 string []parts=FileName2.Split('\\');
367 string NewName=@"file://"+m_TempDir+parts[parts.GetUpperBound(0)];
368
369 strReplace=strReplace.Replace(FileName,NewName);
370 return strReplace;
371 }
372
373 public ArrayList GetFileList(ref string Text, string TempDir)
374 {
375 if (!m_bCHMLoaded)
376 return null;
377
378 m_TempDir=TempDir;
379
380 ArrayList FilesList=new ArrayList();
381
382 // Parse HTML for CCS, ima, etc
383 string regexContent=@"[\x2f a-zA-Z0-9\x5C\x2E\x28\x29\x23\x24\x25\x26\x27\x22\x21\x3F\x3E\x3D\x3C\x3B\x3A\x5B\x5D\x5E\x5F\x7D\x7C\x7B\x7E\x40\x2D\x2C\x2B\x2A]*\s*";
384 string regexFileName=@"\s*=\s*[""|'](?'FileName'[^""^']*)[""|']\s*";
385
386 Regex ScriptRegex = new Regex(@"<script[^>]*>.*</script>",
387 RegexOptions.IgnoreCase
388 | RegexOptions.Multiline
389 | RegexOptions.Singleline
390 | RegexOptions.IgnorePatternWhitespace
391 | RegexOptions.Compiled);
392
393 Regex XMLRegex = new Regex(@"<\?xml.*\?>",
394 RegexOptions.IgnoreCase
395 | RegexOptions.Multiline
396 | RegexOptions.Singleline
397 | RegexOptions.IgnorePatternWhitespace
398 | RegexOptions.Compiled);
399
400 Regex XMLRegex2 = new Regex(@"<xml[^>]*>.*</xml>",
401 RegexOptions.IgnoreCase
402 | RegexOptions.Multiline
403 | RegexOptions.Singleline
404 | RegexOptions.IgnorePatternWhitespace
405 | RegexOptions.Compiled);
406
407 Regex SRCRegex = new Regex(
408 @"src"+regexFileName,
409 RegexOptions.IgnoreCase
410 | RegexOptions.Multiline
411 | RegexOptions.Singleline
412 | RegexOptions.IgnorePatternWhitespace
413 | RegexOptions.Compiled);
414
415 Regex StyleSheetRegex = new Regex(
416 @"<link\s*"+regexContent+@"rel\s*=\s*[""|']stylesheet[""|']\s*"+regexContent + "href"+regexFileName,
417 RegexOptions.IgnoreCase
418 | RegexOptions.Multiline
419 | RegexOptions.Singleline
420 | RegexOptions.IgnorePatternWhitespace
421 | RegexOptions.Compiled);
422
423 // Remove Script Tags
424 Text=ScriptRegex.Replace(Text,"");
425
426 // Remove XML Tags
427 Text=XMLRegex.Replace(Text,"");
428 Text=XMLRegex2.Replace(Text,"");
429
430
431 StringBuilder s=new StringBuilder(Text);
432
433 if (StyleSheetRegex.IsMatch(Text))
434 {
435 Match m = StyleSheetRegex.Match(Text);
436 while (m.Success)
437 {
438 string FileName=m.Groups["FileName"].ToString();
439 FilesList.Add(FileName);
440 m=m.NextMatch();
441 }
442 Text=StyleSheetRegex.Replace(Text,new MatchEvaluator(ReplaceFileName));
443 }
444
445 if (SRCRegex.IsMatch(Text))
446 {
447 Match m = SRCRegex.Match(Text);
448 while (m.Success)
449 {
450 string FileName=m.Groups["FileName"].ToString();
451 FilesList.Add(FileName);
452 m=m.NextMatch();
453 }
454 Text=SRCRegex.Replace(Text,new MatchEvaluator(ReplaceFileName));
455 }
456
457 return FilesList;
458 }
459
460 public string GetHTMLAndFiles(string TempDir, string Url)
461 {
462 string HTMLText="";
463 if (TempDir.EndsWith(@"\")) TempDir=TempDir.Substring(TempDir.Length-1);
464
465 // Delete Temp Directory
466 if (Directory.Exists(TempDir))
467 Directory.Delete(TempDir,true);
468
469 // Create Temp Directory
470 if (!Directory.Exists(TempDir))
471 Directory.CreateDirectory(TempDir);
472
473 if (!TempDir.EndsWith(@"\")) TempDir+=@"\";
474
475 string m_TopicName="";
476
477 string m_CHMFile=CHMFileName;
478 string Anchor="";
479 if (!GetCHMParts(Url,ref m_CHMFile, ref m_TopicName, ref Anchor))
480 {
481 m_CHMFile=this.CHMFileName;
482 m_TopicName=Url;
483 }
484
485 if (m_TopicName=="")
486 return "#No TopicName defined in Url : "+ Url;
487
488 m_TopicName=m_TopicName.Replace("/",@"\");
489 if (!m_CHMFile.StartsWith(@"\"))
490 m_CHMFile=this.Dir+@"\"+m_CHMFile;
491
492 // Open CHM
493 CHMStream LocalCHM=this;
494
495 if (this.CHMFileName.ToLower().CompareTo(m_CHMFile.ToLower())!=0)
496 LocalCHM=new CHMStream(m_CHMFile);
497
498 // Get HTML
499 HTMLText=LocalCHM.ExtractTextFile(m_TopicName);
500 if (HTMLText=="")
501 return "#Failed to find Topic in CHM File : "+Url;
502
503 HTMLText=GetFiles(TempDir, HTMLText, LocalCHM);
504
505 return HTMLText;
506 }
507
508 public string GetFiles(string TempDir, string HTMLText, CHMStream chm)
509 {
510 return GetFiles(TempDir, HTMLText, chm,0);
511 }
512
513 public string GetFiles(string TempDir, string HTMLText, CHMStream chm, int Level)
514 {
515 // Get FilesList & Extract Files to Temp Dir
516 ArrayList FileList=chm.GetFileList(ref HTMLText, TempDir);
517 if (FileList!=null)
518 {
519 foreach(object obj in FileList)
520 {
521 string FileName=(string)obj;
522
523 string CHMFileName="";
524 string TopicName="";
525 string Anchor="";
526 CHMStream NewCHM=chm;
527 if (GetCHMParts(FileName,ref CHMFileName, ref TopicName, ref Anchor))
528 {
529 NewCHM=new CHMStream(chm.Dir+@"\"+CHMFileName);
530 if (!NewCHM.CHMLoaded)
531 NewCHM=null;
532 FileName=TopicName;
533 }
534 else
535 {
536 CHMFileName=chm.CHMFileName;
537 NewCHM=chm;
538 TopicName=FileName;
539 }
540 if (NewCHM==null)
541 continue;
542
543 if (((FileName.ToLower().EndsWith(".htm")) || (FileName.ToLower().EndsWith(".html"))) && (Level<2))
544 {
545 string HTMLText2=NewCHM.ExtractTextFile(FileName);
546 FileInfo Fi=new FileInfo(FileName);
547 string path=TempDir+Fi.Name;
548 HTMLText2=GetFiles(TempDir,HTMLText2, chm, Level+1);
549
550 if (File.Exists(path))
551 File.Delete(path);
552
553 StreamWriter st=new StreamWriter(path);
554 st.WriteLine(HTMLText2);
555 st.Close();
556 }
557 else
558 {
559 // Extract all other files as is
560 string FileName2=FileName.Replace("/",@"\");
561 if (FileName2.Substring(0,1)==@"\")
562 FileName2=FileName2.Substring(1);
563
564 string []parts=FileName2.Split('\\');
565 string path=TempDir+parts[parts.GetUpperBound(0)];
566 if (File.Exists(path))
567 File.Delete(path);
568 System.IO.FileStream st=new FileStream(path,FileMode.CreateNew);
569 NewCHM.ExtractFile(FileName2,st);
570 st.Close();
571 }
572 }
573 }
574
575 // return HTML string of main page
576 return HTMLText;
577 }
578
579 #region CHMStream Enums
580 // the two available spaces in a CHM file
581 // N.B.: The format supports arbitrarily many spaces, but only
582 // two appear to be used at present.
583 public enum CHM_COMPRESSION { CHM_UNCOMPRESSED=0, CHM_COMPRESSED=1};
584
585 // resolve a particular object from the archive
586 public enum CHM_RESOLVE { CHM_RESOLVE_SUCCESS=0, CHM_RESOLVE_FAILURE=1};
587
588 // retrieve part of an object from the archive
589 public enum CHM_ENUMERATE
590 {
591 None=0,
592 CHM_ENUMERATE_NORMAL =1,
593 CHM_ENUMERATE_META =2,
594 CHM_ENUMERATE_SPECIAL =4,
595 CHM_ENUMERATE_FILES =8,
596 CHM_ENUMERATE_DIRS =16,
597 CHM_ENUMERATE_ALL =31};
598
599 public enum CHM_ENUMERATOR
600 {
601 CHM_ENUMERATOR_FAILURE =0,
602 CHM_ENUMERATOR_SUCCESS =2,
603 CHM_ENUMERATOR_CONTINUE =1
604 };
605 #endregion
606
607 #region Internal Parameters
608 private int ffs(int val)
609 {
610 int bit=1;
611 int idx=1;
612 while (bit != 0 && (val & bit) == 0)
613 {
614 bit <<= 1;
615 ++idx;
616 }
617 if (bit == 0)
618 return 0;
619 else
620 return idx;
621 }
622
623 // names of sections essential to decompression
624 private const string _CHMU_RESET_TABLE = @"::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";
625 private const string _CHMU_LZXC_CONTROLDATA = @"::DataSpace/Storage/MSCompressed/ControlData";
626 private const string _CHMU_CONTENT = @"::DataSpace/Storage/MSCompressed/Content";
627 private const string _CHMU_SPANINFO = @"::DataSpace/Storage/MSCompressed/SpanInfo";
628
629 private UInt64 dir_offset=0;
630 private UInt64 dir_len=0;
631 private UInt64 data_offset=0;
632 private Int32 index_root=0;
633 private Int32 index_head=0;
634 private UInt32 block_len=0;
635
636 private chmUnitInfo rt_unit;
637 private chmUnitInfo cn_unit;
638 private chmLzxcResetTable reset_table;
639 private bool compression_enabled=false;
640
641 // LZX control data
642 private int window_size=0;
643 private UInt32 reset_interval=0;
644 private UInt32 reset_blkcount=0;
645 private BinaryReader st=null;
646
647 // decompressor state
648 private lzw lzx_state;
649 private int lzx_last_block=0;
650 #endregion
651
652 #region Open CHM Stream
653 private bool CheckSig(string Sig1, char[] Sig2)
654 {
655 int i=0;
656 foreach(char ch in Sig1.ToCharArray())
657 {
658 if(ch!=Sig2[i])
659 return false;
660 i++;
661 }
662 return true;
663 }
664
665 // open an ITS archive
666 private bool chm_open(string filename)
667 {
668 chmItsfHeader itsfHeader=new chmItsfHeader();
669 chmItspHeader itspHeader=new chmItspHeader();
670 chmUnitInfo uiSpan=new chmUnitInfo();
671 chmUnitInfo uiLzxc=new chmUnitInfo();
672 chmLzxcControlData ctlData=new chmLzxcControlData();
673
674 m_bCHMLoaded=false;
675 if (!File.Exists(filename))
676 return false;
677
678 st=new BinaryReader(File.OpenRead(filename));
679 if (st==null)
680 return false;
681
682 // read and verify header
683 if (itsfHeader.Read_itsf_header(st)==0)
684 {
685 st.Close();
686 return false;
687 }
688 st.BaseStream.Seek((long)itsfHeader.dir_offset,SeekOrigin.Begin);
689
690 // stash important values from header
691 dir_offset = itsfHeader.dir_offset;
692 dir_len = itsfHeader.dir_len;
693 data_offset = itsfHeader.data_offset;
694
695 // now, read and verify the directory header chunk
696 if (itspHeader.Read_itsp_header(st)==0)
697 {
698 st.Close();
699 return false;
700 }
701
702 // grab essential information from ITSP header
703 dir_offset += (UInt64)itspHeader.header_len;
704 dir_len -= (UInt64)itspHeader.header_len;
705 index_root = itspHeader.index_root;
706 index_head = itspHeader.index_head;
707 block_len = itspHeader.block_len;
708
709 // if the index root is -1, this means we don't have any PMGI blocks.
710 // as a result, we must use the sole PMGL block as the index root
711
712 if (index_root == -1)
713 index_root = index_head;
714
715 compression_enabled=true;
716
717 // prefetch most commonly needed unit infos
718 // if (CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_SPANINFO, ref uiSpan)
719 // || uiSpan.space == CHM_COMPRESSION.CHM_COMPRESSED ||
720 if (CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_RESET_TABLE, ref rt_unit)
721 || rt_unit.space == CHM_COMPRESSION.CHM_COMPRESSED
722 || CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_CONTENT,ref cn_unit)
723 || cn_unit.space == CHM_COMPRESSION.CHM_COMPRESSED
724 || CHM_RESOLVE.CHM_RESOLVE_SUCCESS != chm_resolve_object(_CHMU_LZXC_CONTROLDATA, ref uiLzxc)
725 || uiLzxc.space == CHM_COMPRESSION.CHM_COMPRESSED)
726 {
727 compression_enabled=false;
728 // st.Close();
729 // return false;
730 }
731
732 // try to read span
733 // N.B.: we've already checked that uiSpan is in the uncompressed section,
734 // so this should not require attempting to decompress, which may
735 // rely on having a valid "span"
736
737 if (compression_enabled)
738 {
739 reset_table=new chmLzxcResetTable();
740 st.BaseStream.Seek((long)((long)data_offset + (long)rt_unit.start),SeekOrigin.Begin);
741 if (reset_table.Read_lzxc_reset_table(st)!=1)
742 {
743 compression_enabled=false;
744 }
745 }
746
747 if (compression_enabled)
748 {
749 // read control data
750 ctlData=new chmLzxcControlData();
751 st.BaseStream.Seek((long)((long)data_offset + (long)uiLzxc.start),SeekOrigin.Begin);
752 if (ctlData.Read_lzxc_control_data(st)!=1)
753 {
754 compression_enabled=false;
755 }
756
757 window_size = (int)ctlData.windowSize;
758 reset_interval = ctlData.resetInterval;
759 try
760 {
761 reset_blkcount = (uint)(reset_interval /
762 (window_size / 2) *
763 ctlData.windowsPerReset);
764 }
765 catch(Exception)
766 {
767 reset_blkcount=0;
768 }
769 }
770
771 m_bCHMLoaded=true;
772
773 return true;
774 }
775 #endregion
776
777 #region Close CHM Stream
778 // close an ITS archive
779 private void chm_close()
780 {
781 if (!m_bCHMLoaded)
782 return;
783
784 st.Close();
785 lzx_state.LZXteardown();
786 lzx_state=null;
787 }
788 #endregion
789
790 #region Find File in CHM Stream
791 // resolve a particular object from the archive
792 private CHMStream.CHM_RESOLVE chm_resolve_object(string objPath, ref chmUnitInfo ui)
793 {
794 Int32 curPage;
795
796 // starting page
797 curPage = index_root;
798
799 // until we have either returned or given up
800 while (curPage != -1)
801 {
802 st.BaseStream.Seek((long)((long)dir_offset + (long)(curPage*block_len)),SeekOrigin.Begin);
803
804 char[] sig=st.ReadChars(4);
805 st.BaseStream.Seek(-4,SeekOrigin.Current);
806 if (CheckSig("PMGL",sig))
807 {
808 chmPmglHeader PmglHeader=new chmPmglHeader();
809 if (PmglHeader.Read_pmgl_header(st)==1)
810 {
811 // scan block
812 ui=PmglHeader.FindObject(st,block_len,objPath);
813 if (ui== null)
814 return CHMStream.CHM_RESOLVE.CHM_RESOLVE_FAILURE;
815
816 // parse entry and return
817 return CHMStream.CHM_RESOLVE.CHM_RESOLVE_SUCCESS;
818 }
819 }
820 else if (CheckSig("PMGI",sig))
821 {
822 chmPmgiHeader pmgiHeader=new chmPmgiHeader();
823 pmgiHeader.Read_pmgi_header(st);
824 curPage = pmgiHeader._chm_find_in_PMGI(st, block_len, objPath);
825 }
826 else
827 // else, we are confused. give up.
828 return CHMStream.CHM_RESOLVE.CHM_RESOLVE_FAILURE;
829 }
830
831 // didn't find anything. fail.
832 return CHMStream.CHM_RESOLVE.CHM_RESOLVE_FAILURE;
833 }
834 #endregion
835
836 #region Extract File from CHM Stream
837
838 // * utility methods for dealing with compressed data
839 // get the bounds of a compressed block. return 0 on failure
840 private int _chm_get_cmpblock_bounds(System.IO.BinaryReader st, UInt64 block, ref UInt64 start, ref UInt64 len)
841 {
842 // for all but the last block, use the reset table
843 if (block < reset_table.block_count-1)
844 {
845 // unpack the start address
846 st.BaseStream.Seek((long)data_offset + (long)rt_unit.start + (long)reset_table.table_offset + (long)(block*8),SeekOrigin.Begin);
847 start=st.ReadUInt64();
848 len=st.ReadUInt64();
849 }
850
851 // for the last block, use the span in addition to the reset table
852 else
853 {
854 // unpack the start address
855 st.BaseStream.Seek((long)data_offset + (long)rt_unit.start + (long)reset_table.table_offset + (long)(block*8),SeekOrigin.Begin);
856 start=st.ReadUInt64();
857 len = reset_table.compressed_len;
858 }
859
860 // compute the length and absolute start address
861 len -= start;
862 start += data_offset + cn_unit.start;
863
864 return 1;
865 }
866
867 // decompress the block. must have lzx_mutex.
868 private ulong _chm_decompress_block(UInt64 block, System.IO.Stream OutBuffer)
869 {
870 // byte []cbuffer = new byte(reset_table.block_len + 6144);
871 ulong cmpStart=0; // compressed start
872 ulong cmpLen=0; // compressed len
873 UInt32 blockAlign = (UInt32)(block % reset_blkcount); // reset intvl. aln.
874
875 // check if we need previous blocks
876 if (blockAlign != 0)
877 {
878 /* fetch all required previous blocks since last reset */
879 for (UInt32 i = blockAlign; i > 0; i--)
880 {
881 UInt32 curBlockIdx = (UInt32)(block-i);
882
883 /* check if we most recently decompressed the previous block */
884 if ((ulong)lzx_last_block != curBlockIdx)
885 {
886 if ((curBlockIdx % reset_blkcount)==0)
887 {
888 lzx_state.LZXreset();
889 }
890
891 _chm_get_cmpblock_bounds(st,curBlockIdx, ref cmpStart, ref cmpLen);
892 st.BaseStream.Seek((long)cmpStart,SeekOrigin.Begin);
893 if (lzx_state.LZXdecompress(st,OutBuffer, ref cmpLen, ref reset_table.block_len) != lzw.DECR_OK)
894 return (Int64)0;
895 }
896 lzx_last_block = (int)(curBlockIdx);
897 }
898 }
899 else
900 {
901 if ((block % reset_blkcount)==0)
902 {
903 lzx_state.LZXreset();
904 }
905 }
906
907 // decompress the block we actually want
908 if (_chm_get_cmpblock_bounds(st, block, ref cmpStart, ref cmpLen)==0)
909 return 0;
910
911 st.BaseStream.Seek((long)cmpStart,SeekOrigin.Begin);
912
913 if (lzx_state.LZXdecompress(st, OutBuffer, ref cmpLen,ref reset_table.block_len) != lzw.DECR_OK)
914 return (Int64)0;
915
916 lzx_last_block = (int)block;
917
918 // XXX: modify LZX routines to return the length of the data they
919 // * decompressed and return that instead, for an extra sanity check.
920 return reset_table.block_len;
921 }
922
923 // grab a region from a compressed block
924 private ulong _chm_decompress_region(Stream buf, ulong start, ulong len)
925 {
926 ulong nBlock, nOffset;
927 ulong nLen;
928 ulong gotLen;
929 // byte [] ubuffer=null;
930
931 if (len <= 0)
932 return (Int64)0;
933
934 // figure out what we need to read
935 nBlock = start / reset_table.block_len;
936 nOffset = start % reset_table.block_len;
937 nLen = len;
938 if (nLen > (reset_table.block_len - nOffset))
939 nLen = reset_table.block_len - nOffset;
940
941 // data request not satisfied, so... start up the decompressor machine
942 if (lzx_state==null)
943 {
944 int window_size = ffs(this.window_size) - 1;
945 lzx_last_block = -1;
946
947 lzx_state=new lzw();
948 lzx_state.LZXinit(window_size);
949 }
950
951 // decompress some data
952 MemoryStream ms=new MemoryStream((int)reset_table.block_len+6144);
953 gotLen = _chm_decompress_block(nBlock, ms);
954 if (gotLen < nLen)
955 nLen = gotLen;
956
957 // memcpy(buf, ubuffer+nOffset, (unsigned int)nLen);
958 ms.Position=(long)nOffset;
959 for(ulong i=0;i<nLen;i++)
960 buf.WriteByte((byte)ms.ReadByte());
961 buf.Flush();
962 return nLen;
963 }
964
965 // retrieve (part of) an object
966 private ulong chm_retrieve_object(chmUnitInfo ui, Stream buf, ulong addr, ulong len)
967 {
968 // starting address must be in correct range
969 if (addr < 0 || addr >= (ulong)ui.length)
970 return (Int64)0;
971
972 // clip length
973 if (addr + (ulong)len > (ulong)ui.length)
974 len = (ulong)ui.length - (ulong)addr;
975
976 // if the file is uncompressed, it's simple
977 if (ui.space == CHMStream.CHM_COMPRESSION.CHM_UNCOMPRESSED)
978 {
979 // read data
980 long FilePos=st.BaseStream.Position;
981 st.BaseStream.Seek((long)((long)data_offset + (long)ui.start + (long)addr),SeekOrigin.Begin);
982 // byte [] buffer=st.ReadBytes((int)len);
983 buf.Write(st.ReadBytes((int)len),0,(int) len);
984 st.BaseStream.Seek(FilePos,SeekOrigin.Begin);
985 return (ulong)len;
986 }
987
988 // else if the file is compressed, it's a little trickier
989 else // ui->space == CHM_COMPRESSED
990 {
991 if (lzx_state!=null)
992 {
993 lzx_state.LZXteardown();
994 lzx_state=null;
995 }
996 ulong swath=0, total=0;
997 do
998 {
999 if (!compression_enabled)
1000 return total;
1001
1002 // swill another mouthful
1003 swath = _chm_decompress_region(buf, ui.start + addr, len);
1004
1005 // if we didn't get any...
1006 if (swath == 0)
1007 {
1008 Trace.Assert((total!=ui.length),"De-compress failed","Length Required = "+ui.length.ToString()+" Length returned = "+total.ToString());
1009 return total;
1010 }
1011
1012 // update stats
1013 total += swath;
1014 len -= swath;
1015 addr += swath;
1016 } while (len != 0);
1017 lzx_state=null;
1018
1019 Trace.Assert((len!=ui.length),"De-compress failed","Length Required = "+ui.length.ToString()+" Length returned = "+len.ToString());
1020 return len;
1021 }
1022 }
1023 #endregion
1024
1025 #region Enumerate functions
1026 // Enumerate the objects in the .chm archive
1027 // Use delegate to handle callback
1028
1029 public delegate void CHMFileFound(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status);
1030 public event CHMFileFound CHMFileFoundEvent;
1031
1032 public void OnFileFound(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status)
1033 {
1034 if (CHMFileFoundEvent!=null)
1035 CHMFileFoundEvent(Info,ref Status);
1036 }
1037 private int chm_enumerate(CHM_ENUMERATE what)
1038 {
1039 Int32 curPage;
1040
1041 // buffer to hold whatever page we're looking at
1042 chmPmglHeader header;
1043 uint end=0;
1044 uint cur=0;
1045
1046 // the current ui
1047 chmUnitInfo ui= new chmUnitInfo();
1048 CHMStream.CHM_ENUMERATE flag=CHMStream.CHM_ENUMERATE.None;
1049
1050 // starting page
1051 curPage = index_head;
1052
1053 // until we have either returned or given up
1054 while (curPage != -1)
1055 {
1056 st.BaseStream.Seek((long)((long)dir_offset + (long)(curPage*block_len)),SeekOrigin.Begin);
1057
1058 // figure out start and end for this page
1059 cur = (uint)st.BaseStream.Position;
1060
1061 header=new chmPmglHeader();
1062 if (header.Read_pmgl_header(st)==0)
1063 return 0;
1064
1065 end = (uint)(st.BaseStream.Position + block_len - (header.free_space)- chmPmglHeader._CHM_PMGL_LEN);
1066
1067 // loop over this page
1068 while (st.BaseStream.Position < end)
1069 {
1070 if (header._chm_parse_PMGL_entry(st,ref ui)==0)
1071 return 0;
1072
1073 // check for DIRS
1074 if (ui.length == 0 && ((what & CHM_ENUMERATE.CHM_ENUMERATE_DIRS)==0))
1075 continue;
1076
1077 // check for FILES
1078 if (ui.length != 0 && ((what & CHM_ENUMERATE.CHM_ENUMERATE_FILES)==0))
1079 continue;
1080
1081 // check for NORMAL vs. META
1082 if (ui.path[0] == '/')
1083 {
1084 // check for NORMAL vs. SPECIAL
1085 if (ui.path.Length>2)
1086 {
1087 if (ui.path[1] == '#' || ui.path[1] == '$')
1088 flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_SPECIAL;
1089 else
1090 flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_NORMAL;
1091 }
1092 else
1093 flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_META;
1094 if ((what & flag)==0)
1095 continue;
1096 }
1097
1098 // call the enumerator
1099 {
1100 CHMStream.CHM_ENUMERATOR status = CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE;
1101 OnFileFound(ui,ref status);
1102
1103 switch (status)
1104 {
1105 case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_FAILURE:
1106 return 0;
1107
1108 case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE:
1109 break;
1110
1111 case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS:
1112 return 1;
1113
1114 default:
1115 break;
1116 }
1117 }
1118 }
1119
1120 // advance to next page
1121 curPage = header.block_next;
1122 }
1123
1124 return 1;
1125 }
1126 #endregion
1127
1128 #region IDisposable Members
1129
1130 private bool disposed=false;
1131 public void Dispose()
1132 {
1133 Dispose(true);
1134 // This object will be cleaned up by the Dispose method.
1135 // Therefore, you should call GC.SupressFinalize to
1136 // take this object off the finalization queue
1137 // and prevent finalization code for this object
1138 // from executing a second time.
1139 GC.SuppressFinalize(this);
1140 }
1141
1142 /// <summary>
1143 /// Dispose(bool disposing) executes in two distinct scenarios.
1144 /// If disposing equals true, the method has been called directly
1145 /// or indirectly by a user's code. Managed and unmanaged resources
1146 /// can be disposed.
1147 /// If disposing equals false, the method has been called by the
1148 /// runtime from inside the finalizer and you should not reference
1149 /// other objects. Only unmanaged resources can be disposed.
1150 /// </summary>
1151 /// <param name="disposing">disposing flag</param>
1152 private void Dispose(bool disposing)
1153 {
1154 // Check to see if Dispose has already been called.
1155 if(!this.disposed)
1156 {
1157 // If disposing equals true, dispose all managed
1158 // and unmanaged resources.
1159 if(disposing)
1160 {
1161 // Dispose managed resources.
1162 }
1163 }
1164 disposed = true;
1165 }
1166
1167 #endregion
1168 }
1169
1170 #region Structures used by CHM Storage
1171 public class BaseStructure
1172 {
1173 public bool CheckSig(string Sig1, char[] Sig2)
1174 {
1175 int i=0;
1176 foreach(char ch in Sig1.ToCharArray())
1177 {
1178 if (ch!=Sig2[i])
1179 return false;
1180 i++;
1181 }
1182 return true;
1183 }
1184
1185 // skip a compressed dword
1186 public void skip_cword(BinaryReader st)
1187 {
1188 byte b=0;
1189 while ((b=st.ReadByte())>= 0x80);
1190 }
1191
1192 // skip the data from a PMGL entry
1193 public void _chm_skip_PMGL_entry_data(BinaryReader st)
1194 {
1195 skip_cword(st);
1196 skip_cword(st);
1197 skip_cword(st);
1198 }
1199
1200 // parse a compressed dword
1201 public UInt64 _chm_parse_cword(BinaryReader st)
1202 {
1203 UInt64 accum = 0;
1204 byte temp=0;
1205 while ((temp=st.ReadByte()) >= 0x80)
1206 {
1207 accum <<= 7;
1208 accum += (ulong)(temp & 0x7f);
1209 }
1210
1211 return (accum << 7) + temp;
1212 }
1213
1214 // parse a utf-8 string into an ASCII char buffer
1215 public int _chm_parse_UTF8(BinaryReader st, UInt64 count, ref string path)
1216 {
1217 UTF8Encoding utf8=new UTF8Encoding();
1218 path=utf8.GetString(st.ReadBytes((int)count),0,(int)count);
1219 return 1;
1220 }
1221 }
1222
1223 public class chmUnitInfo
1224 {
1225 public UInt64 start=0;
1226 public UInt64 length=0;
1227 public CHMStream.CHM_COMPRESSION space=CHMStream.CHM_COMPRESSION.CHM_UNCOMPRESSED;
1228 public string path="";
1229 }
1230
1231 // structure of ITSF headers
1232 public class chmItsfHeader : BaseStructure
1233 {
1234 public const int _CHM_ITSF_V2_LEN=0x58;
1235 public const int _CHM_ITSF_V3_LEN=0x60;
1236
1237 public char[] signature=null; // 0 (ITSF)
1238 public Int32 version=0; // 4
1239 public Int32 header_len=0; // 8
1240 public Int32 unknown_000c=0; // c
1241 public UInt32 last_modified=0; // 10
1242 public UInt32 lang_id=0; // 14
1243 public Guid dir_uuid; // 18
1244 public Guid stream_uuid; // 28
1245 public UInt64 unknown_offset=0; // 38
1246 public UInt64 unknown_len=0; // 40
1247 public UInt64 dir_offset=0; // 48
1248 public UInt64 dir_len=0; // 50
1249 public UInt64 data_offset=0; // 58 (Not present before V3)
1250
1251 public int Read_itsf_header(BinaryReader st)
1252 {
1253 signature=st.ReadChars(4);
1254 if (CheckSig("ITSF",signature)==false)
1255 return 0;
1256
1257 version=st.ReadInt32();
1258 header_len=st.ReadInt32();
1259 unknown_000c=st.ReadInt32();
1260 last_modified=st.ReadUInt32();
1261 lang_id=st.ReadUInt32();
1262 dir_uuid=new Guid(st.ReadBytes(16));
1263 stream_uuid=new Guid(st.ReadBytes(16));
1264 unknown_offset=st.ReadUInt64();
1265 unknown_len=st.ReadUInt64();
1266 dir_offset=st.ReadUInt64();
1267 dir_len=st.ReadUInt64();
1268
1269 if (version==2)
1270 {
1271 if (header_len != chmItsfHeader._CHM_ITSF_V2_LEN)
1272 return 0;
1273 }
1274 else if (version==3)
1275 {
1276 if (header_len != chmItsfHeader._CHM_ITSF_V3_LEN)
1277 return 0;
1278 }
1279 else return 0;
1280
1281 if (version==3)
1282 data_offset=st.ReadUInt64();
1283 else
1284 data_offset = dir_offset + dir_len;
1285
1286 return 1;
1287 }
1288 }
1289
1290 // structure of ITSP headers
1291 public class chmItspHeader : BaseStructure
1292 {
1293 const int CHM_ITSP_V1_LEN=0x54;
1294
1295 public char[] signature=null; // 0 (ITSP)
1296 public Int32 version=0;
1297 public Int32 header_len=0;
1298 public Int32 unknown_000c=0;
1299 public UInt32 block_len=0;
1300 public Int32 blockidx_intvl=0;
1301 public Int32 index_depth=0;
1302 public Int32 index_root=0;
1303 public Int32 index_head=0;
1304 public Int32 unknown_0024=0;
1305 public Int32 num_blocks=0;
1306 public Int32 unknown_002c=0;
1307 public UInt32 lang_id=0;
1308 public Guid system_uuid;
1309 public Guid unknown_0044;
1310
1311 public int Read_itsp_header(BinaryReader st)
1312 {
1313 signature=st.ReadChars(4); // 0 (ITSP)
1314 if (CheckSig("ITSP",signature)==false)
1315 return 0;
1316
1317 version=st.ReadInt32();
1318 header_len=st.ReadInt32();
1319
1320 if (header_len!=CHM_ITSP_V1_LEN)
1321 return 0;
1322
1323 unknown_000c=st.ReadInt32();
1324 block_len=st.ReadUInt32();
1325 blockidx_intvl=st.ReadInt32();
1326 index_depth=st.ReadInt32();
1327 index_root=st.ReadInt32();
1328 index_head=st.ReadInt32();
1329 unknown_0024=st.ReadInt32();
1330 num_blocks=st.ReadInt32();
1331 unknown_002c=st.ReadInt32();
1332 lang_id=st.ReadUInt32();
1333 system_uuid=new Guid(st.ReadBytes(16));
1334 unknown_0044=new Guid(st.ReadBytes(16));
1335
1336 return 1;
1337 }
1338 }
1339
1340 public class chmPmglHeader : BaseStructure
1341 {
1342 public const int _CHM_PMGL_LEN=0x14;
1343 public char[] signature=null; // 0 (PMGL)
1344 public UInt32 free_space=0; // 4
1345 public UInt32 unknown_0008=0; // 8
1346 public Int32 block_prev=0; // c
1347 public Int32 block_next=0; // 10
1348
1349 public int Read_pmgl_header(BinaryReader st)
1350 {
1351 signature=st.ReadChars(4);
1352 if (CheckSig("PMGL",signature)==false)
1353 return 0;
1354
1355 free_space=st.ReadUInt32();
1356 unknown_0008=st.ReadUInt32();
1357 block_prev=st.ReadInt32();
1358 block_next=st.ReadInt32();
1359 return 1;
1360 }
1361
1362 // parse a PMGL entry into a chmUnitInfo struct; return 1 on success.
1363 public int _chm_parse_PMGL_entry(BinaryReader st, ref chmUnitInfo ui)
1364 {
1365 UInt64 strLen;
1366
1367 // parse str len
1368 strLen = _chm_parse_cword(st);
1369
1370 // parse path
1371 if (_chm_parse_UTF8(st, strLen, ref ui.path)==0)
1372 return 0;
1373
1374 // parse info
1375 ui.space = (CHMStream.CHM_COMPRESSION)_chm_parse_cword(st);
1376 ui.start = _chm_parse_cword(st);
1377 ui.length = _chm_parse_cword(st);
1378 return 1;
1379 }
1380
1381 public chmUnitInfo FindObject(BinaryReader st, UInt32 block_len, string objPath)
1382 {
1383 UInt32 end = (UInt32)st.BaseStream.Position+ block_len - free_space - _CHM_PMGL_LEN;
1384
1385 // now, scan progressively
1386 chmUnitInfo FoundObject=new chmUnitInfo();
1387
1388 while (st.BaseStream.Position < end)
1389 {
1390 _chm_parse_PMGL_entry(st,ref FoundObject);
1391 if (FoundObject.path.ToLower().CompareTo(objPath.ToLower())==0)
1392 return FoundObject;
1393 }
1394 FoundObject=null;
1395
1396 return null;
1397 }
1398 }
1399
1400 public class chmPmgiHeader : BaseStructure
1401 {
1402 public const int _CHM_PMGI_LEN=0x8;
1403
1404 public char[] signature=null; // 0 (PMGL)
1405 public UInt32 free_space=0; // 4
1406
1407 public int Read_pmgi_header(BinaryReader st)
1408 {
1409 signature=st.ReadChars(4);
1410
1411 if ((signature[0]!='P') || (signature[1]!='M') || (signature[2]!='G') || (signature[3]!='I'))
1412 return 0;
1413
1414 free_space=st.ReadUInt32();
1415 return 1;
1416 }
1417
1418 public Int32 _chm_find_in_PMGI(BinaryReader st, UInt32 block_len, string objPath)
1419 {
1420 int page=-1;
1421 UInt64 strLen;
1422 string buffer="";
1423 uint end = (uint)st.BaseStream.Position + block_len - free_space - _CHM_PMGI_LEN;
1424
1425 // now, scan progressively
1426 while (st.BaseStream.Position < end)
1427 {
1428 // grab the name
1429 strLen = _chm_parse_cword(st);
1430 buffer="";
1431 if (_chm_parse_UTF8(st, strLen, ref buffer)==0)
1432 return -1;
1433
1434 // check if it is the right name
1435 if (buffer.ToLower().CompareTo(objPath.ToLower())>0)
1436 return page;
1437
1438 // load next value for path
1439 page = (int)_chm_parse_cword(st);
1440 }
1441 return page;
1442 }
1443 }
1444
1445 public class chmLzxcResetTable:BaseStructure
1446 {
1447 public UInt32 version=0;
1448 public UInt32 block_count=0;
1449 public UInt32 unknown=0;
1450 public UInt32 table_offset=0;
1451 public UInt64 uncompressed_len=0;
1452 public UInt64 compressed_len=0;
1453 public UInt64 block_len=0;
1454
1455 public int Read_lzxc_reset_table(BinaryReader st)
1456 {
1457 version=st.ReadUInt32();
1458 block_count=st.ReadUInt32();
1459 unknown=st.ReadUInt32();
1460 table_offset=st.ReadUInt32();
1461 uncompressed_len=st.ReadUInt64();
1462 compressed_len=st.ReadUInt64();
1463 block_len=st.ReadUInt64();
1464
1465 // check structure
1466 if (version != 2)
1467 return 0;
1468 else
1469 return 1;
1470 }
1471 }
1472
1473 // structure of LZXC control data block
1474 public class chmLzxcControlData:BaseStructure
1475 {
1476 public const int _CHM_LZXC_MIN_LEN=0x18;
1477 public const int _CHM_LZXC_V2_LEN=0x1c;
1478
1479 public UInt32 size=0; // 0
1480 public char[] signature=null; // 4 (LZXC)
1481 public UInt32 version=0; // 8
1482 public UInt32 resetInterval=0; // c
1483 public UInt32 windowSize=0; // 10
1484 public UInt32 windowsPerReset=0; // 14
1485 public UInt32 unknown_18=0; // 18
1486
1487 public int Read_lzxc_control_data(BinaryReader st)
1488 {
1489 size=st.ReadUInt32();
1490 signature=st.ReadChars(4);
1491
1492 if (CheckSig("LZXC",signature)==false)
1493 return 0;
1494
1495 version=st.ReadUInt32();
1496 resetInterval=st.ReadUInt32();
1497 windowSize=st.ReadUInt32();
1498 windowsPerReset=st.ReadUInt32();
1499
1500 if (size>=_CHM_LZXC_V2_LEN)
1501 unknown_18=st.ReadUInt32();
1502 else
1503 unknown_18 = 0;
1504
1505 if (version == 2)
1506 {
1507 resetInterval *= 0x8000;
1508 windowSize *= 0x8000;
1509 }
1510 if (windowSize == 0 || resetInterval == 0)
1511 return 0;
1512
1513 // for now, only support resetInterval a multiple of windowSize/2
1514 if (windowSize == 1)
1515 return 0;
1516 if ((resetInterval % (windowSize/2)) != 0)
1517 return 0;
1518
1519 return 1;
1520 }
1521 }
1522 #endregion
1523
1524 #region LZW Decoder
1525
1526 internal class lzx_bits
1527 {
1528 public UInt32 bb=0;
1529 public int bl=0;
1530 }
1531
1532 internal class lzw
1533 {
1534 public lzw()
1535 {
1536 }
1537
1538 /* $Id: lzx.c,v 1.5 2002/10/09 01:16:33 jedwin Exp $ */
1539 /***************************************************************************
1540 * lzx.c - LZX decompression routines *
1541 * ------------------- *
1542 * *
1543 * maintainer: Jed Wing <jedwin@ugcs.caltech.edu> *
1544 * source: modified lzx.c from cabextract v0.5 *
1545 * notes: This file was taken from cabextract v0.5, which was, *
1546 * itself, a modified version of the lzx decompression code *
1547 * from unlzx. *
1548 * *
1549 * platforms: In its current incarnation, this file has been tested on *
1550 * two different Linux platforms (one, redhat-based, with a *
1551 * 2.1.2 glibc and gcc 2.95.x, and the other, Debian, with *
1552 * 2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2). Both were *
1553 * Intel x86 compatible machines. *
1554 ***************************************************************************/
1555
1556 /***************************************************************************
1557 * *
1558 * This program is free software; you can redistribute it and/or modify *
1559 * it under the terms of the GNU General Public License as published by *
1560 * the Free Software Foundation; either version 2 of the License, or *
1561 * (at your option) any later version. Note that an exemption to this *
1562 * license has been granted by Stuart Caie for the purposes of *
1563 * distribution with CHMFile. This does not, to the best of my *
1564 * knowledge, constitute a change in the license of this (the LZX) code *
1565 * in general. *
1566 * *
1567 ***************************************************************************/
1568
1569 /* some constants defined by the LZX specification */
1570 private const int LZX_MIN_MATCH = 2;
1571 private const int LZX_MAX_MATCH = 257;
1572 private const int LZX_NUM_CHARS = 256;
1573 private const int LZX_BLOCKTYPE_INVALID = 0; /* also blocktypes 4-7 invalid */
1574 private const int LZX_BLOCKTYPE_VERBATIM = 1;
1575 private const int LZX_BLOCKTYPE_ALIGNED = 2;
1576 private const int LZX_BLOCKTYPE_UNCOMPRESSED = 3;
1577 private const int LZX_PRETREE_NUM_ELEMENTS = 20;
1578 private const int LZX_ALIGNED_NUM_ELEMENTS = 8; /* aligned offset tree #elements */
1579 private const int LZX_NUM_PRIMARY_LENGTHS = 7; /* this one missing from spec! */
1580 private const int LZX_NUM_SECONDARY_LENGTHS = 249; /* length tree #elements */
1581
1582 /* LZX huffman defines: tweak tablebits as desired */
1583 private const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
1584 private const int LZX_PRETREE_TABLEBITS = 6;
1585 private const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 50*8;
1586 private const int LZX_MAINTREE_TABLEBITS = 12;
1587 private const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS+1;
1588 private const int LZX_LENGTH_TABLEBITS = 12;
1589 private const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
1590 private const int LZX_ALIGNED_TABLEBITS = 7;
1591 private const int LZX_LENTABLE_SAFETY = 64; /* we allow length table decoding overruns */
1592
1593 public const int DECR_OK = 0;
1594 public const int DECR_DATAFORMAT = 1;
1595 public const int DECR_ILLEGALDATA = 2;
1596 public const int DECR_NOMEMORY = 3;
1597
1598 private byte[] window; /* the actual decoding window */
1599 private ulong window_size; /* window size (32Kb through 2Mb) */
1600 private ulong actual_size; /* window size when it was first allocated */
1601 private ulong window_posn; /* current offset within the window */
1602 private ulong R0, R1, R2; /* for the LRU offset system */
1603 private UInt32 main_elements; /* number of main tree elements */
1604 private int header_read; /* have we started decoding at all yet? */
1605 private UInt32 block_type; /* type of this block */
1606 private ulong block_length; /* uncompressed length of this block */
1607 private ulong block_remaining; /* uncompressed bytes still left to decode */
1608 private ulong frames_read; /* the number of CFDATA blocks processed */
1609 private long intel_filesize; /* magic header value used for transform */
1610 private long intel_curpos; /* current offset in transform space */
1611 private int intel_started; /* have we seen any translatable data yet? */
1612
1613
1614 private uint [] PRETREE_table = new uint[(1<<(6)) + (((20))<<1)];
1615 private byte [] PRETREE_len = new byte [((20)) + (64)];
1616
1617 private uint [] MAINTREE_table= new uint[(1<<(12)) + (((256) + 50*8)<<1)];
1618 private byte [] MAINTREE_len = new byte [((256) + 50*8) + (64)];
1619
1620 private uint [] LENGTH_table= new uint[(1<<(12)) + (((249)+1)<<1)];
1621 private byte [] LENGTH_len = new byte [((249)+1) + (64)];
1622
1623 private uint [] ALIGNED_table= new uint[(1<<(7)) + (((8))<<1)];
1624 private byte [] ALIGNED_len = new byte [((8)) + (64)];
1625 private System.IO.BinaryReader BitSource=null;
1626 private System.IO.Stream OutputStream=null;
1627
1628 /* LZX decruncher */
1629
1630 /* Microsoft's LZX document and their implementation of the
1631 * com.ms.util.cab Java package do not concur.
1632 *
1633 * In the LZX document, there is a table showing the correlation between
1634 * window size and the number of position slots. It states that the 1MB
1635 * window = 40 slots and the 2MB window = 42 slots. In the implementation,
1636 * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
1637 * first slot whose position base is equal to or more than the required
1638 * window size'. This would explain why other tables in the document refer
1639 * to 50 slots rather than 42.
1640 *
1641 * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
1642 * is not defined in the specification.
1643 *
1644 * The LZX document does not state the uncompressed block has an
1645 * uncompressed length field. Where does this length field come from, so
1646 * we can know how large the block is? The implementation has it as the 24
1647 * bits following after the 3 blocktype bits, before the alignment
1648 * padding.
1649 *
1650 * The LZX document states that aligned offset blocks have their aligned
1651 * offset huffman tree AFTER the main and length trees. The implementation
1652 * suggests that the aligned offset tree is BEFORE the main and length
1653 * trees.
1654 *
1655 * The LZX document decoding algorithm states that, in an aligned offset
1656 * block, if an extra_bits value is 1, 2 or 3, then that number of bits
1657 * should be read and the result added to the match offset. This is
1658 * correct for 1 and 2, but not 3, where just a huffman symbol (using the
1659 * aligned tree) should be read.
1660 *
1661 * Regarding the E8 preprocessing, the LZX document states 'No translation
1662 * may be performed on the last 6 bytes of the input block'. This is
1663 * correct. However, the pseudocode provided checks for the *E8 leader*
1664 * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
1665 * from the end, this would cause the next four bytes to be modified, at
1666 * least one of which would be in the last 6 bytes, which is not allowed
1667 * according to the spec.
1668 *
1669 * The specification states that the huffman trees must always contain at
1670 * least one element. However, many CAB files contain blocks where the
1671 * length tree is completely empty (because there are no matches), and
1672 * this is expected to succeed.
1673 */
1674
1675 /* LZX uses what it calls 'position slots' to represent match offsets.
1676 * What this means is that a small 'position slot' number and a small
1677 * offset from that slot are encoded instead of one large offset for
1678 * every match.
1679 * - position_base is an index to the position slot bases
1680 * - extra_bits states how many bits of offset-from-base data is needed.
1681 */
1682 private byte [] extra_bits = {
1683 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
1684 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
1685 15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
1686 17, 17, 17
1687 };
1688
1689 private ulong [] position_base = {
1690 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
1691 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
1692 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
1693 1835008, 1966080, 2097152
1694 };
1695
1696 private UInt32 ReadUInt16()
1697 {
1698 UInt32 rc=0;
1699 UInt32 Byte1=0;
1700 UInt32 Byte2=0;
1701 try
1702 {
1703 Byte1=BitSource.ReadByte();
1704 Byte2=BitSource.ReadByte();
1705 }
1706 catch(Exception)
1707 {
1708 }
1709 rc=(Byte2<<8)+Byte1;
1710 return rc;
1711 }
1712
1713 public bool LZXinit(int WindowSize)
1714 {
1715 ulong wndsize = (ulong)(1 << WindowSize);
1716 int i, posn_slots;
1717
1718 /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
1719 /* if a previously allocated window is big enough, keep it */
1720 if (WindowSize< 15 || WindowSize> 21) return false;
1721
1722 /* allocate state and associated window */
1723 window = new byte[wndsize];
1724 if (window==null)
1725 {
1726 return false;
1727 }
1728
1729 actual_size = wndsize;
1730 window_size = wndsize;
1731
1732 /* calculate required position slots */
1733 if (WindowSize == 20) posn_slots = 42;
1734 else if (WindowSize== 21) posn_slots = 50;
1735 else posn_slots = WindowSize << 1;
1736
1737 /** alternatively **/
1738 /* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */
1739
1740 /* initialize other state */
1741 R0 = R1 = R2 = 1;
1742 main_elements = (uint)(LZX_NUM_CHARS + (posn_slots << 3));
1743 header_read = 0;
1744 frames_read = 0;
1745 block_remaining = 0;
1746 block_type = LZX_BLOCKTYPE_INVALID;
1747 intel_curpos = 0;
1748 intel_started = 0;
1749 window_posn = 0;
1750
1751 /* initialise tables to 0 (because deltas will be applied to them) */
1752 for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) MAINTREE_len[i] = 0;
1753 for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LENGTH_len[i] = 0;
1754
1755 return true;
1756 }
1757
1758 public void LZXteardown()
1759 {
1760 window=null;
1761 }
1762
1763 public int LZXreset()
1764 {
1765 R0 = R1 = R2 = 1;
1766 header_read = 0;
1767 frames_read = 0;
1768 block_remaining = 0;
1769 block_type = LZX_BLOCKTYPE_INVALID;
1770 intel_curpos = 0;
1771 intel_started = 0;
1772 window_posn = 0;
1773
1774 for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) MAINTREE_len[i] = 0;
1775 for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) LENGTH_len[i] = 0;
1776
1777 return DECR_OK;
1778 }
1779
1780
1781 /* Bitstream reading macros:
1782 *
1783 * INIT_BITSTREAM should be used first to set up the system
1784 * READ_BITS(var,n) takes N bits from the buffer and puts them in var
1785 *
1786 * ENSURE_BITS(n) ensures there are at least N bits in the bit buffer
1787 * PEEK_BITS(n) extracts (without removing) N bits from the bit buffer
1788 * REMOVE_BITS(n) removes N bits from the bit buffer
1789 *
1790 * These bit access routines work by using the area beyond the MSB and the
1791 * LSB as a free source of zeroes. This avoids having to mask any bits.
1792 * So we have to know the bit width of the bitbuffer variable. This is
1793 * sizeof(ulong) * 8, also defined as ULONG_BITS
1794 */
1795
1796 /* number of bits in ulong. Note: This must be at multiple of 16, and at
1797 * least 32 for the bitbuffer code to work (ie, it must be able to ensure
1798 * up to 17 bits - that's adding 16 bits when there's one bit left, or
1799 * adding 32 bits when there are no bits left. The code should work fine
1800 * for machines where ulong >= 32 bits.
1801 */
1802 private int ULONG_BITS()
1803 {
1804 int rc=(System.Runtime.InteropServices.Marshal.SizeOf(typeof(System.UInt32))<<3);
1805 return rc;
1806 }
1807
1808 /* make_decode_table(nsyms, nbits, length[], table[])
1809 *
1810 * This function was coded by David Tritscher. It builds a fast huffman
1811 * decoding table out of just a canonical huffman code lengths table.
1812 *
1813 * nsyms = total number of symbols in this huffman tree.
1814 * nbits = any symbols with a code length of nbits or less can be decoded
1815 * in one lookup of the table.
1816 * length = A table to get code lengths from [0 to syms-1]
1817 * table = The table to fill up with decoded symbols and pointers.
1818 *
1819 * Returns 0 for OK or 1 for error
1820 */
1821
1822 private int make_decode_table(ulong nsyms, byte nbits, ref byte [] length, ref UInt32[] table)
1823 {
1824 ulong sym;
1825 ulong leaf;
1826 byte bit_num = 1;
1827 ulong fill;
1828 ulong pos = 0; /* the current position in the decode table */
1829 ulong table_mask = (ulong)(1 << nbits);
1830 ulong bit_mask = table_mask >> 1; /* don't do 0 length codes */
1831 ulong next_symbol = bit_mask; /* base of allocation for long codes */
1832
1833 /* fill entries for codes short enough for a direct mapping */
1834 while (bit_num <= nbits)
1835 {
1836 for (sym = 0; sym < nsyms; sym++)
1837 {
1838 if (length[sym] == bit_num)
1839 {
1840 leaf = pos;
1841
1842 if((pos += bit_mask) > table_mask) return 1; /* table overrun */
1843
1844 /* fill all possible lookups of this symbol with the symbol itself */
1845 fill = bit_mask;
1846 while (fill-- > 0) table[leaf++] = (uint)sym;
1847 }
1848 }
1849 bit_mask >>= 1;
1850 bit_num++;
1851 }
1852
1853 /* if there are any codes longer than nbits */
1854 if (pos != table_mask)
1855 {
1856 /* clear the remainder of the table */
1857 for (sym = pos; sym < table_mask; sym++) table[sym] = 0;
1858
1859 /* give ourselves room for codes to grow by up to 16 more bits */
1860 pos <<= 16;
1861 table_mask <<= 16;
1862 bit_mask = 1 << 15;
1863
1864 while (bit_num <= 16)
1865 {
1866 for (sym = 0; sym < nsyms; sym++)
1867 {
1868 if (length[sym] == bit_num)
1869 {
1870 leaf = pos >> 16;
1871 for (fill = 0; fill < (ulong)(bit_num - nbits); fill++)
1872 {
1873 /* if this path hasn't been taken yet, 'allocate' two entries */
1874 if (table[leaf] == 0)
1875 {
1876 table[(next_symbol << 1)] = 0;
1877 table[(next_symbol << 1) + 1] = 0;
1878 table[leaf] = (uint)next_symbol++;
1879 }
1880 /* follow the path and select either left or right for next bit */
1881 leaf = table[leaf] << 1;
1882 if (((pos >> (byte)(15-fill)) & 1)==1)
1883 leaf++;
1884 }
1885 table[leaf] = (uint)sym;
1886
1887 if ((pos += bit_mask) > table_mask)
1888 return 1; /* table overflow */
1889 }
1890 }
1891 bit_mask >>= 1;
1892 bit_num++;
1893 }
1894 }
1895
1896 /* full table? */
1897 if (pos == table_mask)
1898 return 0;
1899
1900 /* either erroneous table, or all elements are 0 - let's find out. */
1901 for (sym = 0; sym < nsyms; sym++) if (length[(uint)sym]!=0)
1902 return 1;
1903
1904 return 0;
1905 }
1906
1907 private int lzx_read_lens(byte []lens, ulong first, ulong last, ref lzx_bits lb)
1908 {
1909 ulong i,j, x,y;
1910 int z;
1911
1912 UInt32 bitbuf = lb.bb;
1913 int bitsleft = lb.bl;
1914
1915 UInt32 [] hufftbl=null;
1916
1917 for (x = 0; x < 20; x++)
1918 {
1919 do
1920 {
1921 while (bitsleft < (4))
1922 {
1923 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
1924 bitsleft += 16;
1925 }
1926 y = (bitbuf >> (ULONG_BITS()- (4)));
1927 bitbuf <<= 4;
1928 bitsleft -= 4;
1929 }
1930 while (false);
1931 PRETREE_len[x] = (byte)y;
1932 }
1933 if (make_decode_table( 20, 6, ref PRETREE_len, ref PRETREE_table)!=0)
1934 return 2;
1935
1936 for (x = first; x < last; )
1937 {
1938 do
1939 {
1940 while (bitsleft < 16)
1941 {
1942 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
1943 bitsleft += 16;
1944 }
1945 hufftbl = PRETREE_table;
1946 if ((i = hufftbl[((ulong)bitbuf >> (ULONG_BITS()- 6))]) >= 20)
1947 {
1948 j = (ulong)(1 << (byte)(ULONG_BITS()- ((6))));
1949 do
1950 {
1951 j >>= 1;
1952 i <<= 1;
1953 if ((bitbuf & j)!=0)
1954 i|=1;
1955 else
1956 i|=0;
1957
1958 if (j==0)
1959 {
1960 return (2);
1961 }
1962 }
1963 while ((i = hufftbl[i]) >= 20);
1964 }
1965 z = (int)i;
1966 j = PRETREE_len[z];
1967 bitbuf <<= (byte)j;
1968 bitsleft -= (int)j;
1969 }
1970 while (false);
1971
1972 if (z == 17)
1973 {
1974 do
1975 {
1976 while (bitsleft < (4))
1977 {
1978 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
1979 bitsleft += 16;
1980 }
1981 y = (bitbuf >> (ULONG_BITS()- (4)));
1982 bitbuf <<= 4;
1983 bitsleft -= 4;
1984 }
1985 while(false);
1986 y += 4;
1987
1988 while ((y--)!=0)
1989 lens[x++] = 0;
1990 }
1991 else if (z == 18)
1992 {
1993 do
1994 {
1995 while (bitsleft < (5))
1996 {
1997 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
1998 bitsleft += 16;
1999 }
2000 (y) = (bitbuf >> (ULONG_BITS()- (5)));
2001 bitbuf <<= 5;
2002 bitsleft -= 5;
2003 }
2004 while (false);
2005
2006 y += 20;
2007
2008 while ((y--)!=0)
2009 lens[x++] = 0;
2010 }
2011 else if (z == 19)
2012 {
2013 do
2014 {
2015 while (bitsleft < (1))
2016 {
2017 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2018 bitsleft += 16;
2019 }
2020 y = (bitbuf >> (ULONG_BITS()- (1)));
2021 bitbuf <<= 1;
2022 bitsleft -= 1;
2023 }
2024 while(false);
2025 y += 4;
2026 do
2027 {
2028 while (bitsleft < (16))
2029 {
2030 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2031 bitsleft += 16;
2032 }
2033 hufftbl = (PRETREE_table);
2034 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 6))]) >= 20)
2035 {
2036 j = (ulong)1 << (byte)(ULONG_BITS()- 6);
2037 do
2038 {
2039 j >>= 1;
2040 i <<= 1;
2041 if ((bitbuf & j)==0)
2042 i|=0;
2043 else
2044 i|=1;
2045 if (j==0)
2046 {
2047 return (2);
2048 }
2049 }
2050 while ((i = hufftbl[i]) >= 20);
2051 }
2052 z = (int)i;
2053 j = PRETREE_len[z];
2054
2055 bitbuf <<= (byte)j;
2056 bitsleft -= (int)j;
2057 }
2058 while(false);
2059 z = lens[x] - z;
2060 if (z < 0)
2061 z += 17;
2062
2063 while ((y--)!=0)
2064 lens[x++] = (byte)z;
2065 }
2066 else
2067 {
2068 z = lens[x] - z;
2069 if (z < 0)
2070 z += 17;
2071 lens[x++] = (byte)z;
2072 }
2073 }
2074 lb.bb = bitbuf;
2075 lb.bl = bitsleft;
2076 return 0;
2077 }
2078
2079 public int LZXdecompress(System.IO.BinaryReader inpos, System.IO.Stream outpos, ref ulong inlen, ref ulong outlen)
2080 {
2081 BitSource=inpos;
2082 OutputStream=outpos;
2083
2084 long endinp = BitSource.BaseStream.Position+(long)inlen;
2085 ulong runsrc, rundest;
2086 UInt32 [] hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */
2087
2088 UInt32 bitbuf;
2089 int bitsleft;
2090 ulong match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */
2091 lzx_bits lb; /* used in READ_LENGTHS macro */
2092 lb=new lzx_bits();
2093
2094 int togo = (int)outlen, this_run, main_element, aligned_bits;
2095 int match_length, length_footer, extra, verbatim_bits;
2096
2097 bitsleft = 0;
2098 bitbuf = 0;
2099
2100 /* read header if necessary */
2101 if (header_read==0)
2102 {
2103 i = j = 0;
2104 do
2105 {
2106 while (bitsleft < (1))
2107 {
2108 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2109 bitsleft += 16;
2110 }
2111 k = (bitbuf >> (ULONG_BITS()- (1)));
2112 bitbuf <<= 1;
2113 bitsleft -= 1;
2114 }
2115 while(false);
2116
2117 if (k!=0)
2118 {
2119 do
2120 {
2121 while (bitsleft < (16))
2122 {
2123 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() -16 - bitsleft);
2124 bitsleft += 16;
2125 }
2126 i = (bitbuf >> (ULONG_BITS()- (16)));
2127 bitbuf <<= 16;
2128 bitsleft -= 1;
2129 }
2130 while(false);
2131
2132 do
2133 {
2134 while (bitsleft < (16))
2135 {
2136 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2137 bitsleft += 16;
2138 }
2139 j = (bitbuf >> (ULONG_BITS()- (16)));
2140 bitbuf <<= 16;
2141 bitsleft -= 16;
2142 }
2143 while(false);
2144 }
2145 intel_filesize = (long)((i << 16) | j);
2146 header_read = 1;
2147 }
2148
2149 /* main decoding loop */
2150 while (togo > 0)
2151 {
2152 if (block_remaining == 0)
2153 {
2154 if (block_type == (3))
2155 {
2156 if ((block_length & 1)!=0)
2157 BitSource.ReadByte();
2158 bitsleft = 0;
2159 bitbuf = 0;
2160 }
2161
2162 do
2163 {
2164 while (bitsleft < (3))
2165 {
2166 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2167 bitsleft += 16;
2168 }
2169 (block_type) = (uint)(bitbuf >> (ULONG_BITS()- (3)));
2170 bitbuf <<= 3;
2171 bitsleft -= 3;
2172 }
2173 while (false);
2174
2175 do
2176 {
2177 while (bitsleft < (16))
2178 {
2179 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2180 bitsleft += 16;
2181 }
2182 (i) = (bitbuf >> (ULONG_BITS()- (16)));
2183 bitbuf <<= 16;
2184 bitsleft -= 16;
2185 }
2186 while (false);
2187
2188 do
2189 {
2190 while (bitsleft < (8))
2191 {
2192 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2193 bitsleft += 16;
2194 }
2195 (j) = (bitbuf >> (ULONG_BITS()- (8)));
2196 bitbuf <<= 8;
2197 bitsleft -= 8;
2198 }
2199 while (false);
2200 block_remaining = block_length = (i << 8) | j;
2201
2202 switch (block_type)
2203 {
2204 case (LZX_BLOCKTYPE_ALIGNED):
2205 for (i = 0; i < 8; i++)
2206 {
2207 do
2208 {
2209 while (bitsleft < (3))
2210 {
2211 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2212 bitsleft += 16;
2213 }
2214 (j) = (bitbuf >> (ULONG_BITS()- (3)));
2215 bitbuf <<= 3;
2216 bitsleft -= 3;
2217 }
2218 while (false);
2219 (ALIGNED_len)[i] = (byte)j;
2220 }
2221 if (make_decode_table( 8, 7, ref ALIGNED_len, ref ALIGNED_table)!=0)
2222 {
2223 return (2);
2224 }
2225
2226 do
2227 {
2228 lb.bb = bitbuf;
2229 lb.bl = bitsleft;
2230 if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0)
2231 {
2232 return (2);
2233 }
2234 bitbuf = lb.bb;
2235 bitsleft = lb.bl;
2236 }
2237 while (false);
2238 do
2239 {
2240 lb.bb = bitbuf;
2241 lb.bl = bitsleft;
2242 if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0)
2243 {
2244 return (2);
2245 }
2246 bitbuf = lb.bb;
2247 bitsleft = lb.bl;
2248 }
2249 while (false);
2250
2251 if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0)
2252 {
2253 return (2);
2254 }
2255 if (MAINTREE_len[0xE8] != 0) intel_started = 1;
2256
2257 do
2258 {
2259 lb.bb = bitbuf;
2260 lb.bl = bitsleft;
2261 if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0)
2262 {
2263 return (2);
2264 }
2265 bitbuf = lb.bb;
2266 bitsleft = lb.bl;
2267 }
2268 while (false);
2269 if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0)
2270 {
2271 return (2);
2272 }
2273 break;
2274
2275 case (LZX_BLOCKTYPE_VERBATIM):
2276 do
2277 {
2278 lb.bb = bitbuf;
2279 lb.bl = bitsleft;
2280 if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0)
2281 {
2282 return (2);
2283 }
2284 bitbuf = lb.bb;
2285 bitsleft = lb.bl;
2286 }
2287 while (false);
2288 do
2289 {
2290 lb.bb = bitbuf;
2291 lb.bl = bitsleft;
2292 if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0)
2293 {
2294 return (2);
2295 }
2296 bitbuf = lb.bb;
2297 bitsleft = lb.bl;
2298 }
2299 while (false);
2300
2301 if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0)
2302 {
2303 return (2);
2304 }
2305 if (MAINTREE_len[0xE8] != 0) intel_started = 1;
2306
2307 do
2308 {
2309 lb.bb = bitbuf;
2310 lb.bl = bitsleft;
2311 if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0)
2312 {
2313 return (2);
2314 }
2315 bitbuf = lb.bb;
2316 bitsleft = lb.bl;
2317 }
2318 while (false);
2319 if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0)
2320 {
2321 return (2);
2322 }
2323 break;
2324
2325 case (LZX_BLOCKTYPE_UNCOMPRESSED):
2326 intel_started = 1;
2327 while (bitsleft < (16))
2328 {
2329 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - bitsleft);
2330 bitsleft += 16;
2331 }
2332 if (bitsleft > 16)
2333 {
2334 BitSource.BaseStream.Seek(-2,System.IO.SeekOrigin.Current);
2335 }
2336 R0 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));
2337 R1 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));
2338 R2 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24));
2339 break;
2340
2341 default:
2342 return (DECR_ILLEGALDATA);
2343 }
2344 }
2345
2346 /* buffer exhaustion check */
2347 if (BitSource.BaseStream.Position > (long) endinp)
2348 {
2349 /* it's possible to have a file where the next run is less than
2350 * 16 bits in size. In this case, the READ_HUFFSYM() macro used
2351 * in building the tables will exhaust the buffer, so we should
2352 * allow for this, but not allow those accidentally read bits to
2353 * be used (so we check that there are at least 16 bits
2354 * remaining - in this boundary case they aren't really part of
2355 * the compressed data)
2356 */
2357 if (BitSource.BaseStream.Position> (long)(endinp+2) || bitsleft < 16)
2358 return DECR_ILLEGALDATA;
2359 }
2360
2361 while ((this_run = (int)block_remaining) > 0 && togo > 0)
2362 {
2363 if (this_run > togo)
2364 this_run = togo;
2365
2366 togo -= this_run;
2367 block_remaining -= (ulong)this_run;
2368
2369 /* apply 2^x-1 mask */
2370 window_posn &= window_size - 1;
2371
2372 /* runs can't straddle the window wraparound */
2373 if ((window_posn + (ulong)this_run) > window_size)
2374 return DECR_DATAFORMAT;
2375
2376 switch (block_type)
2377 {
2378 case LZX_BLOCKTYPE_VERBATIM:
2379 while (this_run > 0)
2380 {
2381 do
2382 {
2383 while (bitsleft < (16))
2384 {
2385 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2386 bitsleft += 16;
2387 }
2388 hufftbl = MAINTREE_table;
2389 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= 256 + 50*8)
2390 {
2391 j = (ulong)(1 << (ULONG_BITS()- 12));
2392 do
2393 {
2394 j >>= 1;
2395 i <<= 1;
2396 if ((bitbuf & j)!=0)
2397 i|=1;
2398 else
2399 i|=0;
2400 if (j==0)
2401 {
2402 return (2);
2403 }
2404 }
2405 while ((i = hufftbl[i]) >= (((256) + 50*8)));
2406 }
2407 j = MAINTREE_len[main_element = (int)i];
2408 bitbuf <<= (byte)j;
2409 bitsleft -= (byte)j;
2410 }
2411 while (false);
2412
2413 if (main_element < (256))
2414 {
2415 window[window_posn++] = (byte)main_element;
2416 this_run--;
2417 }
2418 else
2419 {
2420 main_element -= (256);
2421
2422 match_length = main_element & (7);
2423 if (match_length == (7))
2424 {
2425 do
2426 {
2427 while (bitsleft < (16))
2428 {
2429 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2430 bitsleft += 16;
2431 }
2432 hufftbl = (LENGTH_table);
2433 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((249)+1)))
2434 {
2435 j = (ulong)(1 << (ULONG_BITS()- ((12))));
2436 do
2437 {
2438 j >>= 1;
2439 i <<= 1;
2440 if ((bitbuf & j)!=0)
2441 i|=1;
2442 else
2443 i|=0;
2444 if (j==0)
2445 {
2446 return (2);
2447 }
2448 }
2449 while ((i = hufftbl[i]) >= (((249)+1)));
2450 }
2451 j = LENGTH_len[(length_footer) = (int)i];
2452 bitbuf <<= (byte)j;
2453 bitsleft -= (byte)j;
2454 }
2455 while (false);
2456
2457 match_length += length_footer;
2458 }
2459 match_length += (2);
2460
2461 match_offset = (ulong)(main_element >> 3);
2462
2463 if (match_offset > 2)
2464 {
2465 if (match_offset != 3)
2466 {
2467 extra = extra_bits[match_offset];
2468 do
2469 {
2470 while (bitsleft < (extra))
2471 {
2472 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2473 bitsleft += 16;
2474 }
2475 verbatim_bits = (int)(bitbuf >> (ULONG_BITS()- (extra)));
2476 bitbuf <<= extra;
2477 bitsleft -= extra;
2478 }
2479 while (false);
2480 match_offset = position_base[match_offset] - 2 + (ulong)verbatim_bits;
2481 }
2482 else
2483 {
2484 match_offset = 1;
2485 }
2486
2487 R2 = R1; R1 = R0; R0 = match_offset;
2488 }
2489 else if (match_offset == 0)
2490 {
2491 match_offset = R0;
2492 }
2493 else if (match_offset == 1)
2494 {
2495 match_offset = R1;
2496 R1 = R0; R0 = match_offset;
2497 }
2498 else
2499 {
2500 match_offset = R2;
2501 R2 = R0; R0 = match_offset;
2502 }
2503
2504 rundest = window_posn;
2505 // rundest= window+window_posn
2506 runsrc = rundest - match_offset;
2507 window_posn += (ulong)match_length;
2508 this_run -= match_length;
2509
2510 // runsrc < window
2511 while ((runsrc<0) && (match_length-- > 0))
2512 {
2513 window[rundest++]=window[runsrc+window_size];
2514 // *rundest++ = *(runsrc + window_size);
2515 runsrc++;
2516 }
2517
2518 while (match_length-- > 0)
2519 {
2520 window[rundest++]=window[runsrc++];
2521 // *rundest++ = *runsrc++;
2522 }
2523 }
2524 }
2525 break;
2526
2527 case LZX_BLOCKTYPE_ALIGNED:
2528 while (this_run > 0)
2529 {
2530 do
2531 {
2532 while (bitsleft < (16))
2533 {
2534 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2535 bitsleft += 16;
2536 }
2537 hufftbl = MAINTREE_table;
2538 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((256) + 50*8)))
2539 {
2540 j = (ulong)1 << (ULONG_BITS()- ((12)));
2541 do
2542 {
2543 j >>= 1;
2544 i <<= 1;
2545 if ((bitbuf & j)!=0)
2546 i|=1;
2547 else
2548 i|=0;
2549 if (j==0)
2550 {
2551 return (2);
2552 }
2553 }
2554 while ((i = hufftbl[i]) >= (((256) + 50*8)));
2555 }
2556 j = MAINTREE_len[(main_element) = (int)i];
2557 bitbuf <<= (int)j;
2558 bitsleft -= (int)j;
2559 }
2560 while (false);
2561
2562 if (main_element < (256))
2563 {
2564 window[window_posn++] = (byte)main_element;
2565 this_run--;
2566 }
2567 else
2568 {
2569 main_element -= (256);
2570 match_length = main_element & (7);
2571 if (match_length == (7))
2572 {
2573 do
2574 {
2575 while (bitsleft < (16))
2576 {
2577 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2578 bitsleft += 16;
2579 }
2580 hufftbl = LENGTH_table;
2581 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((249)+1)))
2582 {
2583 j = (ulong) 1 << (ULONG_BITS()- ((12)));
2584 do
2585 {
2586 j >>= 1;
2587 i <<= 1;
2588 if ((bitbuf & j)!=0)
2589 i|=1;
2590 else
2591 i|=0;
2592
2593 if (j==0)
2594 {
2595 return (2);
2596 }
2597 }
2598 while ((i = hufftbl[i]) >= (((249)+1)));
2599 }
2600 j = LENGTH_len[length_footer = (int)i];
2601 bitbuf <<= (int)j;
2602 bitsleft -= (int)j;
2603 }
2604 while (false);
2605 match_length += length_footer;
2606 }
2607 match_length += (2);
2608
2609 match_offset = (ulong)(main_element >> 3);
2610
2611 if (match_offset > 2)
2612 {
2613 extra = extra_bits[match_offset];
2614 match_offset = position_base[match_offset] - 2;
2615 if (extra > 3)
2616 {
2617 extra -= 3;
2618 do
2619 {
2620 while (bitsleft < (extra))
2621 {
2622 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2623 bitsleft += 16;
2624 }
2625 verbatim_bits = (int)(bitbuf >> (ULONG_BITS()- (extra)));
2626 bitbuf <<= extra;
2627 bitsleft -= extra;
2628 }
2629 while (false);
2630 match_offset += (ulong)(verbatim_bits << 3);
2631 do
2632 {
2633 while (bitsleft < (16))
2634 {
2635 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2636 bitsleft += 16;
2637 }
2638 hufftbl = (ALIGNED_table);
2639 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 7))]) >= 8)
2640 {
2641 j = (ulong)1 << (ULONG_BITS()- ((7)));
2642 do
2643 {
2644 j >>= 1;
2645 i <<= 1;
2646 if ((bitbuf & j)!=0)
2647 i|=1;
2648 else
2649 i|=0;
2650 if (j==0)
2651 {
2652 return (2);
2653 }
2654 }
2655 while ((i = hufftbl[i]) >= (((8))));
2656 }
2657
2658 j = (ALIGNED_len)[(aligned_bits) = (int)i];
2659 bitbuf <<= (int)j;
2660 bitsleft -= (int)j;
2661 }
2662 while (false);
2663 match_offset += (ulong)aligned_bits;
2664 }
2665 else if (extra == 3)
2666 {
2667 do
2668 {
2669 while (bitsleft < (16))
2670 {
2671 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2672 bitsleft += 16;
2673 }
2674 hufftbl = (ALIGNED_table);
2675 if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 7))]) >= 8)
2676 {
2677 j = (ulong)1 << (ULONG_BITS()- ((7)));
2678 do
2679 {
2680 j >>= 1;
2681 i <<= 1;
2682 if ((bitbuf & j)!=0)
2683 i|=1;
2684 else
2685 i|=0;
2686 if (j!=0)
2687 {
2688 return (2);
2689 }
2690 }
2691 while ((i = hufftbl[i]) >= 8);
2692 }
2693 j = (ALIGNED_len)[(aligned_bits) = (int)i];
2694 bitbuf <<= (int)j;
2695 bitsleft -= (int)j;
2696 }
2697 while (false);
2698 match_offset += (ulong)aligned_bits;
2699 }
2700 else if (extra > 0)
2701 {
2702 do
2703 {
2704 while (bitsleft < (extra))
2705 {
2706 bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft);
2707 bitsleft += 16;
2708 }
2709 (verbatim_bits) = (int)(bitbuf >> (int)(ULONG_BITS()- (extra)));
2710 bitbuf <<= extra;
2711 bitsleft -= extra;
2712 }
2713 while (false);
2714 match_offset += (ulong)verbatim_bits;
2715 }
2716 else
2717 {
2718 match_offset = 1;
2719 }
2720
2721 R2 = R1; R1 = R0; R0 = match_offset;
2722 }
2723 else if (match_offset == 0)
2724 {
2725 match_offset = R0;
2726 }
2727 else if (match_offset == 1)
2728 {
2729 match_offset = R1;
2730 R1 = R0; R0 = match_offset;
2731 }
2732 else
2733 {
2734 match_offset = R2;
2735 R2 = R0; R0 = match_offset;
2736 }
2737
2738 rundest = window_posn;
2739 runsrc = rundest - match_offset;
2740 window_posn += (ulong)match_length;
2741 this_run -= match_length;
2742
2743 while ((runsrc<0) && (match_length-- > 0))
2744 {
2745 // *rundest++ = *(runsrc + window_size); runsrc++;
2746 window[rundest++]=window[runsrc + window_size];
2747 runsrc++;
2748 }
2749
2750 while (match_length-- > 0)
2751 {
2752 // *rundest++ = *runsrc++;
2753 window[rundest++]=window[runsrc++];
2754 }
2755 }
2756 }
2757 break;
2758
2759 case LZX_BLOCKTYPE_UNCOMPRESSED:
2760 if ((BitSource.BaseStream.Position + (long)this_run) > (long)endinp)
2761 return (2);
2762
2763 // memcpy(window + window_posn, inposCount, this_run);
2764 for(i=0; i<(ulong)this_run;i++)
2765 {
2766 window[window_posn+i]=BitSource.ReadByte();
2767 }
2768 window_posn += (ulong)this_run;
2769 break;
2770
2771 default:
2772 return DECR_ILLEGALDATA; /* might as well */
2773 }
2774
2775 }
2776 }
2777
2778 if (togo != 0) return DECR_ILLEGALDATA;
2779
2780 // memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t) outlen);
2781 ulong start=0;
2782 if (window_posn==0)
2783 start=(ulong)window_size;
2784 else
2785 start=(ulong)window_posn;
2786
2787 start-=(ulong)outlen;
2788
2789 long Pos=OutputStream.Position;
2790 for(i=0;i<(ulong)outlen;i++)
2791 {
2792 OutputStream.WriteByte(window[start+i]);
2793 }
2794 OutputStream.Seek(Pos,System.IO.SeekOrigin.Begin);
2795
2796 /* intel E8 decoding */
2797 if ((frames_read++ < 32768) && intel_filesize != 0)
2798 {
2799 if (outlen <= 6 || (intel_started==0))
2800 {
2801 intel_curpos += (long)outlen;
2802 }
2803 else
2804 {
2805 // UBYTE *data = outpos;
2806 long dataend = OutputStream.Position + (int)outlen - 10;
2807 long curpos = intel_curpos;
2808 long filesize = intel_filesize;
2809 long abs_off, rel_off;
2810
2811 intel_curpos = (long)curpos + (long)outlen;
2812
2813 while (OutputStream.Position < dataend)
2814 {
2815 if (OutputStream.ReadByte() != 0xE8)
2816 {
2817 curpos++;
2818 continue;
2819 }
2820
2821 abs_off = (long)(OutputStream.ReadByte() | (OutputStream.ReadByte() <<8) | (OutputStream.ReadByte() <<16) | (OutputStream.ReadByte() <<24));
2822 if (abs_off < filesize)
2823 {
2824 if (abs_off >= 0)
2825 rel_off = (long)(abs_off - curpos);
2826 else
2827 rel_off = (long)abs_off + filesize;
2828 OutputStream.WriteByte((byte)(rel_off & 0x000000ff));
2829 OutputStream.WriteByte((byte)((rel_off & 0x0000ff00)>>8));
2830 OutputStream.WriteByte((byte)((rel_off & 0x00ff0000)>>16));
2831 OutputStream.WriteByte((byte)((rel_off & 0xff000000)>>24));
2832 }
2833 curpos += 5;
2834 }
2835 }
2836 }
2837
2838 return DECR_OK;
2839 }
2840 }
2841 #endregion
2842 }