/*--[decompress_section]------------------------------------------------------- | | A somewhat tricky routine as it handles interfacing with the external | LZX library. There is a reason for using the ResetTable here, as | otherwise certain files will not be correctly handled! | | Generally -- | I need to get the LZXC control information to get the window size. | This lets me initialize the LZX library. | | From there, I read the ResetTable to know the uncompressed length and | each "LZXReset()" point. | | Note that the LZXReset() that this routine requires is not part of the | standard lzx distribution. The version that works with these files is from | the chmlib implementation and already has been modified. */ int decompress_section(lit_file * litfile, char * section_name, U8 * pControl, int sizeControl, U8 * pContent, int sizeContent, U8 ** ppUncompressed, int * psizeUncompressed) { char *path; int sizeRT, ofsEntry, base, dst, u; int bytesRemaining, uclength, window_bytes, accum, size; U8 * ptr, * pRT; int window_size, status; if ((sizeControl < 32) || (READ_U32(pControl+CONTROL_TAG) != LZXC_TAG)) { lit_error(ERR_R, "Invalid ControlData tag value %08lx should be %08lx!", (sizeControl > 8)?READ_U32(pControl+CONTROL_TAG):0, LZXC_TAG); return E_LIT_FORMAT_ERROR; } window_size = 14; u = READ_U32(pControl + CONTROL_WINDOW_SIZE); while (u) { u >>= 1; window_size++; } if ((window_size < 15) || (window_size > 21)) { lit_error(ERR_R, "Invalid window in ControlData - %d from %lx.", window_size, READ_U32(pControl+CONTROL_WINDOW_SIZE)); return -1; } status = LZXinit(window_size); if (status) { lit_error(ERR_R, "LZXinit(%d) failed, status = %d.", window_size, status); return E_LIT_LZX_ERROR; } path = lit_i_strmerge(storage_string,section_name,"/Transform/", lzxcompress_guid, rt_tail, NULL); if (!path) { return E_LIT_OUT_OF_MEMORY; } status = lit_get_file(litfile, path, &pRT, &sizeRT); if (status) { free(path); return status;} free(path); path = NULL; if (sizeRT < (RESET_INTERVAL+8)) { lit_error(ERR_R, "Reset table is too short (%d bytes).", sizeRT); free(pRT); return E_LIT_FORMAT_ERROR; } if (READ_U32(pRT + RESET_UCLENGTH + 4)) { lit_error(ERR_R,"Reset table has 64bit value for UCLENGTH!"); free(pRT); return E_LIT_64BIT_VALUE; } /* Skip first entry -- always 0! */ ofsEntry = READ_INT32(pRT + RESET_HDRLEN) + 8; uclength = READ_INT32(pRT + RESET_UCLENGTH); accum = READ_INT32(pRT + RESET_INTERVAL); ptr = malloc(uclength+1); /* Check for corruption */ ptr[uclength] = 0xCC; if (!ptr) { lit_error(ERR_R, "Unable to malloc uc length (%d bytes)!", uclength); free(pRT); return E_LIT_OUT_OF_MEMORY; } bytesRemaining = uclength; window_bytes = (1 << window_size); base = 0; dst = 0; while (ofsEntry < sizeRT) { if (accum == window_bytes) { accum = 0; size = READ_INT32(pRT + ofsEntry); u = READ_INT32(pRT + ofsEntry + 4); if (u) { lit_error(ERR_R, "Reset table entry greater than 32 bits!"); free(pRT); free(ptr); return E_LIT_64BIT_VALUE; } if (size >= sizeContent) { lit_error(ERR_R, "ResetTable entry out of bounds, %lx. (%d)", size, ofsEntry); free(ptr); free(pRT); return E_LIT_FORMAT_ERROR; } status = 0; if (bytesRemaining >= window_bytes) { LZXreset(); status = LZXdecompress(pContent + base, ptr+dst, size - base, window_bytes); bytesRemaining -= window_bytes; dst += window_bytes; base = size; } if (status) { lit_error(ERR_R, "LZXdecompress failed, status = %d.", status); free(ptr); free(pRT); return -1; } } accum += READ_INT32(pRT + RESET_INTERVAL); ofsEntry += 8; } free(pRT); if (bytesRemaining < window_bytes) { LZXreset(); status = LZXdecompress(pContent + base, ptr + dst, sizeContent - base, bytesRemaining); bytesRemaining = 0; } if (ptr[uclength] != 0xCC) { lit_error(ERR_R, "LZXdecompress overflowed memory. (%02x). \n"\ "This is a serious bug, please report this.\n", ptr[uclength]); /* Can't really free, anything may be corrupted at this point */ return -1; } if (status) { lit_error(ERR_R, "LZXdecompress failed, status = %d.", status); free(ptr); return E_LIT_LZX_ERROR; } if (bytesRemaining) { lit_error(ERR_R, "Failed to completely decompress section! (%d left).", bytesRemaining); free(ptr); return E_LIT_LZX_ERROR; } if (ppUncompressed) *ppUncompressed = ptr; else free(ptr); if (psizeUncompressed) *psizeUncompressed = uclength; return 0; }
qword _chm_decompress_block(chm_t *h, uqword block, uchar **ubuffer) { uchar *cbuffer = malloc(((unsigned int)h->reset_table.block_len + 6144)); uqword cmpStart; /* compressed start */ qword cmpLen; /* compressed len */ int indexSlot; /* cache index slot */ uchar *lbuffer; /* local buffer ptr */ udword blockAlign = (udword)(block % h->reset_blkcount); /* reset intvl. aln. */ udword i; /* local loop index */ if (cbuffer == NULL) return -1; /* let the caching system pull its weight! */ if (block - blockAlign <= h->lzx_last_block && block >= h->lzx_last_block) blockAlign = (udword)(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--) { udword curBlockIdx = (udword)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); if (! h->cache_blocks[indexSlot]) h->cache_blocks[indexSlot] = (uchar *)malloc((unsigned int)(h->reset_table.block_len)); if (! h->cache_blocks[indexSlot]) { free(cbuffer); return -1; } h->cache_block_indices[indexSlot] = curBlockIdx; lbuffer = h->cache_blocks[indexSlot]; /* decompress the previous block */ #ifdef CHM_DEBUG fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx); #endif if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) || cmpLen < 0 || cmpLen > h->reset_table.block_len + 6144 || _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, (int)h->reset_table.block_len) != DECR_OK) { #ifdef CHM_DEBUG fprintf(stderr, " (DECOMPRESS FAILED!)\n"); #endif free(cbuffer); return (qword)0; } h->lzx_last_block = (int)curBlockIdx; } } } else { if ((block % h->reset_blkcount) == 0) { #ifdef CHM_DEBUG fprintf(stderr, "***RESET (2)***\n"); #endif LZXreset(h->lzx_state); } } /* allocate slot in cache */ indexSlot = (int)(block % h->cache_num_blocks); if (! h->cache_blocks[indexSlot]) h->cache_blocks[indexSlot] = (uchar *)malloc(((unsigned int)h->reset_table.block_len)); if (! h->cache_blocks[indexSlot]) { free(cbuffer); return -1; } h->cache_block_indices[indexSlot] = block; lbuffer = h->cache_blocks[indexSlot]; *ubuffer = lbuffer; /* decompress the block we actually want */ #ifdef CHM_DEBUG fprintf(stderr, "Decompressing block #%4d (REAL )\n", block); #endif if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) || _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, (int)h->reset_table.block_len) != DECR_OK) { #ifdef CHM_DEBUG fprintf(stderr, " (DECOMPRESS FAILED!)\n"); #endif free(cbuffer); return (qword)0; } h->lzx_last_block = (int)block; /* XXX: modify LZX routines to return the length of the data they * decompressed and return that instead, for an extra sanity check. */ free(cbuffer); return h->reset_table.block_len; }