📄 chm_lib.c
字号:
#define _CHM_PMGL_LEN (0x14)struct chmPmglHeader{ char signature[4]; /* 0 (PMGL) */ UInt32 free_space; /* 4 */ UInt32 unknown_0008; /* 8 */ Int32 block_prev; /* c */ Int32 block_next; /* 10 */}; /* __attribute__ ((aligned (1))); */static int _unmarshal_pmgl_header(unsigned char **pData, unsigned long *pDataLen, struct chmPmglHeader *dest){ /* we only know how to deal with a 0x14 byte structures */ if (*pDataLen != _CHM_PMGL_LEN) return 0; /* unmarshal fields */ _unmarshal_char_array(pData, pDataLen, dest->signature, 4); _unmarshal_uint32 (pData, pDataLen, &dest->free_space); _unmarshal_uint32 (pData, pDataLen, &dest->unknown_0008); _unmarshal_int32 (pData, pDataLen, &dest->block_prev); _unmarshal_int32 (pData, pDataLen, &dest->block_next); /* check structure */ if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0) return 0; return 1;}/* structure of PMGI headers */static const char _chm_pmgi_marker[4] = "PMGI";#define _CHM_PMGI_LEN (0x08)struct chmPmgiHeader{ char signature[4]; /* 0 (PMGI) */ UInt32 free_space; /* 4 */}; /* __attribute__ ((aligned (1))); */static int _unmarshal_pmgi_header(unsigned char **pData, unsigned long *pDataLen, struct chmPmgiHeader *dest){ /* we only know how to deal with a 0x8 byte structures */ if (*pDataLen != _CHM_PMGI_LEN) return 0; /* unmarshal fields */ _unmarshal_char_array(pData, pDataLen, dest->signature, 4); _unmarshal_uint32 (pData, pDataLen, &dest->free_space); /* check structure */ if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0) return 0; return 1;}/* structure of LZXC reset table */#define _CHM_LZXC_RESETTABLE_V1_LEN (0x28)struct chmLzxcResetTable{ UInt32 version; UInt32 block_count; UInt32 unknown; UInt32 table_offset; UInt64 uncompressed_len; UInt64 compressed_len; UInt64 block_len; }; /* __attribute__ ((aligned (1))); */static int _unmarshal_lzxc_reset_table(unsigned char **pData, unsigned long *pDataLen, struct chmLzxcResetTable *dest){ /* we only know how to deal with a 0x28 byte structures */ if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN) return 0; /* unmarshal fields */ _unmarshal_uint32 (pData, pDataLen, &dest->version); _unmarshal_uint32 (pData, pDataLen, &dest->block_count); _unmarshal_uint32 (pData, pDataLen, &dest->unknown); _unmarshal_uint32 (pData, pDataLen, &dest->table_offset); _unmarshal_uint64 (pData, pDataLen, &dest->uncompressed_len); _unmarshal_uint64 (pData, pDataLen, &dest->compressed_len); _unmarshal_uint64 (pData, pDataLen, &dest->block_len); /* check structure */ if (dest->version != 2) return 0; return 1;}/* structure of LZXC control data block */#define _CHM_LZXC_MIN_LEN (0x18)#define _CHM_LZXC_V2_LEN (0x1c)struct chmLzxcControlData{ UInt32 size; /* 0 */ char signature[4]; /* 4 (LZXC) */ UInt32 version; /* 8 */ UInt32 resetInterval; /* c */ UInt32 windowSize; /* 10 */ UInt32 windowsPerReset; /* 14 */ UInt32 unknown_18; /* 18 */};static int _unmarshal_lzxc_control_data(unsigned char **pData, unsigned long *pDataLen, struct chmLzxcControlData *dest){ /* we want at least 0x18 bytes */ if (*pDataLen < _CHM_LZXC_MIN_LEN) return 0; /* unmarshal fields */ _unmarshal_uint32 (pData, pDataLen, &dest->size); _unmarshal_char_array(pData, pDataLen, dest->signature, 4); _unmarshal_uint32 (pData, pDataLen, &dest->version); _unmarshal_uint32 (pData, pDataLen, &dest->resetInterval); _unmarshal_uint32 (pData, pDataLen, &dest->windowSize); _unmarshal_uint32 (pData, pDataLen, &dest->windowsPerReset); if (*pDataLen >= _CHM_LZXC_V2_LEN) _unmarshal_uint32 (pData, pDataLen, &dest->unknown_18); else dest->unknown_18 = 0; if (dest->version == 2) { dest->resetInterval *= 0x8000; dest->windowSize *= 0x8000; } if (dest->windowSize == 0 || dest->resetInterval == 0) return 0; /* for now, only support resetInterval a multiple of windowSize/2 */ if (dest->windowSize == 1) return 0; if ((dest->resetInterval % (dest->windowSize/2)) != 0) return 0; /* check structure */ if (memcmp(dest->signature, "LZXC", 4) != 0) return 0; return 1;}/* the structure used for chm file handles */struct chmFile{#ifdef WIN32 HANDLE fd;#else int fd;#endif#ifdef CHM_MT#ifdef WIN32 CRITICAL_SECTION mutex; CRITICAL_SECTION lzx_mutex; CRITICAL_SECTION cache_mutex;#else pthread_mutex_t mutex; pthread_mutex_t lzx_mutex; pthread_mutex_t cache_mutex;#endif#endif UInt64 dir_offset; UInt64 dir_len; UInt64 data_offset; Int32 index_root; Int32 index_head; UInt32 block_len; UInt64 span; struct chmUnitInfo rt_unit; struct chmUnitInfo cn_unit; struct chmLzxcResetTable reset_table; /* LZX control data */ int compression_enabled; UInt32 window_size; UInt32 reset_interval; UInt32 reset_blkcount; /* decompressor state */ struct LZXstate *lzx_state; int lzx_last_block; /* cache for decompressed blocks */ UChar **cache_blocks; Int64 *cache_block_indices; Int32 cache_num_blocks;};/* * utility functions local to this module *//* utility function to handle differences between {pread,read}(64)? */static Int64 _chm_fetch_bytes(struct chmFile *h, UChar *buf, UInt64 os, Int64 len){ Int64 readLen=0, oldOs=0; if (h->fd == CHM_NULL_FD) return readLen; CHM_ACQUIRE_LOCK(h->mutex);#ifdef CHM_USE_WIN32IO /* NOTE: this might be better done with CreateFileMapping, et cetera... */ { DWORD origOffsetLo=0, origOffsetHi=0; DWORD offsetLo, offsetHi; DWORD actualLen=0; /* awkward Win32 Seek/Tell */ offsetLo = (unsigned long)(os & 0xffffffffL); offsetHi = (unsigned long)((os >> 32) & 0xffffffffL); origOffsetLo = SetFilePointer(h->fd, 0, &origOffsetHi, FILE_CURRENT); offsetLo = SetFilePointer(h->fd, offsetLo, &offsetHi, FILE_BEGIN); /* read the data */ if (ReadFile(h->fd, buf, (DWORD)len, &actualLen, NULL) == TRUE) readLen = actualLen; else readLen = 0; /* restore original position */ SetFilePointer(h->fd, origOffsetLo, &origOffsetHi, FILE_BEGIN); }#else#ifdef CHM_USE_PREAD#ifdef CHM_USE_IO64 readLen = pread64(h->fd, buf, (long)len, os);#else readLen = pread(h->fd, buf, (long)len, (unsigned long)os);#endif#else#ifdef CHM_USE_IO64 oldOs = lseek64(h->fd, 0, SEEK_CUR); lseek64(h->fd, os, SEEK_SET); readLen = read(h->fd, buf, len); lseek64(h->fd, oldOs, SEEK_SET);#else oldOs = lseek(h->fd, 0, SEEK_CUR); lseek(h->fd, (long)os, SEEK_SET); readLen = read(h->fd, buf, len); lseek(h->fd, (long)oldOs, SEEK_SET);#endif#endif#endif CHM_RELEASE_LOCK(h->mutex); return readLen;}/* open an ITS archive */#ifdef PPC_BSTR/* RWE 6/12/2003 */struct chmFile *chm_open(BSTR filename)#elsestruct chmFile *chm_open(const char *filename)#endif{ unsigned char sbuffer[256]; unsigned long sremain; unsigned char *sbufpos; struct chmFile *newHandle=NULL; struct chmItsfHeader itsfHeader; struct chmItspHeader itspHeader; struct chmUnitInfo uiSpan; struct chmUnitInfo uiLzxc; struct chmLzxcControlData ctlData; /* allocate handle */ newHandle = (struct chmFile *)malloc(sizeof(struct chmFile)); newHandle->fd = CHM_NULL_FD; newHandle->lzx_state = NULL; newHandle->cache_blocks = NULL; newHandle->cache_block_indices = NULL; newHandle->cache_num_blocks = 0; /* open file */#ifdef WIN32#ifdef PPC_BSTR if ((newHandle->fd=CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == CHM_NULL_FD) { free(newHandle); return NULL; }#else if ((newHandle->fd=CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == CHM_NULL_FD) { free(newHandle); return NULL; }#endif#else if ((newHandle->fd=open(filename, O_RDONLY)) == CHM_NULL_FD) { free(newHandle); return NULL; }#endif /* initialize mutexes, if needed */#ifdef CHM_MT#ifdef WIN32 InitializeCriticalSection(&newHandle->mutex); InitializeCriticalSection(&newHandle->lzx_mutex); InitializeCriticalSection(&newHandle->cache_mutex);#else pthread_mutex_init(&newHandle->mutex, NULL); pthread_mutex_init(&newHandle->lzx_mutex, NULL); pthread_mutex_init(&newHandle->cache_mutex, NULL);#endif#endif /* read and verify header */ sremain = _CHM_ITSF_V3_LEN; sbufpos = sbuffer; if (_chm_fetch_bytes(newHandle, sbuffer, (UInt64)0, sremain) != sremain || !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader)) { chm_close(newHandle); return NULL; } /* stash important values from header */ newHandle->dir_offset = itsfHeader.dir_offset; newHandle->dir_len = itsfHeader.dir_len; newHandle->data_offset = itsfHeader.data_offset; /* now, read and verify the directory header chunk */ sremain = _CHM_ITSP_V1_LEN; sbufpos = sbuffer; if (_chm_fetch_bytes(newHandle, sbuffer, (UInt64)itsfHeader.dir_offset, sremain) != sremain || !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader)) { chm_close(newHandle); return NULL; } /* grab essential information from ITSP header */ newHandle->dir_offset += itspHeader.header_len; newHandle->dir_len -= itspHeader.header_len; newHandle->index_root = itspHeader.index_root; newHandle->index_head = itspHeader.index_head; newHandle->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 (newHandle->index_root == -1) newHandle->index_root = newHandle->index_head; /* By default, compression is enabled. */ newHandle->compression_enabled = 1;/* Jed, Sun Jun 27: 'span' doesn't seem to be used anywhere?! */#if 0 /* fetch span */ if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, _CHMU_SPANINFO, &uiSpan) || uiSpan.space == CHM_COMPRESSED) { chm_close(newHandle); return NULL; } /* N.B.: we've already checked that uiSpan is in the uncompressed section, * so this should not require attempting to decompress, which may * rely on having a valid "span" */ sremain = 8; sbufpos = sbuffer; if (chm_retrieve_object(newHandle, &uiSpan, sbuffer, 0, sremain) != sremain || !_unmarshal_uint64(&sbufpos, &sremain, &newHandle->span)) { chm_close(newHandle); return NULL; }#endif /* prefetch most commonly needed unit infos */ if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, _CHMU_RESET_TABLE, &newHandle->rt_unit) || newHandle->rt_unit.space == CHM_COMPRESSED || CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, _CHMU_CONTENT, &newHandle->cn_unit) || newHandle->cn_unit.space == CHM_COMPRESSED || CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, _CHMU_LZXC_CONTROLDATA, &uiLzxc) || uiLzxc.space == CHM_COMPRESSED) { newHandle->compression_enabled = 0; } /* read reset table info */ if (newHandle->compression_enabled) { sremain = _CHM_LZXC_RESETTABLE_V1_LEN; sbufpos = sbuffer; if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer, 0, sremain) != sremain || !_unmarshal_lzxc_reset_table(&sbufpos, &sremain, &newHandle->reset_table)) { newHandle->compression_enabled = 0; } } /* read control data */ if (newHandle->compression_enabled) { sremain = (unsigned long)uiLzxc.length; sbufpos = sbuffer; if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer, 0, sremain) != sremain || !_unmarshal_lzxc_control_data(&sbufpos, &sremain, &ctlData)) { newHandle->compression_enabled = 0; } newHandle->window_size = ctlData.windowSize; newHandle->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. */#if 0 newHandle->reset_blkcount = newHandle->reset_interval / (newHandle->window_size / 2);#else newHandle->reset_blkcount = newHandle->reset_interval / (newHandle->window_size / 2) * ctlData.windowsPerReset;#endif } /* initialize cache */ chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -