📄 chm_lib.c
字号:
CHM_MAX_BLOCKS_CACHED); return newHandle;}/* close an ITS archive */void chm_close(struct chmFile *h){ if (h != NULL) { if (h->fd != CHM_NULL_FD) CHM_CLOSE_FILE(h->fd); h->fd = CHM_NULL_FD;#ifdef CHM_MT#ifdef WIN32 DeleteCriticalSection(&h->mutex); DeleteCriticalSection(&h->lzx_mutex); DeleteCriticalSection(&h->cache_mutex);#else pthread_mutex_destroy(&h->mutex); pthread_mutex_destroy(&h->lzx_mutex); pthread_mutex_destroy(&h->cache_mutex);#endif#endif if (h->lzx_state) LZXteardown(h->lzx_state); h->lzx_state = NULL; if (h->cache_blocks) { int i; for (i=0; i<h->cache_num_blocks; i++) { if (h->cache_blocks[i]) free(h->cache_blocks[i]); } free(h->cache_blocks); h->cache_blocks = NULL; } if (h->cache_block_indices) free(h->cache_block_indices); h->cache_block_indices = NULL; free(h); }}/* * set a parameter on the file handle. * valid parameter types: * CHM_PARAM_MAX_BLOCKS_CACHED: * how many decompressed blocks should be cached? A simple * caching scheme is used, wherein the index of the block is * used as a hash value, and hash collision results in the * invalidation of the previously cached block. */void chm_set_param(struct chmFile *h, int paramType, int paramVal){ switch (paramType) { case CHM_PARAM_MAX_BLOCKS_CACHED: CHM_ACQUIRE_LOCK(h->cache_mutex); if (paramVal != h->cache_num_blocks) { UChar **newBlocks; UInt64 *newIndices; int i; /* allocate new cached blocks */ newBlocks = (UChar **)malloc(paramVal * sizeof (UChar *)); newIndices = (UInt64 *)malloc(paramVal * sizeof (UInt64)); for (i=0; i<paramVal; i++) { newBlocks[i] = NULL; newIndices[i] = 0; } /* re-distribute old cached blocks */ if (h->cache_blocks) { for (i=0; i<h->cache_num_blocks; i++) { int newSlot = (int)(h->cache_block_indices[i] % paramVal); if (h->cache_blocks[i]) { /* in case of collision, destroy newcomer */ if (newBlocks[newSlot]) { free(h->cache_blocks[i]); h->cache_blocks[i] = NULL; } else { newBlocks[newSlot] = h->cache_blocks[i]; newIndices[newSlot] = h->cache_block_indices[i]; } } } free(h->cache_blocks); free(h->cache_block_indices); } /* now, set new values */ h->cache_blocks = newBlocks; h->cache_block_indices = newIndices; h->cache_num_blocks = paramVal; } CHM_RELEASE_LOCK(h->cache_mutex); break; default: break; }}/* * helper methods for chm_resolve_object *//* skip a compressed dword */static void _chm_skip_cword(UChar **pEntry){ while (*(*pEntry)++ >= 0x80) ;}/* skip the data from a PMGL entry */static void _chm_skip_PMGL_entry_data(UChar **pEntry){ _chm_skip_cword(pEntry); _chm_skip_cword(pEntry); _chm_skip_cword(pEntry);}/* parse a compressed dword */static UInt64 _chm_parse_cword(UChar **pEntry){ UInt64 accum = 0; UChar temp; while ((temp=*(*pEntry)++) >= 0x80) { accum <<= 7; accum += temp & 0x7f; } return (accum << 7) + temp;}/* parse a utf-8 string into an ASCII char buffer */static int _chm_parse_UTF8(UChar **pEntry, UInt64 count, char *path){ /* XXX: implement UTF-8 support, including a real mapping onto * ISO-8859-1? probably there is a library to do this? As is * immediately apparent from the below code, I'm presently not doing * any special handling for files in which none of the strings contain * UTF-8 multi-byte characters. */ while (count != 0) { *path++ = (char)(*(*pEntry)++); --count; } *path = '\0'; return 1;}/* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */static int _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui){ UInt64 strLen; /* parse str len */ strLen = _chm_parse_cword(pEntry); if (strLen > CHM_MAX_PATHLEN) return 0; /* parse path */ if (! _chm_parse_UTF8(pEntry, strLen, ui->path)) return 0; /* parse info */ ui->space = (int)_chm_parse_cword(pEntry); ui->start = _chm_parse_cword(pEntry); ui->length = _chm_parse_cword(pEntry); return 1;}/* find an exact entry in PMGL; return NULL if we fail */static UChar *_chm_find_in_PMGL(UChar *page_buf, UInt32 block_len, const char *objPath){ /* XXX: modify this to do a binary search using the nice index structure * that is provided for us. */ struct chmPmglHeader header; UInt32 hremain; UChar *end; UChar *cur; UChar *temp; UInt64 strLen; char buffer[CHM_MAX_PATHLEN+1]; /* figure out where to start and end */ cur = page_buf; hremain = _CHM_PMGL_LEN; if (! _unmarshal_pmgl_header(&cur, &hremain, &header)) return NULL; end = page_buf + block_len - (header.free_space); /* now, scan progressively */ while (cur < end) { /* grab the name */ temp = cur; strLen = _chm_parse_cword(&cur); if (! _chm_parse_UTF8(&cur, strLen, buffer)) return NULL; /* check if it is the right name */ if (! strcasecmp(buffer, objPath)) return temp; _chm_skip_PMGL_entry_data(&cur); } return NULL;}/* find which block should be searched next for the entry; -1 if no block */static Int32 _chm_find_in_PMGI(UChar *page_buf, UInt32 block_len, const char *objPath){ /* XXX: modify this to do a binary search using the nice index structure * that is provided for us */ struct chmPmgiHeader header; UInt32 hremain; int page=-1; UChar *end; UChar *cur; UInt64 strLen; char buffer[CHM_MAX_PATHLEN+1]; /* figure out where to start and end */ cur = page_buf; hremain = _CHM_PMGI_LEN; if (! _unmarshal_pmgi_header(&cur, &hremain, &header)) return -1; end = page_buf + block_len - (header.free_space); /* now, scan progressively */ while (cur < end) { /* grab the name */ strLen = _chm_parse_cword(&cur); if (! _chm_parse_UTF8(&cur, strLen, buffer)) return -1; /* check if it is the right name */ if (strcasecmp(buffer, objPath) > 0) return page; /* load next value for path */ page = (int)_chm_parse_cword(&cur); } return page;}/* resolve a particular object from the archive */int chm_resolve_object(struct chmFile *h, const char *objPath, struct chmUnitInfo *ui){ /* * XXX: implement caching scheme for dir pages */ Int32 curPage; /* buffer to hold whatever page we're looking at */#ifdef WIN32#ifdef _WIN32_WCE /* RWE 6/12/2003 */ UChar *page_buf = malloc(h->block_len);#else UChar *page_buf = alloca(h->block_len);#endif#else UChar page_buf[h->block_len];#endif /* starting page */ curPage = h->index_root; /* until we have either returned or given up */ while (curPage != -1) { /* try to fetch the index page */ if (_chm_fetch_bytes(h, page_buf, (UInt64)h->dir_offset + (UInt64)curPage*h->block_len, h->block_len) != h->block_len) { FREEBUF(page_buf); return CHM_RESOLVE_FAILURE; } /* now, if it is a leaf node: */ if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0) { /* scan block */ UChar *pEntry = _chm_find_in_PMGL(page_buf, h->block_len, objPath); if (pEntry == NULL) { FREEBUF(page_buf); return CHM_RESOLVE_FAILURE; } /* parse entry and return */ _chm_parse_PMGL_entry(&pEntry, ui); FREEBUF(page_buf); return CHM_RESOLVE_SUCCESS; } /* else, if it is a branch node: */ else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0) curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath); /* else, we are confused. give up. */ else { FREEBUF(page_buf); return CHM_RESOLVE_FAILURE; } } /* didn't find anything. fail. */ FREEBUF(page_buf); return CHM_RESOLVE_FAILURE;}/* * utility methods for dealing with compressed data *//* get the bounds of a compressed block. return 0 on failure */static int _chm_get_cmpblock_bounds(struct chmFile *h, UInt64 block, UInt64 *start, Int64 *len){ UChar buffer[8], *dummy; UInt32 remain; /* for all but the last block, use the reset table */ if (block < h->reset_table.block_count-1) { /* unpack the start address */ dummy = buffer; remain = 8; if (_chm_fetch_bytes(h, buffer, (UInt64)h->data_offset + (UInt64)h->rt_unit.start + (UInt64)h->reset_table.table_offset + (UInt64)block*8, remain) != remain || !_unmarshal_uint64(&dummy, &remain, start)) return 0; /* unpack the end address */ dummy = buffer; remain = 8; if (_chm_fetch_bytes(h, buffer, (UInt64)h->data_offset + (UInt64)h->rt_unit.start + (UInt64)h->reset_table.table_offset + (UInt64)block*8 + 8, remain) != remain || !_unmarshal_int64(&dummy, &remain, len)) return 0; } /* for the last block, use the span in addition to the reset table */ else { /* unpack the start address */ dummy = buffer; remain = 8; if (_chm_fetch_bytes(h, buffer, (UInt64)h->data_offset + (UInt64)h->rt_unit.start + (UInt64)h->reset_table.table_offset + (UInt64)block*8, remain) != remain || !_unmarshal_uint64(&dummy, &remain, start)) return 0; *len = h->reset_table.compressed_len; } /* compute the length and absolute start address */ *len -= *start; *start += h->data_offset + h->cn_unit.start; return 1;}/* decompress the block. must have lzx_mutex. */static Int64 _chm_decompress_block(struct chmFile *h, UInt64 block, UChar **ubuffer){#ifdef WIN32#ifdef _WIN32_WCE /* RWE 6/12/2003 */ UChar *cbuffer = malloc(((unsigned int)h->reset_table.block_len + 6144));#else UChar *cbuffer = alloca(((unsigned int)h->reset_table.block_len + 6144));#endif#else UChar cbuffer[h->reset_table.block_len + 6144]; /* compressed buffer */#endif UInt64 cmpStart; /* compressed start */ Int64 cmpLen; /* compressed len */ int indexSlot; /* cache index slot */ UChar *lbuffer; /* local buffer ptr */ UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset intvl. aln. */ UInt32 i; /* local loop index */ /* let the caching system pull its weight! */ if (block - blockAlign <= h->lzx_last_block && block >= h->lzx_last_block) blockAlign = (block - h->lzx_last_block); /* check if we need previous blocks */ if (blockAlign != 0) { /* fetch all required previous blocks since last reset */ for (i = blockAlign; i > 0; i--) { UInt32 curBlockIdx = block - i; /* check if we most recently decompressed the previous block */ if (h->lzx_last_block != curBlockIdx) { if ((curBlockIdx % h->reset_blkcount) == 0) {#ifdef CHM_DEBUG fprintf(stderr, "***RESET (1)***\n");#endif LZXreset(h->lzx_state); } indexSlot = (int)((curBlockIdx) % h->cache_num_blocks); h->cache_block_indices[indexSlot] = curBlockIdx; if (! h->cache_blocks[indexSlot]) h->cache_blocks[indexSlot] = (UChar *)malloc( (unsigned int)(h->reset_table.block_len)); lbuffer = h->cache_blocks[indexSlot];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -