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; }
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; }
public static void InitialiseItsfHeader(ref chmItsfHeader h) { h.signature = new char[4]; h.dir_uuid = new byte[16]; h.stream_uuid = new byte[16]; }
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); }
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); }