Example #1
0
        static EnumerateStatus EnumeratorCallback(ChmFile file, ChmUnitInfo ui, 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;
        }
Example #2
0
        static EnumerateStatus EnumeratorCallback(ChmFile file, ChmUnitInfo ui, 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 static EnumerateStatus ArticleEnumeratorCallback(ChmFile file, ChmUnitInfo unitInfo, EnumeratorContext context)
        {
            if (string.Equals(unitInfo.path.TrimStart('/'), "index.html", StringComparison.OrdinalIgnoreCase))
            {
                context.IsGoalReached = true;
                return(EnumerateStatus.Success);
            }

            return(EnumerateStatus.Continue);
        }
Example #4
0
        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;
        }
Example #5
0
        /* 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;

                } while (len != 0);

                return total;
            }
        }
Example #6
0
        /* 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 */
                    long pEntry = Pmgl.FindInPmgl(page_buf, _h.block_len, objPath);
                    if (pEntry < 0)
                        return false;

                    /* parse entry and return */
                    uint os = (uint)pEntry;
                    Pmgl.ParsePgmlEntry(page_buf, ref os, 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;
        }
Example #7
0
        /* enumerate the objects in the .chm archive */
        public bool Enumerate(EnumerateLevel what, ChmEnumerator e, 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, ui, 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;
        }
Example #8
0
        /* 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;
        }
Example #9
0
        public static SystemFile Read(ChmFile f)
        {
            List<SystemFileEntry> sfelst = new List<SystemFileEntry>();
            ChmUnitInfo ui = new ChmUnitInfo();
            byte[] buf;
            uint pos = 0, remaining = 0;

            if (!f.ResolveObject("/#SYSTEM", ref ui))
                throw new InvalidOperationException("Could not find SYSTEM file in CHM!");

            buf = new byte[ui.length];
            remaining = (uint)buf.Length;

            if (f.RetrieveObject(ui, ref buf, 0, (long)ui.length) == 0)
                throw new InvalidOperationException("Could not read SYSTEM file in CHM!");

            Int32 version = 0;
            Unmarshal.ToInt32(ref buf, ref pos, ref remaining, ref version);

            for ( ; pos < buf.Length; ) {
                SystemFileEntry sfe = new SystemFileEntry();

                Unmarshal.ToUInt16(ref buf, ref pos, ref remaining, ref sfe.code);
                Unmarshal.ToUInt16(ref buf, ref pos, ref remaining, ref sfe.length);

                sfe.data = new byte[sfe.length];
                Array.Copy(buf, pos, sfe.data, 0, sfe.length);

                pos += sfe.length;
                remaining -= sfe.length;

                sfelst.Add(sfe);
            }

            return new SystemFile(version, sfelst.ToArray());
        }