📄 chm_lib.cpp
字号:
static const char _chm_pmgl_marker[] = "PMGL";
#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[] = "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 unknown_14; /* 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->unknown_14);
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;
dest->unknown_14 *= 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
{
FILE* fd;
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 */
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;
fseek(h->fd,(long)os,SEEK_SET);
readLen=fread(buf,(size_t)1,(size_t)len,h->fd);
return readLen;
}
/* open an ITS archive */
struct chmFile *chm_open(const char *filename)
{
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 */
if ((newHandle->fd=fopen(filename, "rb")) == CHM_NULL_FD)
{
free(newHandle);
return NULL;
}
/* 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;
/* prefetch most commonly needed unit infos */
if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
_CHMU_SPANINFO,
&uiSpan) ||
uiSpan.space == CHM_COMPRESSED ||
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)
{
chm_close(newHandle);
return NULL;
}
/* try to read span */
/* 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;
}
/* read reset table info */
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))
{
chm_close(newHandle);
return NULL;
}
/* read control data */
sremain = (unsigned long)uiLzxc.length;
sbufpos = sbuffer;
if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer,
0, sremain) != sremain ||
!_unmarshal_lzxc_control_data(&sbufpos, &sremain,
&ctlData))
{
chm_close(newHandle);
return NULL;
}
newHandle->window_size = ctlData.windowSize;
newHandle->reset_interval = ctlData.resetInterval;
newHandle->reset_blkcount = newHandle->reset_interval /
(newHandle->window_size / 2);
/* initialize cache */
chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
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;
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);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -