using System; using System.Diagnostics; using System.Text; using System.Data; using System.Text.RegularExpressions; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Runtime.InteropServices; namespace CHMStream { /// /// Summary description for CHMFile. /// /// public class CHMStream : IDisposable { public MemoryStream OpenStream(chmUnitInfo Info) { if (Info==null) return null; MemoryStream st=new MemoryStream(); this.ExtractFile(Info,st); return st; } public MemoryStream OpenStream(string FileName) { chmUnitInfo info=this.GetFileInfo(FileName); if (info==null) return null; return OpenStream(info); } private string m_CHMFileName; public string CHMFileName { get { return m_CHMFileName; } } public CHMStream() { } public CHMStream(string CHMFileName) { OpenCHM(CHMFileName); } public void OpenCHM(string CHMFileName) { m_CHMFileName=CHMFileName; FileInfo fi=new FileInfo(m_CHMFileName); Dir=fi.DirectoryName; m_CHMName=fi.Name; fi=null; chm_open(m_CHMFileName); } private bool m_bCHMLoaded=false; public bool CHMLoaded { get { return m_bCHMLoaded; } } private string m_CHMName=""; public string CHMName { get { return m_CHMName; } } private string Dir=""; private string m_FileFind=""; private string m_FileFindLastPart=""; private chmUnitInfo m_FileInfo=null; private int m_FileCount=0; public void FindFile(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { string LocalFile=Info.path; LocalFile=LocalFile.Replace("/",@"\"); if (!LocalFile.StartsWith(@"\")) LocalFile=@"\"+LocalFile; LocalFile=LocalFile.ToLower(); if (m_FileFind.Length<=LocalFile.Length) { if (LocalFile.IndexOf(m_FileFind)==LocalFile.Length-m_FileFind.Length) { Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS; m_FileInfo=Info; return; } } Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; } public void FileCount(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { m_FileCount++; Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; } private ArrayList m_FileList=null; private string m_strByExt=""; public void FileList(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { m_FileList.Add(Info.path); Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; } public void FileListByExtension(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { FileInfo fi=new FileInfo(Info.path); if (fi.Extension.ToLower()==m_strByExt.ToLower()) m_FileList.Add(Info.path); Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; } public void FindFileIndex(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { if (m_FileCount==m_FileFindIndex) { m_FileInfo=Info; Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS; } else { m_FileCount++; Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; } } public int GetFileCount() { if (!m_bCHMLoaded) return 0; m_FileCount=0; this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileCount); this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL); this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileCount); return m_FileCount; } public ArrayList GetFileList() { if (!m_bCHMLoaded) return null; m_FileList=null; m_FileList=new ArrayList(1000); this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileList); this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL); this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileList); return m_FileList; } public ArrayList GetFileListByExtenstion(string Ext) { if (!m_bCHMLoaded) return null; m_FileList=null; m_FileList=new ArrayList(1000); m_strByExt=Ext; this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FileListByExtension); this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL); this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FileListByExtension); return m_FileList; } public chmUnitInfo GetFileInfo(string FileName) { if (!m_bCHMLoaded) return null; m_FileFind=FileName.ToLower().Replace("/",@"\"); // Remove all leading '..\' do { if (m_FileFind.StartsWith(@"..\")) m_FileFind=m_FileFind.Substring(3); else break; } while(true); if (!m_FileFind.StartsWith(@"\")) m_FileFind=@"\"+m_FileFind; string []parts=m_FileFind.Split('\\'); m_FileFindLastPart=@"\"+parts[parts.GetUpperBound(0)]; this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFile); m_FileInfo=null; this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL); this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFile); return m_FileInfo; } private int m_FileFindIndex=0; public chmUnitInfo GetFileInfo(int FileIndex) { if (!m_bCHMLoaded) return null; m_FileFindIndex=FileIndex; this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFileIndex); m_FileCount=0; m_FileInfo=null; this.chm_enumerate(CHM_ENUMERATE.CHM_ENUMERATE_ALL); this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFileIndex); return m_FileInfo; } public chmUnitInfo GetFileInfoByExtension(string Ext) { this.CHMFileFoundEvent+=new CHMStream.CHMFileFound(FindFileByExtension); m_FileInfo=null; m_FileFind=Ext.ToLower(); this.chm_enumerate(CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_ALL); this.CHMFileFoundEvent-=new CHMStream.CHMFileFound(FindFileByExtension); return m_FileInfo; } public bool ExtractFile(string FileName, System.IO.Stream st) { if (!m_bCHMLoaded) return false; chmUnitInfo Info=GetFileInfo(FileName); return ExtractFile(Info,st); } public bool ExtractFile(chmUnitInfo Info, System.IO.Stream st) { if (!m_bCHMLoaded) return false; if (Info==null) return false; else { chm_retrieve_object(Info,st,0,Info.length); } return true; } public string ExtractTextFile(string FileName) { if (!m_bCHMLoaded) return "CHM File not loaded"; chmUnitInfo Info=GetFileInfo(FileName); return ExtractTextFile(Info); } public string ExtractTextFile(chmUnitInfo Info) { if (!m_bCHMLoaded) return "CHM File not loaded"; if (Info==null) return ""; if (Info.path.Length>=2) { if (Info.path.Substring(0,2).CompareTo("/#")==0) return ""; if (Info.path.Substring(0,2).CompareTo("/$")==0) return ""; } MemoryStream st=new MemoryStream((int)Info.length); this.chm_retrieve_object(Info,st,0,Info.length); if (st.Length==0) return ""; string Text=""; ASCIIEncoding ascii=new ASCIIEncoding(); Text=ascii.GetString(st.ToArray(),0,50); // UTF Decoding if (Text.IndexOf("UTF-8")!=-1) { UTF8Encoding utf8 = new UTF8Encoding(); Text=utf8.GetString(st.ToArray(),0,(int)st.Length); } else Text=ascii.GetString(st.ToArray(),0,(int)st.Length); return Text; } public void FindFileByExtension(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { if ((Info.path.StartsWith("::")) || (Info.path.StartsWith("#")) ||(Info.path.StartsWith("$"))) { Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; return; } FileInfo Fi=new FileInfo(Info.path); if (Fi.Extension.ToLower()==m_FileFind) { Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS; m_FileInfo=Info; } else Status=CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; } public bool GetCHMParts(string Url, ref string CHMFileName, ref string FileName, ref string Anchor) { Regex ParseURLRegEx= new Regex( @"ms-its:(?'CHMFile'.*)::(?'Topic'.*)", RegexOptions.IgnoreCase| RegexOptions.Singleline | RegexOptions.ExplicitCapture| RegexOptions.IgnorePatternWhitespace| RegexOptions.Compiled); // Parse URL - Get CHM Filename & Page Name // Format 'ms-its:file name.chm::/topic.htm' if (ParseURLRegEx.IsMatch(Url)) { Match m=ParseURLRegEx.Match(Url); CHMFileName=m.Groups["CHMFile"].Value; string Topic=m.Groups["Topic"].Value; int idx=Topic.IndexOf("#"); if (idx>-1) { FileName=Topic.Substring(0,idx); Anchor=Topic.Substring(idx+1); } else FileName=Topic; return true; } return false; } private string m_TempDir=""; string ReplaceFileName(Match m) { string strReplace = m.ToString(); // Process string. if (m.Groups["FileName"]==null) return strReplace; string FileName=m.Groups["FileName"].Value; string FileName2=FileName.Replace("/",@"\"); int idx=FileName2.IndexOf("::"); if (idx!=-1) FileName2=FileName2.Substring(idx+2); string []parts=FileName2.Split('\\'); string NewName=@"file://"+m_TempDir+parts[parts.GetUpperBound(0)]; strReplace=strReplace.Replace(FileName,NewName); return strReplace; } public ArrayList GetFileList(ref string Text, string TempDir) { if (!m_bCHMLoaded) return null; m_TempDir=TempDir; ArrayList FilesList=new ArrayList(); // Parse HTML for CCS, ima, etc 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*"; string regexFileName=@"\s*=\s*[""|'](?'FileName'[^""^']*)[""|']\s*"; Regex ScriptRegex = new Regex(@"]*>.*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); Regex XMLRegex = new Regex(@"<\?xml.*\?>", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); Regex XMLRegex2 = new Regex(@"]*>.*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); Regex SRCRegex = new Regex( @"src"+regexFileName, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); Regex StyleSheetRegex = new Regex( @" 0; i--) { UInt32 curBlockIdx = (UInt32)(block-i); /* check if we most recently decompressed the previous block */ if ((ulong)lzx_last_block != curBlockIdx) { if ((curBlockIdx % reset_blkcount)==0) { lzx_state.LZXreset(); } _chm_get_cmpblock_bounds(st,curBlockIdx, ref cmpStart, ref cmpLen); st.BaseStream.Seek((long)cmpStart,SeekOrigin.Begin); if (lzx_state.LZXdecompress(st,OutBuffer, ref cmpLen, ref reset_table.block_len) != lzw.DECR_OK) return (Int64)0; } lzx_last_block = (int)(curBlockIdx); } } else { if ((block % reset_blkcount)==0) { lzx_state.LZXreset(); } } // decompress the block we actually want if (_chm_get_cmpblock_bounds(st, block, ref cmpStart, ref cmpLen)==0) return 0; st.BaseStream.Seek((long)cmpStart,SeekOrigin.Begin); if (lzx_state.LZXdecompress(st, OutBuffer, ref cmpLen,ref reset_table.block_len) != lzw.DECR_OK) return (Int64)0; lzx_last_block = (int)block; // XXX: modify LZX routines to return the length of the data they // * decompressed and return that instead, for an extra sanity check. return reset_table.block_len; } // grab a region from a compressed block private ulong _chm_decompress_region(Stream buf, ulong start, ulong len) { ulong nBlock, nOffset; ulong nLen; ulong gotLen; // byte [] ubuffer=null; if (len <= 0) return (Int64)0; // figure out what we need to read nBlock = start / reset_table.block_len; nOffset = start % reset_table.block_len; nLen = len; if (nLen > (reset_table.block_len - nOffset)) nLen = reset_table.block_len - nOffset; // data request not satisfied, so... start up the decompressor machine if (lzx_state==null) { int window_size = ffs(this.window_size) - 1; lzx_last_block = -1; lzx_state=new lzw(); lzx_state.LZXinit(window_size); } // decompress some data MemoryStream ms=new MemoryStream((int)reset_table.block_len+6144); gotLen = _chm_decompress_block(nBlock, ms); if (gotLen < nLen) nLen = gotLen; // memcpy(buf, ubuffer+nOffset, (unsigned int)nLen); ms.Position=(long)nOffset; for(ulong i=0;i= (ulong)ui.length) return (Int64)0; // clip length if (addr + (ulong)len > (ulong)ui.length) len = (ulong)ui.length - (ulong)addr; // if the file is uncompressed, it's simple if (ui.space == CHMStream.CHM_COMPRESSION.CHM_UNCOMPRESSED) { // read data long FilePos=st.BaseStream.Position; st.BaseStream.Seek((long)((long)data_offset + (long)ui.start + (long)addr),SeekOrigin.Begin); // byte [] buffer=st.ReadBytes((int)len); buf.Write(st.ReadBytes((int)len),0,(int) len); st.BaseStream.Seek(FilePos,SeekOrigin.Begin); return (ulong)len; } // else if the file is compressed, it's a little trickier else // ui->space == CHM_COMPRESSED { if (lzx_state!=null) { lzx_state.LZXteardown(); lzx_state=null; } ulong swath=0, total=0; do { if (!compression_enabled) return total; // swill another mouthful swath = _chm_decompress_region(buf, ui.start + addr, len); // if we didn't get any... if (swath == 0) { Trace.Assert((total!=ui.length),"De-compress failed","Length Required = "+ui.length.ToString()+" Length returned = "+total.ToString()); return total; } // update stats total += swath; len -= swath; addr += swath; } while (len != 0); lzx_state=null; Trace.Assert((len!=ui.length),"De-compress failed","Length Required = "+ui.length.ToString()+" Length returned = "+len.ToString()); return len; } } #endregion #region Enumerate functions // Enumerate the objects in the .chm archive // Use delegate to handle callback public delegate void CHMFileFound(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status); public event CHMFileFound CHMFileFoundEvent; public void OnFileFound(chmUnitInfo Info, ref CHMStream.CHM_ENUMERATOR Status) { if (CHMFileFoundEvent!=null) CHMFileFoundEvent(Info,ref Status); } private int chm_enumerate(CHM_ENUMERATE what) { Int32 curPage; // buffer to hold whatever page we're looking at chmPmglHeader header; uint end=0; uint cur=0; // the current ui chmUnitInfo ui= new chmUnitInfo(); CHMStream.CHM_ENUMERATE flag=CHMStream.CHM_ENUMERATE.None; // starting page curPage = index_head; // until we have either returned or given up while (curPage != -1) { st.BaseStream.Seek((long)((long)dir_offset + (long)(curPage*block_len)),SeekOrigin.Begin); // figure out start and end for this page cur = (uint)st.BaseStream.Position; header=new chmPmglHeader(); if (header.Read_pmgl_header(st)==0) return 0; end = (uint)(st.BaseStream.Position + block_len - (header.free_space)- chmPmglHeader._CHM_PMGL_LEN); // loop over this page while (st.BaseStream.Position < end) { if (header._chm_parse_PMGL_entry(st,ref ui)==0) return 0; // check for DIRS if (ui.length == 0 && ((what & CHM_ENUMERATE.CHM_ENUMERATE_DIRS)==0)) continue; // check for FILES if (ui.length != 0 && ((what & CHM_ENUMERATE.CHM_ENUMERATE_FILES)==0)) continue; // check for NORMAL vs. META if (ui.path[0] == '/') { // check for NORMAL vs. SPECIAL if (ui.path.Length>2) { if (ui.path[1] == '#' || ui.path[1] == '$') flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_SPECIAL; else flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_NORMAL; } else flag = CHMStream.CHM_ENUMERATE.CHM_ENUMERATE_META; if ((what & flag)==0) continue; } // call the enumerator { CHMStream.CHM_ENUMERATOR status = CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE; OnFileFound(ui,ref status); switch (status) { case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_FAILURE: return 0; case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_CONTINUE: break; case CHMStream.CHM_ENUMERATOR.CHM_ENUMERATOR_SUCCESS: return 1; default: break; } } } // advance to next page curPage = header.block_next; } return 1; } #endregion #region IDisposable Members private bool disposed=false; public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } /// /// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly /// or indirectly by a user's code. Managed and unmanaged resources /// can be disposed. /// If disposing equals false, the method has been called by the /// runtime from inside the finalizer and you should not reference /// other objects. Only unmanaged resources can be disposed. /// /// disposing flag private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. } } disposed = true; } #endregion } #region Structures used by CHM Storage public class BaseStructure { public bool CheckSig(string Sig1, char[] Sig2) { int i=0; foreach(char ch in Sig1.ToCharArray()) { if (ch!=Sig2[i]) return false; i++; } return true; } // skip a compressed dword public void skip_cword(BinaryReader st) { byte b=0; while ((b=st.ReadByte())>= 0x80); } // skip the data from a PMGL entry public void _chm_skip_PMGL_entry_data(BinaryReader st) { skip_cword(st); skip_cword(st); skip_cword(st); } // parse a compressed dword public UInt64 _chm_parse_cword(BinaryReader st) { UInt64 accum = 0; byte temp=0; while ((temp=st.ReadByte()) >= 0x80) { accum <<= 7; accum += (ulong)(temp & 0x7f); } return (accum << 7) + temp; } // parse a utf-8 string into an ASCII char buffer public int _chm_parse_UTF8(BinaryReader st, UInt64 count, ref string path) { UTF8Encoding utf8=new UTF8Encoding(); path=utf8.GetString(st.ReadBytes((int)count),0,(int)count); return 1; } } public class chmUnitInfo { public UInt64 start=0; public UInt64 length=0; public CHMStream.CHM_COMPRESSION space=CHMStream.CHM_COMPRESSION.CHM_UNCOMPRESSED; public string path=""; } // structure of ITSF headers public class chmItsfHeader : BaseStructure { public const int _CHM_ITSF_V2_LEN=0x58; public const int _CHM_ITSF_V3_LEN=0x60; public char[] signature=null; // 0 (ITSF) public Int32 version=0; // 4 public Int32 header_len=0; // 8 public Int32 unknown_000c=0; // c public UInt32 last_modified=0; // 10 public UInt32 lang_id=0; // 14 public Guid dir_uuid; // 18 public Guid stream_uuid; // 28 public UInt64 unknown_offset=0; // 38 public UInt64 unknown_len=0; // 40 public UInt64 dir_offset=0; // 48 public UInt64 dir_len=0; // 50 public UInt64 data_offset=0; // 58 (Not present before V3) public int Read_itsf_header(BinaryReader st) { signature=st.ReadChars(4); if (CheckSig("ITSF",signature)==false) return 0; version=st.ReadInt32(); header_len=st.ReadInt32(); unknown_000c=st.ReadInt32(); last_modified=st.ReadUInt32(); lang_id=st.ReadUInt32(); dir_uuid=new Guid(st.ReadBytes(16)); stream_uuid=new Guid(st.ReadBytes(16)); unknown_offset=st.ReadUInt64(); unknown_len=st.ReadUInt64(); dir_offset=st.ReadUInt64(); dir_len=st.ReadUInt64(); if (version==2) { if (header_len != chmItsfHeader._CHM_ITSF_V2_LEN) return 0; } else if (version==3) { if (header_len != chmItsfHeader._CHM_ITSF_V3_LEN) return 0; } else return 0; if (version==3) data_offset=st.ReadUInt64(); else data_offset = dir_offset + dir_len; return 1; } } // structure of ITSP headers public class chmItspHeader : BaseStructure { const int CHM_ITSP_V1_LEN=0x54; public char[] signature=null; // 0 (ITSP) public Int32 version=0; public Int32 header_len=0; public Int32 unknown_000c=0; public UInt32 block_len=0; public Int32 blockidx_intvl=0; public Int32 index_depth=0; public Int32 index_root=0; public Int32 index_head=0; public Int32 unknown_0024=0; public Int32 num_blocks=0; public Int32 unknown_002c=0; public UInt32 lang_id=0; public Guid system_uuid; public Guid unknown_0044; public int Read_itsp_header(BinaryReader st) { signature=st.ReadChars(4); // 0 (ITSP) if (CheckSig("ITSP",signature)==false) return 0; version=st.ReadInt32(); header_len=st.ReadInt32(); if (header_len!=CHM_ITSP_V1_LEN) return 0; unknown_000c=st.ReadInt32(); block_len=st.ReadUInt32(); blockidx_intvl=st.ReadInt32(); index_depth=st.ReadInt32(); index_root=st.ReadInt32(); index_head=st.ReadInt32(); unknown_0024=st.ReadInt32(); num_blocks=st.ReadInt32(); unknown_002c=st.ReadInt32(); lang_id=st.ReadUInt32(); system_uuid=new Guid(st.ReadBytes(16)); unknown_0044=new Guid(st.ReadBytes(16)); return 1; } } public class chmPmglHeader : BaseStructure { public const int _CHM_PMGL_LEN=0x14; public char[] signature=null; // 0 (PMGL) public UInt32 free_space=0; // 4 public UInt32 unknown_0008=0; // 8 public Int32 block_prev=0; // c public Int32 block_next=0; // 10 public int Read_pmgl_header(BinaryReader st) { signature=st.ReadChars(4); if (CheckSig("PMGL",signature)==false) return 0; free_space=st.ReadUInt32(); unknown_0008=st.ReadUInt32(); block_prev=st.ReadInt32(); block_next=st.ReadInt32(); return 1; } // parse a PMGL entry into a chmUnitInfo struct; return 1 on success. public int _chm_parse_PMGL_entry(BinaryReader st, ref chmUnitInfo ui) { UInt64 strLen; // parse str len strLen = _chm_parse_cword(st); // parse path if (_chm_parse_UTF8(st, strLen, ref ui.path)==0) return 0; // parse info ui.space = (CHMStream.CHM_COMPRESSION)_chm_parse_cword(st); ui.start = _chm_parse_cword(st); ui.length = _chm_parse_cword(st); return 1; } public chmUnitInfo FindObject(BinaryReader st, UInt32 block_len, string objPath) { UInt32 end = (UInt32)st.BaseStream.Position+ block_len - free_space - _CHM_PMGL_LEN; // now, scan progressively chmUnitInfo FoundObject=new chmUnitInfo(); while (st.BaseStream.Position < end) { _chm_parse_PMGL_entry(st,ref FoundObject); if (FoundObject.path.ToLower().CompareTo(objPath.ToLower())==0) return FoundObject; } FoundObject=null; return null; } } public class chmPmgiHeader : BaseStructure { public const int _CHM_PMGI_LEN=0x8; public char[] signature=null; // 0 (PMGL) public UInt32 free_space=0; // 4 public int Read_pmgi_header(BinaryReader st) { signature=st.ReadChars(4); if ((signature[0]!='P') || (signature[1]!='M') || (signature[2]!='G') || (signature[3]!='I')) return 0; free_space=st.ReadUInt32(); return 1; } public Int32 _chm_find_in_PMGI(BinaryReader st, UInt32 block_len, string objPath) { int page=-1; UInt64 strLen; string buffer=""; uint end = (uint)st.BaseStream.Position + block_len - free_space - _CHM_PMGI_LEN; // now, scan progressively while (st.BaseStream.Position < end) { // grab the name strLen = _chm_parse_cword(st); buffer=""; if (_chm_parse_UTF8(st, strLen, ref buffer)==0) return -1; // check if it is the right name if (buffer.ToLower().CompareTo(objPath.ToLower())>0) return page; // load next value for path page = (int)_chm_parse_cword(st); } return page; } } public class chmLzxcResetTable:BaseStructure { public UInt32 version=0; public UInt32 block_count=0; public UInt32 unknown=0; public UInt32 table_offset=0; public UInt64 uncompressed_len=0; public UInt64 compressed_len=0; public UInt64 block_len=0; public int Read_lzxc_reset_table(BinaryReader st) { version=st.ReadUInt32(); block_count=st.ReadUInt32(); unknown=st.ReadUInt32(); table_offset=st.ReadUInt32(); uncompressed_len=st.ReadUInt64(); compressed_len=st.ReadUInt64(); block_len=st.ReadUInt64(); // check structure if (version != 2) return 0; else return 1; } } // structure of LZXC control data block public class chmLzxcControlData:BaseStructure { public const int _CHM_LZXC_MIN_LEN=0x18; public const int _CHM_LZXC_V2_LEN=0x1c; public UInt32 size=0; // 0 public char[] signature=null; // 4 (LZXC) public UInt32 version=0; // 8 public UInt32 resetInterval=0; // c public UInt32 windowSize=0; // 10 public UInt32 windowsPerReset=0; // 14 public UInt32 unknown_18=0; // 18 public int Read_lzxc_control_data(BinaryReader st) { size=st.ReadUInt32(); signature=st.ReadChars(4); if (CheckSig("LZXC",signature)==false) return 0; version=st.ReadUInt32(); resetInterval=st.ReadUInt32(); windowSize=st.ReadUInt32(); windowsPerReset=st.ReadUInt32(); if (size>=_CHM_LZXC_V2_LEN) unknown_18=st.ReadUInt32(); else unknown_18 = 0; if (version == 2) { resetInterval *= 0x8000; windowSize *= 0x8000; } if (windowSize == 0 || resetInterval == 0) return 0; // for now, only support resetInterval a multiple of windowSize/2 if (windowSize == 1) return 0; if ((resetInterval % (windowSize/2)) != 0) return 0; return 1; } } #endregion #region LZW Decoder internal class lzx_bits { public UInt32 bb=0; public int bl=0; } internal class lzw { public lzw() { } /* $Id: lzx.c,v 1.5 2002/10/09 01:16:33 jedwin Exp $ */ /*************************************************************************** * lzx.c - LZX decompression routines * * ------------------- * * * * maintainer: Jed Wing * * source: modified lzx.c from cabextract v0.5 * * notes: This file was taken from cabextract v0.5, which was, * * itself, a modified version of the lzx decompression code * * from unlzx. * * * * platforms: In its current incarnation, this file has been tested on * * two different Linux platforms (one, redhat-based, with a * * 2.1.2 glibc and gcc 2.95.x, and the other, Debian, with * * 2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2). Both were * * Intel x86 compatible machines. * ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. Note that an exemption to this * * license has been granted by Stuart Caie for the purposes of * * distribution with CHMFile. This does not, to the best of my * * knowledge, constitute a change in the license of this (the LZX) code * * in general. * * * ***************************************************************************/ /* some constants defined by the LZX specification */ private const int LZX_MIN_MATCH = 2; private const int LZX_MAX_MATCH = 257; private const int LZX_NUM_CHARS = 256; private const int LZX_BLOCKTYPE_INVALID = 0; /* also blocktypes 4-7 invalid */ private const int LZX_BLOCKTYPE_VERBATIM = 1; private const int LZX_BLOCKTYPE_ALIGNED = 2; private const int LZX_BLOCKTYPE_UNCOMPRESSED = 3; private const int LZX_PRETREE_NUM_ELEMENTS = 20; private const int LZX_ALIGNED_NUM_ELEMENTS = 8; /* aligned offset tree #elements */ private const int LZX_NUM_PRIMARY_LENGTHS = 7; /* this one missing from spec! */ private const int LZX_NUM_SECONDARY_LENGTHS = 249; /* length tree #elements */ /* LZX huffman defines: tweak tablebits as desired */ private const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS; private const int LZX_PRETREE_TABLEBITS = 6; private const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 50*8; private const int LZX_MAINTREE_TABLEBITS = 12; private const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS+1; private const int LZX_LENGTH_TABLEBITS = 12; private const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS; private const int LZX_ALIGNED_TABLEBITS = 7; private const int LZX_LENTABLE_SAFETY = 64; /* we allow length table decoding overruns */ public const int DECR_OK = 0; public const int DECR_DATAFORMAT = 1; public const int DECR_ILLEGALDATA = 2; public const int DECR_NOMEMORY = 3; private byte[] window; /* the actual decoding window */ private ulong window_size; /* window size (32Kb through 2Mb) */ private ulong actual_size; /* window size when it was first allocated */ private ulong window_posn; /* current offset within the window */ private ulong R0, R1, R2; /* for the LRU offset system */ private UInt32 main_elements; /* number of main tree elements */ private int header_read; /* have we started decoding at all yet? */ private UInt32 block_type; /* type of this block */ private ulong block_length; /* uncompressed length of this block */ private ulong block_remaining; /* uncompressed bytes still left to decode */ private ulong frames_read; /* the number of CFDATA blocks processed */ private long intel_filesize; /* magic header value used for transform */ private long intel_curpos; /* current offset in transform space */ private int intel_started; /* have we seen any translatable data yet? */ private uint [] PRETREE_table = new uint[(1<<(6)) + (((20))<<1)]; private byte [] PRETREE_len = new byte [((20)) + (64)]; private uint [] MAINTREE_table= new uint[(1<<(12)) + (((256) + 50*8)<<1)]; private byte [] MAINTREE_len = new byte [((256) + 50*8) + (64)]; private uint [] LENGTH_table= new uint[(1<<(12)) + (((249)+1)<<1)]; private byte [] LENGTH_len = new byte [((249)+1) + (64)]; private uint [] ALIGNED_table= new uint[(1<<(7)) + (((8))<<1)]; private byte [] ALIGNED_len = new byte [((8)) + (64)]; private System.IO.BinaryReader BitSource=null; private System.IO.Stream OutputStream=null; /* LZX decruncher */ /* Microsoft's LZX document and their implementation of the * com.ms.util.cab Java package do not concur. * * In the LZX document, there is a table showing the correlation between * window size and the number of position slots. It states that the 1MB * window = 40 slots and the 2MB window = 42 slots. In the implementation, * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the * first slot whose position base is equal to or more than the required * window size'. This would explain why other tables in the document refer * to 50 slots rather than 42. * * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode * is not defined in the specification. * * The LZX document does not state the uncompressed block has an * uncompressed length field. Where does this length field come from, so * we can know how large the block is? The implementation has it as the 24 * bits following after the 3 blocktype bits, before the alignment * padding. * * The LZX document states that aligned offset blocks have their aligned * offset huffman tree AFTER the main and length trees. The implementation * suggests that the aligned offset tree is BEFORE the main and length * trees. * * The LZX document decoding algorithm states that, in an aligned offset * block, if an extra_bits value is 1, 2 or 3, then that number of bits * should be read and the result added to the match offset. This is * correct for 1 and 2, but not 3, where just a huffman symbol (using the * aligned tree) should be read. * * Regarding the E8 preprocessing, the LZX document states 'No translation * may be performed on the last 6 bytes of the input block'. This is * correct. However, the pseudocode provided checks for the *E8 leader* * up to the last 6 bytes. If the leader appears between -10 and -7 bytes * from the end, this would cause the next four bytes to be modified, at * least one of which would be in the last 6 bytes, which is not allowed * according to the spec. * * The specification states that the huffman trees must always contain at * least one element. However, many CAB files contain blocks where the * length tree is completely empty (because there are no matches), and * this is expected to succeed. */ /* LZX uses what it calls 'position slots' to represent match offsets. * What this means is that a small 'position slot' number and a small * offset from that slot are encoded instead of one large offset for * every match. * - position_base is an index to the position slot bases * - extra_bits states how many bits of offset-from-base data is needed. */ private byte [] extra_bits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 }; private ulong [] position_base = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, 1835008, 1966080, 2097152 }; private UInt32 ReadUInt16() { UInt32 rc=0; UInt32 Byte1=0; UInt32 Byte2=0; try { Byte1=BitSource.ReadByte(); Byte2=BitSource.ReadByte(); } catch(Exception) { } rc=(Byte2<<8)+Byte1; return rc; } public bool LZXinit(int WindowSize) { ulong wndsize = (ulong)(1 << WindowSize); int i, posn_slots; /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ /* if a previously allocated window is big enough, keep it */ if (WindowSize< 15 || WindowSize> 21) return false; /* allocate state and associated window */ window = new byte[wndsize]; if (window==null) { return false; } actual_size = wndsize; window_size = wndsize; /* calculate required position slots */ if (WindowSize == 20) posn_slots = 42; else if (WindowSize== 21) posn_slots = 50; else posn_slots = WindowSize << 1; /** alternatively **/ /* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */ /* initialize other state */ R0 = R1 = R2 = 1; main_elements = (uint)(LZX_NUM_CHARS + (posn_slots << 3)); header_read = 0; frames_read = 0; block_remaining = 0; block_type = LZX_BLOCKTYPE_INVALID; intel_curpos = 0; intel_started = 0; window_posn = 0; /* initialise tables to 0 (because deltas will be applied to them) */ for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) MAINTREE_len[i] = 0; for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LENGTH_len[i] = 0; return true; } public void LZXteardown() { window=null; } public int LZXreset() { R0 = R1 = R2 = 1; header_read = 0; frames_read = 0; block_remaining = 0; block_type = LZX_BLOCKTYPE_INVALID; intel_curpos = 0; intel_started = 0; window_posn = 0; for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) MAINTREE_len[i] = 0; for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) LENGTH_len[i] = 0; return DECR_OK; } /* Bitstream reading macros: * * INIT_BITSTREAM should be used first to set up the system * READ_BITS(var,n) takes N bits from the buffer and puts them in var * * ENSURE_BITS(n) ensures there are at least N bits in the bit buffer * PEEK_BITS(n) extracts (without removing) N bits from the bit buffer * REMOVE_BITS(n) removes N bits from the bit buffer * * These bit access routines work by using the area beyond the MSB and the * LSB as a free source of zeroes. This avoids having to mask any bits. * So we have to know the bit width of the bitbuffer variable. This is * sizeof(ulong) * 8, also defined as ULONG_BITS */ /* number of bits in ulong. Note: This must be at multiple of 16, and at * least 32 for the bitbuffer code to work (ie, it must be able to ensure * up to 17 bits - that's adding 16 bits when there's one bit left, or * adding 32 bits when there are no bits left. The code should work fine * for machines where ulong >= 32 bits. */ private int ULONG_BITS() { int rc=(System.Runtime.InteropServices.Marshal.SizeOf(typeof(System.UInt32))<<3); return rc; } /* make_decode_table(nsyms, nbits, length[], table[]) * * This function was coded by David Tritscher. It builds a fast huffman * decoding table out of just a canonical huffman code lengths table. * * nsyms = total number of symbols in this huffman tree. * nbits = any symbols with a code length of nbits or less can be decoded * in one lookup of the table. * length = A table to get code lengths from [0 to syms-1] * table = The table to fill up with decoded symbols and pointers. * * Returns 0 for OK or 1 for error */ private int make_decode_table(ulong nsyms, byte nbits, ref byte [] length, ref UInt32[] table) { ulong sym; ulong leaf; byte bit_num = 1; ulong fill; ulong pos = 0; /* the current position in the decode table */ ulong table_mask = (ulong)(1 << nbits); ulong bit_mask = table_mask >> 1; /* don't do 0 length codes */ ulong next_symbol = bit_mask; /* base of allocation for long codes */ /* fill entries for codes short enough for a direct mapping */ while (bit_num <= nbits) { for (sym = 0; sym < nsyms; sym++) { if (length[sym] == bit_num) { leaf = pos; if((pos += bit_mask) > table_mask) return 1; /* table overrun */ /* fill all possible lookups of this symbol with the symbol itself */ fill = bit_mask; while (fill-- > 0) table[leaf++] = (uint)sym; } } bit_mask >>= 1; bit_num++; } /* if there are any codes longer than nbits */ if (pos != table_mask) { /* clear the remainder of the table */ for (sym = pos; sym < table_mask; sym++) table[sym] = 0; /* give ourselves room for codes to grow by up to 16 more bits */ pos <<= 16; table_mask <<= 16; bit_mask = 1 << 15; while (bit_num <= 16) { for (sym = 0; sym < nsyms; sym++) { if (length[sym] == bit_num) { leaf = pos >> 16; for (fill = 0; fill < (ulong)(bit_num - nbits); fill++) { /* if this path hasn't been taken yet, 'allocate' two entries */ if (table[leaf] == 0) { table[(next_symbol << 1)] = 0; table[(next_symbol << 1) + 1] = 0; table[leaf] = (uint)next_symbol++; } /* follow the path and select either left or right for next bit */ leaf = table[leaf] << 1; if (((pos >> (byte)(15-fill)) & 1)==1) leaf++; } table[leaf] = (uint)sym; if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ } } bit_mask >>= 1; bit_num++; } } /* full table? */ if (pos == table_mask) return 0; /* either erroneous table, or all elements are 0 - let's find out. */ for (sym = 0; sym < nsyms; sym++) if (length[(uint)sym]!=0) return 1; return 0; } private int lzx_read_lens(byte []lens, ulong first, ulong last, ref lzx_bits lb) { ulong i,j, x,y; int z; UInt32 bitbuf = lb.bb; int bitsleft = lb.bl; UInt32 [] hufftbl=null; for (x = 0; x < 20; x++) { do { while (bitsleft < (4)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } y = (bitbuf >> (ULONG_BITS()- (4))); bitbuf <<= 4; bitsleft -= 4; } while (false); PRETREE_len[x] = (byte)y; } if (make_decode_table( 20, 6, ref PRETREE_len, ref PRETREE_table)!=0) return 2; for (x = first; x < last; ) { do { while (bitsleft < 16) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = PRETREE_table; if ((i = hufftbl[((ulong)bitbuf >> (ULONG_BITS()- 6))]) >= 20) { j = (ulong)(1 << (byte)(ULONG_BITS()- ((6)))); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= 20); } z = (int)i; j = PRETREE_len[z]; bitbuf <<= (byte)j; bitsleft -= (int)j; } while (false); if (z == 17) { do { while (bitsleft < (4)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } y = (bitbuf >> (ULONG_BITS()- (4))); bitbuf <<= 4; bitsleft -= 4; } while(false); y += 4; while ((y--)!=0) lens[x++] = 0; } else if (z == 18) { do { while (bitsleft < (5)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } (y) = (bitbuf >> (ULONG_BITS()- (5))); bitbuf <<= 5; bitsleft -= 5; } while (false); y += 20; while ((y--)!=0) lens[x++] = 0; } else if (z == 19) { do { while (bitsleft < (1)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } y = (bitbuf >> (ULONG_BITS()- (1))); bitbuf <<= 1; bitsleft -= 1; } while(false); y += 4; do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = (PRETREE_table); if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 6))]) >= 20) { j = (ulong)1 << (byte)(ULONG_BITS()- 6); do { j >>= 1; i <<= 1; if ((bitbuf & j)==0) i|=0; else i|=1; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= 20); } z = (int)i; j = PRETREE_len[z]; bitbuf <<= (byte)j; bitsleft -= (int)j; } while(false); z = lens[x] - z; if (z < 0) z += 17; while ((y--)!=0) lens[x++] = (byte)z; } else { z = lens[x] - z; if (z < 0) z += 17; lens[x++] = (byte)z; } } lb.bb = bitbuf; lb.bl = bitsleft; return 0; } public int LZXdecompress(System.IO.BinaryReader inpos, System.IO.Stream outpos, ref ulong inlen, ref ulong outlen) { BitSource=inpos; OutputStream=outpos; long endinp = BitSource.BaseStream.Position+(long)inlen; ulong runsrc, rundest; UInt32 [] hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ UInt32 bitbuf; int bitsleft; ulong match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */ lzx_bits lb; /* used in READ_LENGTHS macro */ lb=new lzx_bits(); int togo = (int)outlen, this_run, main_element, aligned_bits; int match_length, length_footer, extra, verbatim_bits; bitsleft = 0; bitbuf = 0; /* read header if necessary */ if (header_read==0) { i = j = 0; do { while (bitsleft < (1)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } k = (bitbuf >> (ULONG_BITS()- (1))); bitbuf <<= 1; bitsleft -= 1; } while(false); if (k!=0) { do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() -16 - bitsleft); bitsleft += 16; } i = (bitbuf >> (ULONG_BITS()- (16))); bitbuf <<= 16; bitsleft -= 1; } while(false); do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } j = (bitbuf >> (ULONG_BITS()- (16))); bitbuf <<= 16; bitsleft -= 16; } while(false); } intel_filesize = (long)((i << 16) | j); header_read = 1; } /* main decoding loop */ while (togo > 0) { if (block_remaining == 0) { if (block_type == (3)) { if ((block_length & 1)!=0) BitSource.ReadByte(); bitsleft = 0; bitbuf = 0; } do { while (bitsleft < (3)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } (block_type) = (uint)(bitbuf >> (ULONG_BITS()- (3))); bitbuf <<= 3; bitsleft -= 3; } while (false); do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } (i) = (bitbuf >> (ULONG_BITS()- (16))); bitbuf <<= 16; bitsleft -= 16; } while (false); do { while (bitsleft < (8)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } (j) = (bitbuf >> (ULONG_BITS()- (8))); bitbuf <<= 8; bitsleft -= 8; } while (false); block_remaining = block_length = (i << 8) | j; switch (block_type) { case (LZX_BLOCKTYPE_ALIGNED): for (i = 0; i < 8; i++) { do { while (bitsleft < (3)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } (j) = (bitbuf >> (ULONG_BITS()- (3))); bitbuf <<= 3; bitsleft -= 3; } while (false); (ALIGNED_len)[i] = (byte)j; } if (make_decode_table( 8, 7, ref ALIGNED_len, ref ALIGNED_table)!=0) { return (2); } do { lb.bb = bitbuf; lb.bl = bitsleft; if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0) { return (2); } bitbuf = lb.bb; bitsleft = lb.bl; } while (false); do { lb.bb = bitbuf; lb.bl = bitsleft; if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0) { return (2); } bitbuf = lb.bb; bitsleft = lb.bl; } while (false); if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0) { return (2); } if (MAINTREE_len[0xE8] != 0) intel_started = 1; do { lb.bb = bitbuf; lb.bl = bitsleft; if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0) { return (2); } bitbuf = lb.bb; bitsleft = lb.bl; } while (false); if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0) { return (2); } break; case (LZX_BLOCKTYPE_VERBATIM): do { lb.bb = bitbuf; lb.bl = bitsleft; if (lzx_read_lens(MAINTREE_len,0,256,ref lb)!=0) { return (2); } bitbuf = lb.bb; bitsleft = lb.bl; } while (false); do { lb.bb = bitbuf; lb.bl = bitsleft; if (lzx_read_lens(MAINTREE_len,256,main_elements,ref lb)!=0) { return (2); } bitbuf = lb.bb; bitsleft = lb.bl; } while (false); if (make_decode_table( (256 + 50*8), 12, ref MAINTREE_len, ref MAINTREE_table)!=0) { return (2); } if (MAINTREE_len[0xE8] != 0) intel_started = 1; do { lb.bb = bitbuf; lb.bl = bitsleft; if (lzx_read_lens(LENGTH_len,0,249,ref lb)!=0) { return (2); } bitbuf = lb.bb; bitsleft = lb.bl; } while (false); if (make_decode_table( (249+1), 12, ref LENGTH_len, ref LENGTH_table)!=0) { return (2); } break; case (LZX_BLOCKTYPE_UNCOMPRESSED): intel_started = 1; while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - bitsleft); bitsleft += 16; } if (bitsleft > 16) { BitSource.BaseStream.Seek(-2,System.IO.SeekOrigin.Current); } R0 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24)); R1 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24)); R2 = (ulong)(BitSource.ReadByte()+(BitSource.ReadByte()<<8)+(BitSource.ReadByte()<<16)+(BitSource.ReadByte()<<24)); break; default: return (DECR_ILLEGALDATA); } } /* buffer exhaustion check */ if (BitSource.BaseStream.Position > (long) endinp) { /* it's possible to have a file where the next run is less than * 16 bits in size. In this case, the READ_HUFFSYM() macro used * in building the tables will exhaust the buffer, so we should * allow for this, but not allow those accidentally read bits to * be used (so we check that there are at least 16 bits * remaining - in this boundary case they aren't really part of * the compressed data) */ if (BitSource.BaseStream.Position> (long)(endinp+2) || bitsleft < 16) return DECR_ILLEGALDATA; } while ((this_run = (int)block_remaining) > 0 && togo > 0) { if (this_run > togo) this_run = togo; togo -= this_run; block_remaining -= (ulong)this_run; /* apply 2^x-1 mask */ window_posn &= window_size - 1; /* runs can't straddle the window wraparound */ if ((window_posn + (ulong)this_run) > window_size) return DECR_DATAFORMAT; switch (block_type) { case LZX_BLOCKTYPE_VERBATIM: while (this_run > 0) { do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = MAINTREE_table; if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= 256 + 50*8) { j = (ulong)(1 << (ULONG_BITS()- 12)); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= (((256) + 50*8))); } j = MAINTREE_len[main_element = (int)i]; bitbuf <<= (byte)j; bitsleft -= (byte)j; } while (false); if (main_element < (256)) { window[window_posn++] = (byte)main_element; this_run--; } else { main_element -= (256); match_length = main_element & (7); if (match_length == (7)) { do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = (LENGTH_table); if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((249)+1))) { j = (ulong)(1 << (ULONG_BITS()- ((12)))); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= (((249)+1))); } j = LENGTH_len[(length_footer) = (int)i]; bitbuf <<= (byte)j; bitsleft -= (byte)j; } while (false); match_length += length_footer; } match_length += (2); match_offset = (ulong)(main_element >> 3); if (match_offset > 2) { if (match_offset != 3) { extra = extra_bits[match_offset]; do { while (bitsleft < (extra)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } verbatim_bits = (int)(bitbuf >> (ULONG_BITS()- (extra))); bitbuf <<= extra; bitsleft -= extra; } while (false); match_offset = position_base[match_offset] - 2 + (ulong)verbatim_bits; } else { match_offset = 1; } R2 = R1; R1 = R0; R0 = match_offset; } else if (match_offset == 0) { match_offset = R0; } else if (match_offset == 1) { match_offset = R1; R1 = R0; R0 = match_offset; } else { match_offset = R2; R2 = R0; R0 = match_offset; } rundest = window_posn; // rundest= window+window_posn runsrc = rundest - match_offset; window_posn += (ulong)match_length; this_run -= match_length; // runsrc < window while ((runsrc<0) && (match_length-- > 0)) { window[rundest++]=window[runsrc+window_size]; // *rundest++ = *(runsrc + window_size); runsrc++; } while (match_length-- > 0) { window[rundest++]=window[runsrc++]; // *rundest++ = *runsrc++; } } } break; case LZX_BLOCKTYPE_ALIGNED: while (this_run > 0) { do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = MAINTREE_table; if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((256) + 50*8))) { j = (ulong)1 << (ULONG_BITS()- ((12))); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= (((256) + 50*8))); } j = MAINTREE_len[(main_element) = (int)i]; bitbuf <<= (int)j; bitsleft -= (int)j; } while (false); if (main_element < (256)) { window[window_posn++] = (byte)main_element; this_run--; } else { main_element -= (256); match_length = main_element & (7); if (match_length == (7)) { do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = LENGTH_table; if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 12))]) >= (((249)+1))) { j = (ulong) 1 << (ULONG_BITS()- ((12))); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= (((249)+1))); } j = LENGTH_len[length_footer = (int)i]; bitbuf <<= (int)j; bitsleft -= (int)j; } while (false); match_length += length_footer; } match_length += (2); match_offset = (ulong)(main_element >> 3); if (match_offset > 2) { extra = extra_bits[match_offset]; match_offset = position_base[match_offset] - 2; if (extra > 3) { extra -= 3; do { while (bitsleft < (extra)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } verbatim_bits = (int)(bitbuf >> (ULONG_BITS()- (extra))); bitbuf <<= extra; bitsleft -= extra; } while (false); match_offset += (ulong)(verbatim_bits << 3); do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = (ALIGNED_table); if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 7))]) >= 8) { j = (ulong)1 << (ULONG_BITS()- ((7))); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j==0) { return (2); } } while ((i = hufftbl[i]) >= (((8)))); } j = (ALIGNED_len)[(aligned_bits) = (int)i]; bitbuf <<= (int)j; bitsleft -= (int)j; } while (false); match_offset += (ulong)aligned_bits; } else if (extra == 3) { do { while (bitsleft < (16)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } hufftbl = (ALIGNED_table); if ((i = hufftbl[(bitbuf >> (ULONG_BITS()- 7))]) >= 8) { j = (ulong)1 << (ULONG_BITS()- ((7))); do { j >>= 1; i <<= 1; if ((bitbuf & j)!=0) i|=1; else i|=0; if (j!=0) { return (2); } } while ((i = hufftbl[i]) >= 8); } j = (ALIGNED_len)[(aligned_bits) = (int)i]; bitbuf <<= (int)j; bitsleft -= (int)j; } while (false); match_offset += (ulong)aligned_bits; } else if (extra > 0) { do { while (bitsleft < (extra)) { bitbuf |= (UInt32)ReadUInt16() << (ULONG_BITS() - 16 - bitsleft); bitsleft += 16; } (verbatim_bits) = (int)(bitbuf >> (int)(ULONG_BITS()- (extra))); bitbuf <<= extra; bitsleft -= extra; } while (false); match_offset += (ulong)verbatim_bits; } else { match_offset = 1; } R2 = R1; R1 = R0; R0 = match_offset; } else if (match_offset == 0) { match_offset = R0; } else if (match_offset == 1) { match_offset = R1; R1 = R0; R0 = match_offset; } else { match_offset = R2; R2 = R0; R0 = match_offset; } rundest = window_posn; runsrc = rundest - match_offset; window_posn += (ulong)match_length; this_run -= match_length; while ((runsrc<0) && (match_length-- > 0)) { // *rundest++ = *(runsrc + window_size); runsrc++; window[rundest++]=window[runsrc + window_size]; runsrc++; } while (match_length-- > 0) { // *rundest++ = *runsrc++; window[rundest++]=window[runsrc++]; } } } break; case LZX_BLOCKTYPE_UNCOMPRESSED: if ((BitSource.BaseStream.Position + (long)this_run) > (long)endinp) return (2); // memcpy(window + window_posn, inposCount, this_run); for(i=0; i<(ulong)this_run;i++) { window[window_posn+i]=BitSource.ReadByte(); } window_posn += (ulong)this_run; break; default: return DECR_ILLEGALDATA; /* might as well */ } } } if (togo != 0) return DECR_ILLEGALDATA; // memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t) outlen); ulong start=0; if (window_posn==0) start=(ulong)window_size; else start=(ulong)window_posn; start-=(ulong)outlen; long Pos=OutputStream.Position; for(i=0;i<(ulong)outlen;i++) { OutputStream.WriteByte(window[start+i]); } OutputStream.Seek(Pos,System.IO.SeekOrigin.Begin); /* intel E8 decoding */ if ((frames_read++ < 32768) && intel_filesize != 0) { if (outlen <= 6 || (intel_started==0)) { intel_curpos += (long)outlen; } else { // UBYTE *data = outpos; long dataend = OutputStream.Position + (int)outlen - 10; long curpos = intel_curpos; long filesize = intel_filesize; long abs_off, rel_off; intel_curpos = (long)curpos + (long)outlen; while (OutputStream.Position < dataend) { if (OutputStream.ReadByte() != 0xE8) { curpos++; continue; } abs_off = (long)(OutputStream.ReadByte() | (OutputStream.ReadByte() <<8) | (OutputStream.ReadByte() <<16) | (OutputStream.ReadByte() <<24)); if (abs_off < filesize) { if (abs_off >= 0) rel_off = (long)(abs_off - curpos); else rel_off = (long)abs_off + filesize; OutputStream.WriteByte((byte)(rel_off & 0x000000ff)); OutputStream.WriteByte((byte)((rel_off & 0x0000ff00)>>8)); OutputStream.WriteByte((byte)((rel_off & 0x00ff0000)>>16)); OutputStream.WriteByte((byte)((rel_off & 0xff000000)>>24)); } curpos += 5; } } } return DECR_OK; } } #endregion }