/* 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); } }
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); }