Example #1
0
        public static bool UnmarshalItsfHeader(ref byte[] pData, ref uint pDataPos, 
            ref uint pDataLen, ref chmItsfHeader dest)
        {
            /* we only know how to deal with the 0x58 and 0x60 byte structures */
            if (pDataLen != CHM_ITSF_V2_LEN && pDataLen != CHM_ITSF_V3_LEN)
                return false;

            InitialiseItsfHeader(ref dest);

            /* unmarshal common fields */
            Unmarshal.ToCharArray(ref pData, ref pDataPos, ref pDataLen, ref dest.signature, 4);
            Unmarshal.ToInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.version);
            Unmarshal.ToInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.header_len);
            Unmarshal.ToInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.unknown_000c);
            Unmarshal.ToUInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.last_modified);
            Unmarshal.ToUInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.lang_id);
            Unmarshal.ToUuid(ref pData, ref pDataPos, ref pDataLen, ref dest.dir_uuid);
            Unmarshal.ToUuid(ref pData, ref pDataPos, ref pDataLen, ref dest.stream_uuid);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.unknown_offset);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.unknown_len);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.dir_offset);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.dir_len);

            /* error check the data */
            /* XXX: should also check UUIDs, probably, though with a version 3 file,
             * current MS tools do not seem to use them.
             */
            if (new String(dest.signature).CompareTo("ITSF") != 0)
                return false;
            if (dest.version == 2) {
                if (dest.header_len < CHM_ITSF_V2_LEN)
                    return false;
            } else if (dest.version == 3) {
                if (dest.header_len < CHM_ITSF_V3_LEN)
                    return false;
            } else
                return false;

            /* now, if we have a V3 structure, unmarshal the rest.
             * otherwise, compute it
             */
            if (dest.version == 3) {
                if (pDataLen != 0)
                    Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.data_offset);
                else
                    return false;
            } else
                dest.data_offset = dest.dir_offset + dest.dir_len;

            return true;
        }
Example #2
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 #3
0
 public static void InitialiseItsfHeader(ref chmItsfHeader h)
 {
     h.signature = new char[4];
     h.dir_uuid = new byte[16];
     h.stream_uuid = new byte[16];
 }
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
        public static bool UnmarshalItsfHeader(ref byte[] pData, ref uint pDataPos,
                                               ref uint pDataLen, ref chmItsfHeader dest)
        {
            /* we only know how to deal with the 0x58 and 0x60 byte structures */
            if (pDataLen != CHM_ITSF_V2_LEN && pDataLen != CHM_ITSF_V3_LEN)
            {
                return(false);
            }

            InitialiseItsfHeader(ref dest);

            /* unmarshal common fields */
            Unmarshal.ToCharArray(ref pData, ref pDataPos, ref pDataLen, ref dest.signature, 4);
            Unmarshal.ToInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.version);
            Unmarshal.ToInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.header_len);
            Unmarshal.ToInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.unknown_000c);
            Unmarshal.ToUInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.last_modified);
            Unmarshal.ToUInt32(ref pData, ref pDataPos, ref pDataLen, ref dest.lang_id);
            Unmarshal.ToUuid(ref pData, ref pDataPos, ref pDataLen, ref dest.dir_uuid);
            Unmarshal.ToUuid(ref pData, ref pDataPos, ref pDataLen, ref dest.stream_uuid);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.unknown_offset);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.unknown_len);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.dir_offset);
            Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.dir_len);

            /* error check the data */

            /* XXX: should also check UUIDs, probably, though with a version 3 file,
             * current MS tools do not seem to use them.
             */
            if (new String(dest.signature).CompareTo("ITSF") != 0)
            {
                return(false);
            }
            if (dest.version == 2)
            {
                if (dest.header_len < CHM_ITSF_V2_LEN)
                {
                    return(false);
                }
            }
            else if (dest.version == 3)
            {
                if (dest.header_len < CHM_ITSF_V3_LEN)
                {
                    return(false);
                }
            }
            else
            {
                return(false);
            }

            /* now, if we have a V3 structure, unmarshal the rest.
             * otherwise, compute it
             */
            if (dest.version == 3)
            {
                if (pDataLen != 0)
                {
                    Unmarshal.ToUInt64(ref pData, ref pDataPos, ref pDataLen, ref dest.data_offset);
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                dest.data_offset = dest.dir_offset + dest.dir_len;
            }

            return(true);
        }
Example #6
0
 public static void InitialiseItsfHeader(ref chmItsfHeader h)
 {
     h.signature   = new char[4];
     h.dir_uuid    = new byte[16];
     h.stream_uuid = new byte[16];
 }