private string ChmFileToString(chmUnitInfo ui) { const ulong tmpBufSize = 1025; StringBuilder strb = new StringBuilder(); ulong size = tmpBufSize - 1; ulong cur = 0; IntPtr raw = Marshal.AllocCoTaskMem((int)tmpBufSize); do { size = chm_retrieve_object(chmfile, ui, raw, cur, tmpBufSize - 1); // If I dont create a copy of the string when i free 'raw' the builder data dissapear // the last chunk readed dissapear (mono bug or mi endless stupidity) // I'll have to check it out strb.Append(Marshal.PtrToStringAuto(raw, (int)size)); cur += size; }while(size == tmpBufSize - 1); Marshal.FreeCoTaskMem(raw); return(strb.ToString()); }
static EnumerateStatus EnumeratorCallback(chmFile file, ref chmUnitInfo ui, ref Object context) { if (!ui.path.EndsWith("/")) Console.WriteLine(file.FileName + ": " + ui.path); if (ui.length > 0) { byte[] buf = new byte[ui.length]; long ret = file.RetrieveObject(ui, ref buf, 0, buf.Length); if (ret > 0) { try { FileInfo fi = new FileInfo(Path.Combine(_outdir, ui.path.Trim('/'))); List<DirectoryInfo> created; fi.Directory.CreateDirectory(out created); File.WriteAllBytes(fi.FullName, buf); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); } } } return EnumerateStatus.Continue; }
private int GetHtmlFiles(IntPtr chmFile, chmUnitInfo info, IntPtr context) { if (info.path.EndsWith(".html")) { htmlFiles.Add(info.path.Trim()); } return(1); }
/* * TODO: * We should trow something like a FileNotFoundException */ public TextReader GetFile(string path) { chmUnitInfo ui = new chmUnitInfo(); if (chm_resolve_object(chmfile, path, ui) == ChmResolve.Failure) { //Console.WriteLine("Fails to Open: {0}",path); return(new StringReader("")); } return(new StringReader(ChmFileToString(ui))); }
public void ParseContents(ChmHtmlParseFunc Parse) { if (this.loaded) { foreach (string fileName in htmlFiles) { chmUnitInfo ui = new chmUnitInfo(); chm_resolve_object(this.chmfile, fileName, ui); //Console.WriteLine("Parsing....{0}",ui.path); ///Logger.Log.Debug("CHMFile: Parsing {0}....",ui.path); Parse(new StringReader(ChmFileToString(ui).Trim())); } } }
/* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */ public static bool ParsePgmlEntry(byte[] pEntry, ref uint os, ref chmUnitInfo ui) { char[] buf = new char[Storage.CHM_MAX_PATHLEN]; UInt64 strLen; /* parse str len */ strLen = Storage.ParseCWord(pEntry, ref os); if (strLen > Storage.CHM_MAX_PATHLEN) return false; /* parse path */ if (!Storage.ParseUTF8(pEntry, ref os, strLen, ref buf)) return false; /* parse info */ ui.space = (int)Storage.ParseCWord(pEntry, ref os); ui.start = Storage.ParseCWord(pEntry, ref os); ui.length = Storage.ParseCWord(pEntry, ref os); ui.path = new String(buf); return true; }
/* retrieve (part of) an object */ public LONGINT64 RetrieveObject(chmUnitInfo ui, ref byte[] buf, LONGUINT64 addr, LONGINT64 len) { /* must be valid file handle */ if (_h.fd == null) return (Int64)0; /* starting address must be in correct range */ if (addr < 0 || addr >= ui.length) return (Int64)0; /* clip length */ if (addr + (ulong)len > ui.length) len = (LONGINT64)(ui.length - addr); /* if the file is uncompressed, it's simple */ if (ui.space == Storage.CHM_UNCOMPRESSED) { /* read data */ return Storage.FetchBytes(ref _h, ref buf, (UInt64)_h.data_offset + (UInt64)ui.start + (UInt64)addr, len); } /* else if the file is compressed, it's a little trickier */ else /* ui->space == CHM_COMPRESSED */ { Int64 swath = 0, total = 0; UInt64 bufpos = 0; /* if compression is not enabled for this file... */ if (!_h.compression_enabled) return total; do { /* swill another mouthful */ swath = Lzxc.DecompressRegion(ref _h, ref buf, bufpos + ui.start + addr, len); /* if we didn't get any... */ if (swath == 0) return total; /* update stats */ total += swath; len -= swath; addr += (LONGUINT64)swath; bufpos += (LONGUINT64)swath; /* TODO bobc: this might not be right */ } while (len != 0); return total; } }
private bool WindowsInfo() { int entries; int entrySize; IntPtr windowsData; long size = 0; uint block; const int headerLen = 0x8; IntPtr buf = Marshal.AllocCoTaskMem(bufSize); chmUnitInfo ui = new chmUnitInfo(); if (chm_resolve_object(chmfile, "/#WINDOWS", ui) == ChmResolve.Failure) { return(false); } if (chm_retrieve_object(chmfile, ui, buf, 0, headerLen) == 0) { return(false); } entries = Marshal.ReadInt32(buf); entrySize = Marshal.ReadInt32(buf, 0x4); //Console.WriteLine ("entries -> {0}\nsize = {1}",entries,entrySize); windowsData = Marshal.AllocCoTaskMem(entries * entrySize); size = (long)chm_retrieve_object(chmfile, ui, windowsData, headerLen, (ulong)(entries * entrySize)); if (size == 0) { return(false); } size = 0; if (chm_resolve_object(chmfile, "/#STRINGS", ui) == ChmResolve.Failure) { return(false); } /* * From Pabs' CHM Spec: * "(STRINGS)This file is a list of ANSI/UTF-8 NT strings. * The first is just a NIL character so that offsets to this file can specify * zero & get a valid string. * The strings are sliced up into blocks that are 4096 bytes in length." */ for (int i = 0; i < entries; i++) { int offset = i * entrySize; uint offTitle = (uint)Marshal.ReadInt32(windowsData, offset + 0x14); uint offTocFile = (uint)Marshal.ReadInt32(windowsData, offset + 0x60); uint offDefaultFile = (uint)Marshal.ReadInt32(windowsData, offset + 0x68); //Console.WriteLine("offTocFile = {0}",offTocFile); block = offTitle / 4096; if (size == 0) { size = (long)chm_retrieve_object(chmfile, ui, buf, block * 4096, (ulong)bufSize); } if (size > 0 && offTitle > 0) { this.title = ChmGetString(buf, (int)offTitle, 4096); } if (block != offTocFile / 4096) { block = offTocFile / 4096; size = (long)chm_retrieve_object(chmfile, ui, buf, block * 4096, (ulong)bufSize); } if (size > 0 && offTocFile > 0) { topicsFile = "/" + ChmGetString(buf, (int)offTocFile % 4096, 4096); hasTopics = true; } if (block != offDefaultFile / 4096) { block = offDefaultFile / 4096; size = (long)chm_retrieve_object(chmfile, ui, buf, block * 4096, (ulong)bufSize); } if (size > 0 && offDefaultFile > 0) { defaultFile = ("/" + ChmGetString(buf, (int)offDefaultFile % 4096, 4096)); } } Marshal.FreeCoTaskMem(buf); Marshal.FreeCoTaskMem(windowsData); return(true); }
private int GetHtmlFiles(IntPtr chmFile, chmUnitInfo info, IntPtr context) { if(info.path.EndsWith(".html")) htmlFiles.Add(info.path.Trim()); return 1; }
/** * * From the #SYSTEM File we are interested in the title (for now). * */ private bool SystemInfo() { ChmResolve res; ulong size; bool gottitle = false; if (!loaded) { return(false); } IntPtr buf = Marshal.AllocCoTaskMem(bufSize); chmUnitInfo ui = new chmUnitInfo(); res = chm_resolve_object(this.chmfile, "/#SYSTEM", ui); if (res == ChmResolve.Failure) { return(false); } size = chm_retrieve_object(this.chmfile, ui, buf, 4, (ulong)bufSize); int index = 0; ushort value = 0; long tol = (long)size - 2; while (index < tol) { value = (ushort)Marshal.ReadInt16(buf, index); if (value == 3) { index += 2; ushort len = (ushort)Marshal.ReadInt16(buf, (int)index); if (this.title == "") { this.title = ChmGetString(buf, index + 2, (int)len); } gottitle = true; break; } else { index += 2; } value = (ushort)Marshal.ReadInt16(buf, (int)index); index += (int)value + 2; } Marshal.FreeCoTaskMem(buf); return(gottitle); }
private string ChmFileToString(chmUnitInfo ui) { const ulong tmpBufSize = 1025; StringBuilder strb = new StringBuilder(); ulong size = tmpBufSize -1; ulong cur = 0; IntPtr raw = Marshal.AllocCoTaskMem ((int)tmpBufSize); do { size = chm_retrieve_object(chmfile,ui,raw,cur,tmpBufSize-1); // If I dont create a copy of the string when i free 'raw' the builder data dissapear // the last chunk readed dissapear (mono bug or mi endless stupidity) // I'll have to check it out strb.Append(Marshal.PtrToStringAuto(raw,(int)size)); cur += size; } while(size == tmpBufSize-1); Marshal.FreeCoTaskMem (raw); return strb.ToString(); }
/** From the #SYSTEM File we are interested in the title (for now). */ private bool SystemInfo() { ChmResolve res; ulong size; bool gottitle = false; if(!loaded) return false; IntPtr buf = Marshal.AllocCoTaskMem (bufSize); chmUnitInfo ui = new chmUnitInfo() ; res = chm_resolve_object (this.chmfile,"/#SYSTEM", ui); if(res == ChmResolve.Failure) return false; size = chm_retrieve_object (this.chmfile, ui,buf, 4, (ulong)bufSize); int index = 0; ushort value = 0; long tol = (long)size - 2; while(index < tol) { value = (ushort)Marshal.ReadInt16 (buf, index); if(value == 3) { index += 2; ushort len = (ushort)Marshal.ReadInt16 (buf, (int)index); if(this.title == "") this.title = ChmGetString (buf,index+2, (int)len); gottitle = true; break; } else index += 2; value = (ushort) Marshal.ReadInt16(buf,(int)index); index += (int)value + 2; } Marshal.FreeCoTaskMem (buf); return gottitle; }
private static extern UInt64 chm_retrieve_object(IntPtr raw, [In, Out] chmUnitInfo ui, IntPtr buf, UInt64 addr, UInt64 len);
private static extern ChmResolve chm_resolve_object(IntPtr raw, string objPath, [Out] chmUnitInfo ui);
public static chmFile Open(string filename) { byte[] sbuffer = new byte[256]; uint sremain; uint sbufpos; chmItsfHeader itsfHeader = new chmItsfHeader(); chmItspHeader itspHeader = new chmItspHeader(); chmUnitInfo uiLzxc = new chmUnitInfo(); chmLzxcControlData ctlData = new chmLzxcControlData(); chmFile chmf = new chmFile(filename); /* allocate handle */ chmf._h.fd = null; chmf._h.lzx_state = null; chmf._h.cache_blocks = null; chmf._h.cache_block_indices = null; chmf._h.cache_num_blocks = 0; /* open file */ chmf._h.fd = File.Open(filename, FileMode.Open, FileAccess.Read); /* initialize mutexes, if needed */ chmf._h.mutex = new Mutex(); chmf._h.lzx_mutex = new Mutex(); chmf._h.cache_mutex = new Mutex(); /* read and verify header */ sremain = Itsf.CHM_ITSF_V3_LEN; sbufpos = 0; if (Storage.FetchBytes(ref chmf._h, ref sbuffer, 0, sremain) != sremain || !Itsf.UnmarshalItsfHeader(ref sbuffer, ref sbufpos, ref sremain, ref itsfHeader)) { chmf.Close(); throw new InvalidDataException(); } /* stash important values from header */ chmf._h.dir_offset = itsfHeader.dir_offset; chmf._h.dir_len = itsfHeader.dir_len; chmf._h.data_offset = itsfHeader.data_offset; /* now, read and verify the directory header chunk */ sremain = Itsp.CHM_ITSP_V1_LEN; sbufpos = 0; if (Storage.FetchBytes(ref chmf._h, ref sbuffer, (UInt64)itsfHeader.dir_offset, sremain) != sremain || !Itsp.UnmarshalItpfHeader(ref sbuffer, ref sbufpos, ref sremain, ref itspHeader)) { chmf.Close(); throw new InvalidDataException(); } /* grab essential information from ITSP header */ chmf._h.dir_offset += (ulong)itspHeader.header_len; chmf._h.dir_len -= (ulong)itspHeader.header_len; chmf._h.index_root = itspHeader.index_root; chmf._h.index_head = itspHeader.index_head; chmf._h.block_len = itspHeader.block_len; /* if the index root is -1, this means we don't have any PMGI blocks. * as a result, we must use the sole PMGL block as the index root */ if (chmf._h.index_root <= -1) chmf._h.index_root = chmf._h.index_head; /* By default, compression is enabled. */ chmf._h.compression_enabled = true; /* prefetch most commonly needed unit infos */ if (!chmf.ResolveObject(Storage.CHMU_RESET_TABLE, ref chmf._h.rt_unit) || chmf._h.rt_unit.space == Storage.CHM_COMPRESSED || !chmf.ResolveObject(Storage.CHMU_CONTENT, ref chmf._h.cn_unit) || chmf._h.cn_unit.space == Storage.CHM_COMPRESSED || !chmf.ResolveObject(Storage.CHMU_LZXC_CONTROLDATA, ref uiLzxc) || uiLzxc.space == Storage.CHM_COMPRESSED) { chmf._h.compression_enabled = false; } /* read reset table info */ if (chmf._h.compression_enabled) { sremain = Lzxc.CHM_LZXC_RESETTABLE_V1_LEN; sbufpos = 0; /* TODO bobc: is sbuffer actually at index 0?? */ if (chmf.RetrieveObject(chmf._h.rt_unit, ref sbuffer, 0, sremain) != sremain || !Lzxc.UnmarshalLzxcResetTable(ref sbuffer, ref sbufpos, ref sremain, ref chmf._h.reset_table)) { chmf._h.compression_enabled = false; } } /* read control data */ if (chmf._h.compression_enabled) { sremain = (uint)uiLzxc.length; if (uiLzxc.length > (ulong)sbuffer.Length) { chmf.Close(); throw new InvalidDataException(); } sbufpos = 0; /* TODO bobc: is sbuffer actually at index 0?? */ if (chmf.RetrieveObject(uiLzxc, ref sbuffer, 0, sremain) != sremain || !Lzxc.UnmarshalLzxcControlData(ref sbuffer, ref sbufpos, ref sremain, ref ctlData)) { chmf._h.compression_enabled = false; } chmf._h.window_size = ctlData.windowSize; chmf._h.reset_interval = ctlData.resetInterval; /* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */ /* must be multiplied by this formerly unknown ctrl data field in */ /* order to decompress some files. */ chmf._h.reset_blkcount = chmf._h.reset_interval / (chmf._h.window_size / 2) * ctlData.windowsPerReset; } /* initialize cache */ chmf.SetParam(CHM_PARAM_MAX_BLOCKS_CACHED, CHM_MAX_BLOCKS_CACHED); return chmf; }
/* resolve a particular object from the archive */ public bool ResolveObject(string objPath, ref chmUnitInfo ui) { /* * XXX: implement caching scheme for dir pages */ Int32 curPage; /* buffer to hold whatever page we're looking at */ /* RWE 6/12/2003 */ byte[] page_buf = new byte[_h.block_len]; /* starting page */ curPage = _h.index_root; /* until we have either returned or given up */ while (curPage != -1) { /* try to fetch the index page */ if (Storage.FetchBytes(ref _h, ref page_buf, (UInt64)_h.dir_offset + (UInt64)curPage * _h.block_len, _h.block_len) != _h.block_len) return false; /* now, if it is a leaf node: */ if (ASCIIEncoding.UTF8.GetString(page_buf, 0, 4).CompareTo(Pmgl.CHM_PMGL_MARKER) == 0) { /* scan block */ uint pEntry = (uint)Pmgl.FindInPmgl(page_buf, _h.block_len, objPath); if (pEntry < 0) return false; /* parse entry and return */ Pmgl.ParsePgmlEntry(page_buf, ref pEntry, ref ui); return true; } /* else, if it is a branch node: */ else if (ASCIIEncoding.UTF8.GetString(page_buf, 0, 4).CompareTo(Pmgi.CHM_PMGI_MARKER) == 0) curPage = Pmgi.FindInPmgi(page_buf, _h.block_len, objPath); /* else, we are confused. give up. */ else return false; } /* didn't find anything. fail. */ return false; }
/* enumerate the objects in the .chm archive */ public bool Enumerate(EnumerateLevel what, chmEnumerator e, ref object context) { Int32 curPage; /* buffer to hold whatever page we're looking at */ /* RWE 6/12/2003 */ byte[] page_buf = new byte[_h.block_len]; chmPmglHeader header = new chmPmglHeader(); uint end; uint cur; uint lenRemain; UInt64 ui_path_len; /* the current ui */ chmUnitInfo ui = new chmUnitInfo(); int type_bits = ((int)what & 0x7); int filter_bits = ((int)what & 0xF8); /* starting page */ curPage = _h.index_head; /* until we have either returned or given up */ while (curPage != -1) { /* try to fetch the index page */ if (Storage.FetchBytes(ref _h, ref page_buf, (UInt64)_h.dir_offset + (UInt64)curPage * _h.block_len, _h.block_len) != _h.block_len) return false; /* figure out start and end for this page */ cur = 0; lenRemain = Pmgl.CHM_PMGL_LEN; if (!Pmgl.UnmarshalPmglHeader(ref page_buf, ref cur, ref lenRemain, ref header)) return false; end = _h.block_len - (header.free_space); /* loop over this page */ while (cur < end) { ui.flags = 0; if (!Pmgl.ParsePgmlEntry(page_buf, ref cur, ref ui)) return false; /* get the length of the path */ ui_path_len = (ulong)ui.path.Length; /* check for DIRS */ if (ui.path.EndsWith("/")) ui.flags |= (int)EnumerateLevel.Directories; /* check for FILES */ if (!ui.path.EndsWith("/")) ui.flags |= (int)EnumerateLevel.Files; /* check for NORMAL vs. META */ if (ui.path.StartsWith("/")) { /* check for NORMAL vs. SPECIAL */ if (ui.path.Length > 1 && (ui.path[1] == '#' || ui.path[1] == '$')) ui.flags |= (int)EnumerateLevel.Special; else ui.flags |= (int)EnumerateLevel.Normal; } else ui.flags |= (int)EnumerateLevel.Meta; if (!Convert.ToBoolean(type_bits & ui.flags)) continue; if (filter_bits != 0 && (filter_bits & ui.flags) == 0) continue; /* call the enumerator */ EnumerateStatus status = e(this, ref ui, ref context); switch (status) { case EnumerateStatus.Failure: return false; case EnumerateStatus.Continue: break; case EnumerateStatus.Success: return true; default: break; } } /* advance to next page */ curPage = header.block_next; } return true; }
public void ParseContents(ChmHtmlParseFunc Parse) { if(this.loaded) foreach(string fileName in htmlFiles) { chmUnitInfo ui = new chmUnitInfo(); chm_resolve_object(this.chmfile, fileName, ui) ; //Console.WriteLine("Parsing....{0}",ui.path); ///Logger.Log.Debug("CHMFile: Parsing {0}....",ui.path); Parse( new StringReader(ChmFileToString(ui).Trim()) ); } }
private bool WindowsInfo() { int entries; int entrySize; IntPtr windowsData; long size = 0; uint block; const int headerLen = 0x8; IntPtr buf = Marshal.AllocCoTaskMem (bufSize); chmUnitInfo ui = new chmUnitInfo (); if(chm_resolve_object (chmfile,"/#WINDOWS",ui) == ChmResolve.Failure) return false; if(chm_retrieve_object (chmfile,ui,buf,0,headerLen) == 0) return false; entries = Marshal.ReadInt32 (buf); entrySize = Marshal.ReadInt32 (buf,0x4); //Console.WriteLine ("entries -> {0}\nsize = {1}",entries,entrySize); windowsData = Marshal.AllocCoTaskMem(entries * entrySize); size = (long)chm_retrieve_object (chmfile, ui, windowsData, headerLen, (ulong)(entries * entrySize)); if(size == 0) return false; size = 0; if(chm_resolve_object (chmfile,"/#STRINGS",ui) == ChmResolve.Failure) return false; /* From Pabs' CHM Spec: "(STRINGS)This file is a list of ANSI/UTF-8 NT strings. The first is just a NIL character so that offsets to this file can specify zero & get a valid string. The strings are sliced up into blocks that are 4096 bytes in length." */ for(int i = 0; i < entries; i++) { int offset = i * entrySize; uint offTitle = (uint)Marshal.ReadInt32(windowsData, offset + 0x14); uint offTocFile = (uint)Marshal.ReadInt32(windowsData, offset + 0x60); uint offDefaultFile = (uint)Marshal.ReadInt32(windowsData, offset + 0x68); //Console.WriteLine("offTocFile = {0}",offTocFile); block = offTitle / 4096; if(size == 0) size = (long)chm_retrieve_object(chmfile, ui, buf, block * 4096, (ulong)bufSize); if(size > 0 && offTitle > 0) this.title = ChmGetString(buf,(int)offTitle,4096); if(block != offTocFile / 4096) { block = offTocFile / 4096; size = (long)chm_retrieve_object(chmfile, ui, buf, block * 4096, (ulong)bufSize); } if(size > 0 && offTocFile > 0){ topicsFile = "/" + ChmGetString(buf, (int)offTocFile % 4096 , 4096); hasTopics = true; } if(block != offDefaultFile / 4096) { block = offDefaultFile / 4096; size = (long)chm_retrieve_object(chmfile, ui, buf, block * 4096, (ulong)bufSize); } if(size > 0 && offDefaultFile > 0) defaultFile = ("/" + ChmGetString(buf, (int)offDefaultFile % 4096 , 4096) ); } Marshal.FreeCoTaskMem (buf); Marshal.FreeCoTaskMem (windowsData); return true; }
/* TODO: We should trow something like a FileNotFoundException */ public TextReader GetFile(string path) { chmUnitInfo ui = new chmUnitInfo(); if(chm_resolve_object (chmfile,path,ui) == ChmResolve.Failure) { //Console.WriteLine("Fails to Open: {0}",path); return new StringReader(""); } return (new StringReader(ChmFileToString(ui))); }