Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenchm_lib.c
Go to the documentation of this file.
00001 /*************************************************************************** 00002 * chm_lib.c - CHM archive manipulation routines * 00003 * ------------------- * 00004 * * 00005 * author: Jed Wing <jedwin@ugcs.caltech.edu> * 00006 * version: 0.3 * 00007 * notes: These routines are meant for the manipulation of microsoft * 00008 * .chm (compiled html help) files, but may likely be used * 00009 * for the manipulation of any ITSS archive, if ever ITSS * 00010 * archives are used for any other purpose. * 00011 * * 00012 * Note also that the section names are statically handled. * 00013 * To be entirely correct, the section names should be read * 00014 * from the section names meta-file, and then the various * 00015 * content sections and the "transforms" to apply to the data * 00016 * they contain should be inferred from the section name and * 00017 * the meta-files referenced using that name; however, all of * 00018 * the files I've been able to get my hands on appear to have * 00019 * only two sections: Uncompressed and MSCompressed. * 00020 * Additionally, the ITSS.DLL file included with Windows does * 00021 * not appear to handle any different transforms than the * 00022 * simple LZX-transform. Furthermore, the list of transforms * 00023 * to apply is broken, in that only half the required space * 00024 * is allocated for the list. (It appears as though the * 00025 * space is allocated for ASCII strings, but the strings are * 00026 * written as unicode. As a result, only the first half of * 00027 * the string appears.) So this is probably not too big of * 00028 * a deal, at least until CHM v4 (MS .lit files), which also * 00029 * incorporate encryption, of some description. * 00030 * * 00031 ***************************************************************************/ 00032 00033 /*************************************************************************** 00034 * 00035 * This library is free software; you can redistribute it and/or 00036 * modify it under the terms of the GNU Lesser General Public 00037 * License as published by the Free Software Foundation; either 00038 * version 2.1 of the License, or (at your option) any later version. 00039 * 00040 * This library is distributed in the hope that it will be useful, 00041 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00042 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00043 * Lesser General Public License for more details. 00044 * 00045 * You should have received a copy of the GNU Lesser General Public 00046 * License along with this library; if not, write to the Free Software 00047 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00048 * 00049 ***************************************************************************/ 00050 00051 /*************************************************************************** 00052 * * 00053 * Adapted for Wine by Mike McCormack * 00054 * * 00055 ***************************************************************************/ 00056 00057 #include "config.h" 00058 #include "wine/port.h" 00059 00060 #include <stdarg.h> 00061 #include <stdio.h> 00062 #include <stdlib.h> 00063 #include <string.h> 00064 00065 #include "windef.h" 00066 #include "winbase.h" 00067 #include "wine/unicode.h" 00068 00069 #include "chm_lib.h" 00070 #include "lzx.h" 00071 00072 #define CHM_ACQUIRE_LOCK(a) do { \ 00073 EnterCriticalSection(&(a)); \ 00074 } while(0) 00075 #define CHM_RELEASE_LOCK(a) do { \ 00076 LeaveCriticalSection(&(a)); \ 00077 } while(0) 00078 00079 #define CHM_NULL_FD (INVALID_HANDLE_VALUE) 00080 #define CHM_CLOSE_FILE(fd) CloseHandle((fd)) 00081 00082 /* 00083 * defines related to tuning 00084 */ 00085 #ifndef CHM_MAX_BLOCKS_CACHED 00086 #define CHM_MAX_BLOCKS_CACHED 5 00087 #endif 00088 #define CHM_PARAM_MAX_BLOCKS_CACHED 0 00089 00090 /* 00091 * architecture specific defines 00092 * 00093 * Note: as soon as C99 is more widespread, the below defines should 00094 * probably just use the C99 sized-int types. 00095 * 00096 * The following settings will probably work for many platforms. The sizes 00097 * don't have to be exactly correct, but the types must accommodate at least as 00098 * many bits as they specify. 00099 */ 00100 00101 /* i386, 32-bit, Windows */ 00102 typedef BYTE UChar; 00103 typedef SHORT Int16; 00104 typedef USHORT UInt16; 00105 typedef LONG Int32; 00106 typedef DWORD UInt32; 00107 typedef LONGLONG Int64; 00108 typedef ULONGLONG UInt64; 00109 00110 /* utilities for unmarshalling data */ 00111 static int _unmarshal_char_array(unsigned char **pData, 00112 unsigned int *pLenRemain, 00113 char *dest, 00114 int count) 00115 { 00116 if (count <= 0 || (unsigned int)count > *pLenRemain) 00117 return 0; 00118 memcpy(dest, (*pData), count); 00119 *pData += count; 00120 *pLenRemain -= count; 00121 return 1; 00122 } 00123 00124 static int _unmarshal_uchar_array(unsigned char **pData, 00125 unsigned int *pLenRemain, 00126 unsigned char *dest, 00127 int count) 00128 { 00129 if (count <= 0 || (unsigned int)count > *pLenRemain) 00130 return 0; 00131 memcpy(dest, (*pData), count); 00132 *pData += count; 00133 *pLenRemain -= count; 00134 return 1; 00135 } 00136 00137 static int _unmarshal_int32(unsigned char **pData, 00138 unsigned int *pLenRemain, 00139 Int32 *dest) 00140 { 00141 if (4 > *pLenRemain) 00142 return 0; 00143 *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24; 00144 *pData += 4; 00145 *pLenRemain -= 4; 00146 return 1; 00147 } 00148 00149 static int _unmarshal_uint32(unsigned char **pData, 00150 unsigned int *pLenRemain, 00151 UInt32 *dest) 00152 { 00153 if (4 > *pLenRemain) 00154 return 0; 00155 *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24; 00156 *pData += 4; 00157 *pLenRemain -= 4; 00158 return 1; 00159 } 00160 00161 static int _unmarshal_int64(unsigned char **pData, 00162 unsigned int *pLenRemain, 00163 Int64 *dest) 00164 { 00165 Int64 temp; 00166 int i; 00167 if (8 > *pLenRemain) 00168 return 0; 00169 temp=0; 00170 for(i=8; i>0; i--) 00171 { 00172 temp <<= 8; 00173 temp |= (*pData)[i-1]; 00174 } 00175 *dest = temp; 00176 *pData += 8; 00177 *pLenRemain -= 8; 00178 return 1; 00179 } 00180 00181 static int _unmarshal_uint64(unsigned char **pData, 00182 unsigned int *pLenRemain, 00183 UInt64 *dest) 00184 { 00185 UInt64 temp; 00186 int i; 00187 if (8 > *pLenRemain) 00188 return 0; 00189 temp=0; 00190 for(i=8; i>0; i--) 00191 { 00192 temp <<= 8; 00193 temp |= (*pData)[i-1]; 00194 } 00195 *dest = temp; 00196 *pData += 8; 00197 *pLenRemain -= 8; 00198 return 1; 00199 } 00200 00201 static int _unmarshal_uuid(unsigned char **pData, 00202 unsigned int *pDataLen, 00203 unsigned char *dest) 00204 { 00205 return _unmarshal_uchar_array(pData, pDataLen, dest, 16); 00206 } 00207 00208 /* names of sections essential to decompression */ 00209 static const WCHAR _CHMU_RESET_TABLE[] = { 00210 ':',':','D','a','t','a','S','p','a','c','e','/', 00211 'S','t','o','r','a','g','e','/', 00212 'M','S','C','o','m','p','r','e','s','s','e','d','/', 00213 'T','r','a','n','s','f','o','r','m','/', 00214 '{','7','F','C','2','8','9','4','0','-','9','D','3','1', 00215 '-','1','1','D','0','-','9','B','2','7','-', 00216 '0','0','A','0','C','9','1','E','9','C','7','C','}','/', 00217 'I','n','s','t','a','n','c','e','D','a','t','a','/', 00218 'R','e','s','e','t','T','a','b','l','e',0 00219 }; 00220 static const WCHAR _CHMU_LZXC_CONTROLDATA[] = { 00221 ':',':','D','a','t','a','S','p','a','c','e','/', 00222 'S','t','o','r','a','g','e','/', 00223 'M','S','C','o','m','p','r','e','s','s','e','d','/', 00224 'C','o','n','t','r','o','l','D','a','t','a',0 00225 }; 00226 static const WCHAR _CHMU_CONTENT[] = { 00227 ':',':','D','a','t','a','S','p','a','c','e','/', 00228 'S','t','o','r','a','g','e','/', 00229 'M','S','C','o','m','p','r','e','s','s','e','d','/', 00230 'C','o','n','t','e','n','t',0 00231 }; 00232 00233 /* 00234 * structures local to this module 00235 */ 00236 00237 /* structure of ITSF headers */ 00238 #define _CHM_ITSF_V2_LEN (0x58) 00239 #define _CHM_ITSF_V3_LEN (0x60) 00240 struct chmItsfHeader 00241 { 00242 char signature[4]; /* 0 (ITSF) */ 00243 Int32 version; /* 4 */ 00244 Int32 header_len; /* 8 */ 00245 Int32 unknown_000c; /* c */ 00246 UInt32 last_modified; /* 10 */ 00247 UInt32 lang_id; /* 14 */ 00248 UChar dir_uuid[16]; /* 18 */ 00249 UChar stream_uuid[16]; /* 28 */ 00250 UInt64 unknown_offset; /* 38 */ 00251 UInt64 unknown_len; /* 40 */ 00252 UInt64 dir_offset; /* 48 */ 00253 UInt64 dir_len; /* 50 */ 00254 UInt64 data_offset; /* 58 (Not present before V3) */ 00255 }; /* __attribute__ ((aligned (1))); */ 00256 00257 static int _unmarshal_itsf_header(unsigned char **pData, 00258 unsigned int *pDataLen, 00259 struct chmItsfHeader *dest) 00260 { 00261 /* we only know how to deal with the 0x58 and 0x60 byte structures */ 00262 if (*pDataLen != _CHM_ITSF_V2_LEN && *pDataLen != _CHM_ITSF_V3_LEN) 00263 return 0; 00264 00265 /* unmarshal common fields */ 00266 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 00267 _unmarshal_int32 (pData, pDataLen, &dest->version); 00268 _unmarshal_int32 (pData, pDataLen, &dest->header_len); 00269 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c); 00270 _unmarshal_uint32 (pData, pDataLen, &dest->last_modified); 00271 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id); 00272 _unmarshal_uuid (pData, pDataLen, dest->dir_uuid); 00273 _unmarshal_uuid (pData, pDataLen, dest->stream_uuid); 00274 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_offset); 00275 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_len); 00276 _unmarshal_uint64 (pData, pDataLen, &dest->dir_offset); 00277 _unmarshal_uint64 (pData, pDataLen, &dest->dir_len); 00278 00279 /* error check the data */ 00280 /* XXX: should also check UUIDs, probably, though with a version 3 file, 00281 * current MS tools do not seem to use them. 00282 */ 00283 if (memcmp(dest->signature, "ITSF", 4) != 0) 00284 return 0; 00285 if (dest->version == 2) 00286 { 00287 if (dest->header_len < _CHM_ITSF_V2_LEN) 00288 return 0; 00289 } 00290 else if (dest->version == 3) 00291 { 00292 if (dest->header_len < _CHM_ITSF_V3_LEN) 00293 return 0; 00294 } 00295 else 00296 return 0; 00297 00298 /* now, if we have a V3 structure, unmarshal the rest. 00299 * otherwise, compute it 00300 */ 00301 if (dest->version == 3) 00302 { 00303 if (*pDataLen != 0) 00304 _unmarshal_uint64(pData, pDataLen, &dest->data_offset); 00305 else 00306 return 0; 00307 } 00308 else 00309 dest->data_offset = dest->dir_offset + dest->dir_len; 00310 00311 return 1; 00312 } 00313 00314 /* structure of ITSP headers */ 00315 #define _CHM_ITSP_V1_LEN (0x54) 00316 struct chmItspHeader 00317 { 00318 char signature[4]; /* 0 (ITSP) */ 00319 Int32 version; /* 4 */ 00320 Int32 header_len; /* 8 */ 00321 Int32 unknown_000c; /* c */ 00322 UInt32 block_len; /* 10 */ 00323 Int32 blockidx_intvl; /* 14 */ 00324 Int32 index_depth; /* 18 */ 00325 Int32 index_root; /* 1c */ 00326 Int32 index_head; /* 20 */ 00327 Int32 unknown_0024; /* 24 */ 00328 UInt32 num_blocks; /* 28 */ 00329 Int32 unknown_002c; /* 2c */ 00330 UInt32 lang_id; /* 30 */ 00331 UChar system_uuid[16]; /* 34 */ 00332 UChar unknown_0044[16]; /* 44 */ 00333 }; /* __attribute__ ((aligned (1))); */ 00334 00335 static int _unmarshal_itsp_header(unsigned char **pData, 00336 unsigned int *pDataLen, 00337 struct chmItspHeader *dest) 00338 { 00339 /* we only know how to deal with a 0x54 byte structures */ 00340 if (*pDataLen != _CHM_ITSP_V1_LEN) 00341 return 0; 00342 00343 /* unmarshal fields */ 00344 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 00345 _unmarshal_int32 (pData, pDataLen, &dest->version); 00346 _unmarshal_int32 (pData, pDataLen, &dest->header_len); 00347 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c); 00348 _unmarshal_uint32 (pData, pDataLen, &dest->block_len); 00349 _unmarshal_int32 (pData, pDataLen, &dest->blockidx_intvl); 00350 _unmarshal_int32 (pData, pDataLen, &dest->index_depth); 00351 _unmarshal_int32 (pData, pDataLen, &dest->index_root); 00352 _unmarshal_int32 (pData, pDataLen, &dest->index_head); 00353 _unmarshal_int32 (pData, pDataLen, &dest->unknown_0024); 00354 _unmarshal_uint32 (pData, pDataLen, &dest->num_blocks); 00355 _unmarshal_int32 (pData, pDataLen, &dest->unknown_002c); 00356 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id); 00357 _unmarshal_uuid (pData, pDataLen, dest->system_uuid); 00358 _unmarshal_uchar_array(pData, pDataLen, dest->unknown_0044, 16); 00359 00360 /* error check the data */ 00361 if (memcmp(dest->signature, "ITSP", 4) != 0) 00362 return 0; 00363 if (dest->version != 1) 00364 return 0; 00365 if (dest->header_len != _CHM_ITSP_V1_LEN) 00366 return 0; 00367 00368 return 1; 00369 } 00370 00371 /* structure of PMGL headers */ 00372 static const char _chm_pmgl_marker[4] = "PMGL"; 00373 #define _CHM_PMGL_LEN (0x14) 00374 struct chmPmglHeader 00375 { 00376 char signature[4]; /* 0 (PMGL) */ 00377 UInt32 free_space; /* 4 */ 00378 UInt32 unknown_0008; /* 8 */ 00379 Int32 block_prev; /* c */ 00380 Int32 block_next; /* 10 */ 00381 }; /* __attribute__ ((aligned (1))); */ 00382 00383 static int _unmarshal_pmgl_header(unsigned char **pData, 00384 unsigned int *pDataLen, 00385 struct chmPmglHeader *dest) 00386 { 00387 /* we only know how to deal with a 0x14 byte structures */ 00388 if (*pDataLen != _CHM_PMGL_LEN) 00389 return 0; 00390 00391 /* unmarshal fields */ 00392 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 00393 _unmarshal_uint32 (pData, pDataLen, &dest->free_space); 00394 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_0008); 00395 _unmarshal_int32 (pData, pDataLen, &dest->block_prev); 00396 _unmarshal_int32 (pData, pDataLen, &dest->block_next); 00397 00398 /* check structure */ 00399 if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0) 00400 return 0; 00401 00402 return 1; 00403 } 00404 00405 /* structure of PMGI headers */ 00406 static const char _chm_pmgi_marker[4] = "PMGI"; 00407 #define _CHM_PMGI_LEN (0x08) 00408 struct chmPmgiHeader 00409 { 00410 char signature[4]; /* 0 (PMGI) */ 00411 UInt32 free_space; /* 4 */ 00412 }; /* __attribute__ ((aligned (1))); */ 00413 00414 static int _unmarshal_pmgi_header(unsigned char **pData, 00415 unsigned int *pDataLen, 00416 struct chmPmgiHeader *dest) 00417 { 00418 /* we only know how to deal with a 0x8 byte structures */ 00419 if (*pDataLen != _CHM_PMGI_LEN) 00420 return 0; 00421 00422 /* unmarshal fields */ 00423 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 00424 _unmarshal_uint32 (pData, pDataLen, &dest->free_space); 00425 00426 /* check structure */ 00427 if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0) 00428 return 0; 00429 00430 return 1; 00431 } 00432 00433 /* structure of LZXC reset table */ 00434 #define _CHM_LZXC_RESETTABLE_V1_LEN (0x28) 00435 struct chmLzxcResetTable 00436 { 00437 UInt32 version; 00438 UInt32 block_count; 00439 UInt32 unknown; 00440 UInt32 table_offset; 00441 UInt64 uncompressed_len; 00442 UInt64 compressed_len; 00443 UInt64 block_len; 00444 }; /* __attribute__ ((aligned (1))); */ 00445 00446 static int _unmarshal_lzxc_reset_table(unsigned char **pData, 00447 unsigned int *pDataLen, 00448 struct chmLzxcResetTable *dest) 00449 { 00450 /* we only know how to deal with a 0x28 byte structures */ 00451 if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN) 00452 return 0; 00453 00454 /* unmarshal fields */ 00455 _unmarshal_uint32 (pData, pDataLen, &dest->version); 00456 _unmarshal_uint32 (pData, pDataLen, &dest->block_count); 00457 _unmarshal_uint32 (pData, pDataLen, &dest->unknown); 00458 _unmarshal_uint32 (pData, pDataLen, &dest->table_offset); 00459 _unmarshal_uint64 (pData, pDataLen, &dest->uncompressed_len); 00460 _unmarshal_uint64 (pData, pDataLen, &dest->compressed_len); 00461 _unmarshal_uint64 (pData, pDataLen, &dest->block_len); 00462 00463 /* check structure */ 00464 if (dest->version != 2) 00465 return 0; 00466 00467 return 1; 00468 } 00469 00470 /* structure of LZXC control data block */ 00471 #define _CHM_LZXC_MIN_LEN (0x18) 00472 #define _CHM_LZXC_V2_LEN (0x1c) 00473 struct chmLzxcControlData 00474 { 00475 UInt32 size; /* 0 */ 00476 char signature[4]; /* 4 (LZXC) */ 00477 UInt32 version; /* 8 */ 00478 UInt32 resetInterval; /* c */ 00479 UInt32 windowSize; /* 10 */ 00480 UInt32 windowsPerReset; /* 14 */ 00481 UInt32 unknown_18; /* 18 */ 00482 }; 00483 00484 static int _unmarshal_lzxc_control_data(unsigned char **pData, 00485 unsigned int *pDataLen, 00486 struct chmLzxcControlData *dest) 00487 { 00488 /* we want at least 0x18 bytes */ 00489 if (*pDataLen < _CHM_LZXC_MIN_LEN) 00490 return 0; 00491 00492 /* unmarshal fields */ 00493 _unmarshal_uint32 (pData, pDataLen, &dest->size); 00494 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 00495 _unmarshal_uint32 (pData, pDataLen, &dest->version); 00496 _unmarshal_uint32 (pData, pDataLen, &dest->resetInterval); 00497 _unmarshal_uint32 (pData, pDataLen, &dest->windowSize); 00498 _unmarshal_uint32 (pData, pDataLen, &dest->windowsPerReset); 00499 00500 if (*pDataLen >= _CHM_LZXC_V2_LEN) 00501 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_18); 00502 else 00503 dest->unknown_18 = 0; 00504 00505 if (dest->version == 2) 00506 { 00507 dest->resetInterval *= 0x8000; 00508 dest->windowSize *= 0x8000; 00509 } 00510 if (dest->windowSize == 0 || dest->resetInterval == 0) 00511 return 0; 00512 00513 /* for now, only support resetInterval a multiple of windowSize/2 */ 00514 if (dest->windowSize == 1) 00515 return 0; 00516 if ((dest->resetInterval % (dest->windowSize/2)) != 0) 00517 return 0; 00518 00519 /* check structure */ 00520 if (memcmp(dest->signature, "LZXC", 4) != 0) 00521 return 0; 00522 00523 return 1; 00524 } 00525 00526 /* the structure used for chm file handles */ 00527 struct chmFile 00528 { 00529 HANDLE fd; 00530 00531 CRITICAL_SECTION mutex; 00532 CRITICAL_SECTION lzx_mutex; 00533 CRITICAL_SECTION cache_mutex; 00534 00535 UInt64 dir_offset; 00536 UInt64 dir_len; 00537 UInt64 data_offset; 00538 Int32 index_root; 00539 Int32 index_head; 00540 UInt32 block_len; 00541 00542 UInt64 span; 00543 struct chmUnitInfo rt_unit; 00544 struct chmUnitInfo cn_unit; 00545 struct chmLzxcResetTable reset_table; 00546 00547 /* LZX control data */ 00548 int compression_enabled; 00549 UInt32 window_size; 00550 UInt32 reset_interval; 00551 UInt32 reset_blkcount; 00552 00553 /* decompressor state */ 00554 struct LZXstate *lzx_state; 00555 int lzx_last_block; 00556 00557 /* cache for decompressed blocks */ 00558 UChar **cache_blocks; 00559 Int64 *cache_block_indices; 00560 Int32 cache_num_blocks; 00561 }; 00562 00563 /* 00564 * utility functions local to this module 00565 */ 00566 00567 /* utility function to handle differences between {pread,read}(64)? */ 00568 static Int64 _chm_fetch_bytes(struct chmFile *h, 00569 UChar *buf, 00570 UInt64 os, 00571 Int64 len) 00572 { 00573 Int64 readLen=0; 00574 if (h->fd == CHM_NULL_FD) 00575 return readLen; 00576 00577 CHM_ACQUIRE_LOCK(h->mutex); 00578 /* NOTE: this might be better done with CreateFileMapping, et cetera... */ 00579 { 00580 LARGE_INTEGER old_pos, new_pos; 00581 DWORD actualLen=0; 00582 00583 /* awkward Win32 Seek/Tell */ 00584 new_pos.QuadPart = 0; 00585 SetFilePointerEx( h->fd, new_pos, &old_pos, FILE_CURRENT ); 00586 new_pos.QuadPart = os; 00587 SetFilePointerEx( h->fd, new_pos, NULL, FILE_BEGIN ); 00588 00589 /* read the data */ 00590 if (ReadFile(h->fd, 00591 buf, 00592 (DWORD)len, 00593 &actualLen, 00594 NULL)) 00595 readLen = actualLen; 00596 else 00597 readLen = 0; 00598 00599 /* restore original position */ 00600 SetFilePointerEx( h->fd, old_pos, NULL, FILE_BEGIN ); 00601 } 00602 CHM_RELEASE_LOCK(h->mutex); 00603 return readLen; 00604 } 00605 00606 /* 00607 * set a parameter on the file handle. 00608 * valid parameter types: 00609 * CHM_PARAM_MAX_BLOCKS_CACHED: 00610 * how many decompressed blocks should be cached? A simple 00611 * caching scheme is used, wherein the index of the block is 00612 * used as a hash value, and hash collision results in the 00613 * invalidation of the previously cached block. 00614 */ 00615 static void chm_set_param(struct chmFile *h, 00616 int paramType, 00617 int paramVal) 00618 { 00619 switch (paramType) 00620 { 00621 case CHM_PARAM_MAX_BLOCKS_CACHED: 00622 CHM_ACQUIRE_LOCK(h->cache_mutex); 00623 if (paramVal != h->cache_num_blocks) 00624 { 00625 UChar **newBlocks; 00626 Int64 *newIndices; 00627 int i; 00628 00629 /* allocate new cached blocks */ 00630 newBlocks = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UChar *)); 00631 newIndices = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UInt64)); 00632 for (i=0; i<paramVal; i++) 00633 { 00634 newBlocks[i] = NULL; 00635 newIndices[i] = 0; 00636 } 00637 00638 /* re-distribute old cached blocks */ 00639 if (h->cache_blocks) 00640 { 00641 for (i=0; i<h->cache_num_blocks; i++) 00642 { 00643 int newSlot = (int)(h->cache_block_indices[i] % paramVal); 00644 00645 if (h->cache_blocks[i]) 00646 { 00647 /* in case of collision, destroy newcomer */ 00648 if (newBlocks[newSlot]) 00649 { 00650 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]); 00651 h->cache_blocks[i] = NULL; 00652 } 00653 else 00654 { 00655 newBlocks[newSlot] = h->cache_blocks[i]; 00656 newIndices[newSlot] = 00657 h->cache_block_indices[i]; 00658 } 00659 } 00660 } 00661 00662 HeapFree(GetProcessHeap(), 0, h->cache_blocks); 00663 HeapFree(GetProcessHeap(), 0, h->cache_block_indices); 00664 } 00665 00666 /* now, set new values */ 00667 h->cache_blocks = newBlocks; 00668 h->cache_block_indices = newIndices; 00669 h->cache_num_blocks = paramVal; 00670 } 00671 CHM_RELEASE_LOCK(h->cache_mutex); 00672 break; 00673 00674 default: 00675 break; 00676 } 00677 } 00678 00679 /* open an ITS archive */ 00680 struct chmFile *chm_openW(const WCHAR *filename) 00681 { 00682 unsigned char sbuffer[256]; 00683 unsigned int sremain; 00684 unsigned char *sbufpos; 00685 struct chmFile *newHandle=NULL; 00686 struct chmItsfHeader itsfHeader; 00687 struct chmItspHeader itspHeader; 00688 #if 0 00689 struct chmUnitInfo uiSpan; 00690 #endif 00691 struct chmUnitInfo uiLzxc; 00692 struct chmLzxcControlData ctlData; 00693 00694 /* allocate handle */ 00695 newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile)); 00696 newHandle->fd = CHM_NULL_FD; 00697 newHandle->lzx_state = NULL; 00698 newHandle->cache_blocks = NULL; 00699 newHandle->cache_block_indices = NULL; 00700 newHandle->cache_num_blocks = 0; 00701 00702 /* open file */ 00703 if ((newHandle->fd=CreateFileW(filename, 00704 GENERIC_READ, 00705 FILE_SHARE_READ, 00706 NULL, 00707 OPEN_EXISTING, 00708 FILE_ATTRIBUTE_NORMAL, 00709 NULL)) == CHM_NULL_FD) 00710 { 00711 HeapFree(GetProcessHeap(), 0, newHandle); 00712 return NULL; 00713 } 00714 00715 /* initialize mutexes, if needed */ 00716 InitializeCriticalSection(&newHandle->mutex); 00717 newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex"); 00718 InitializeCriticalSection(&newHandle->lzx_mutex); 00719 newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex"); 00720 InitializeCriticalSection(&newHandle->cache_mutex); 00721 newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex"); 00722 00723 /* read and verify header */ 00724 sremain = _CHM_ITSF_V3_LEN; 00725 sbufpos = sbuffer; 00726 if (_chm_fetch_bytes(newHandle, sbuffer, 0, sremain) != sremain || 00727 !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader)) 00728 { 00729 chm_close(newHandle); 00730 return NULL; 00731 } 00732 00733 /* stash important values from header */ 00734 newHandle->dir_offset = itsfHeader.dir_offset; 00735 newHandle->dir_len = itsfHeader.dir_len; 00736 newHandle->data_offset = itsfHeader.data_offset; 00737 00738 /* now, read and verify the directory header chunk */ 00739 sremain = _CHM_ITSP_V1_LEN; 00740 sbufpos = sbuffer; 00741 if (_chm_fetch_bytes(newHandle, sbuffer, 00742 itsfHeader.dir_offset, sremain) != sremain || 00743 !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader)) 00744 { 00745 chm_close(newHandle); 00746 return NULL; 00747 } 00748 00749 /* grab essential information from ITSP header */ 00750 newHandle->dir_offset += itspHeader.header_len; 00751 newHandle->dir_len -= itspHeader.header_len; 00752 newHandle->index_root = itspHeader.index_root; 00753 newHandle->index_head = itspHeader.index_head; 00754 newHandle->block_len = itspHeader.block_len; 00755 00756 /* if the index root is -1, this means we don't have any PMGI blocks. 00757 * as a result, we must use the sole PMGL block as the index root 00758 */ 00759 if (newHandle->index_root == -1) 00760 newHandle->index_root = newHandle->index_head; 00761 00762 /* initialize cache */ 00763 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED, 00764 CHM_MAX_BLOCKS_CACHED); 00765 00766 /* By default, compression is enabled. */ 00767 newHandle->compression_enabled = 1; 00768 00769 /* prefetch most commonly needed unit infos */ 00770 if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, 00771 _CHMU_RESET_TABLE, 00772 &newHandle->rt_unit) || 00773 newHandle->rt_unit.space == CHM_COMPRESSED || 00774 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, 00775 _CHMU_CONTENT, 00776 &newHandle->cn_unit) || 00777 newHandle->cn_unit.space == CHM_COMPRESSED || 00778 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, 00779 _CHMU_LZXC_CONTROLDATA, 00780 &uiLzxc) || 00781 uiLzxc.space == CHM_COMPRESSED) 00782 { 00783 newHandle->compression_enabled = 0; 00784 } 00785 00786 /* read reset table info */ 00787 if (newHandle->compression_enabled) 00788 { 00789 sremain = _CHM_LZXC_RESETTABLE_V1_LEN; 00790 sbufpos = sbuffer; 00791 if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer, 00792 0, sremain) != sremain || 00793 !_unmarshal_lzxc_reset_table(&sbufpos, &sremain, 00794 &newHandle->reset_table)) 00795 { 00796 newHandle->compression_enabled = 0; 00797 } 00798 } 00799 00800 /* read control data */ 00801 if (newHandle->compression_enabled) 00802 { 00803 sremain = (unsigned long)uiLzxc.length; 00804 sbufpos = sbuffer; 00805 if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer, 00806 0, sremain) != sremain || 00807 !_unmarshal_lzxc_control_data(&sbufpos, &sremain, 00808 &ctlData)) 00809 { 00810 newHandle->compression_enabled = 0; 00811 } 00812 00813 newHandle->window_size = ctlData.windowSize; 00814 newHandle->reset_interval = ctlData.resetInterval; 00815 00816 /* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */ 00817 /* must be multiplied by this formerly unknown ctrl data field in */ 00818 /* order to decompress some files. */ 00819 #if 0 00820 newHandle->reset_blkcount = newHandle->reset_interval / 00821 (newHandle->window_size / 2); 00822 #else 00823 newHandle->reset_blkcount = newHandle->reset_interval / 00824 (newHandle->window_size / 2) * 00825 ctlData.windowsPerReset; 00826 #endif 00827 } 00828 00829 return newHandle; 00830 } 00831 00832 /* Duplicate an ITS archive handle */ 00833 struct chmFile *chm_dup(struct chmFile *oldHandle) 00834 { 00835 struct chmFile *newHandle=NULL; 00836 00837 newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile)); 00838 memcpy(newHandle, oldHandle, sizeof(struct chmFile)); 00839 00840 /* duplicate fd handle */ 00841 DuplicateHandle(GetCurrentProcess(), oldHandle->fd, 00842 GetCurrentProcess(), &(newHandle->fd), 00843 0, FALSE, DUPLICATE_SAME_ACCESS); 00844 newHandle->lzx_state = NULL; 00845 newHandle->cache_blocks = NULL; 00846 newHandle->cache_block_indices = NULL; 00847 newHandle->cache_num_blocks = 0; 00848 00849 /* initialize mutexes, if needed */ 00850 InitializeCriticalSection(&newHandle->mutex); 00851 newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex"); 00852 InitializeCriticalSection(&newHandle->lzx_mutex); 00853 newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex"); 00854 InitializeCriticalSection(&newHandle->cache_mutex); 00855 newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex"); 00856 00857 /* initialize cache */ 00858 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED, 00859 CHM_MAX_BLOCKS_CACHED); 00860 00861 return newHandle; 00862 } 00863 00864 /* close an ITS archive */ 00865 void chm_close(struct chmFile *h) 00866 { 00867 if (h != NULL) 00868 { 00869 if (h->fd != CHM_NULL_FD) 00870 CHM_CLOSE_FILE(h->fd); 00871 h->fd = CHM_NULL_FD; 00872 00873 h->mutex.DebugInfo->Spare[0] = 0; 00874 DeleteCriticalSection(&h->mutex); 00875 h->lzx_mutex.DebugInfo->Spare[0] = 0; 00876 DeleteCriticalSection(&h->lzx_mutex); 00877 h->cache_mutex.DebugInfo->Spare[0] = 0; 00878 DeleteCriticalSection(&h->cache_mutex); 00879 00880 if (h->lzx_state) 00881 LZXteardown(h->lzx_state); 00882 h->lzx_state = NULL; 00883 00884 if (h->cache_blocks) 00885 { 00886 int i; 00887 for (i=0; i<h->cache_num_blocks; i++) 00888 { 00889 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]); 00890 } 00891 HeapFree(GetProcessHeap(), 0, h->cache_blocks); 00892 h->cache_blocks = NULL; 00893 } 00894 00895 HeapFree(GetProcessHeap(), 0, h->cache_block_indices); 00896 h->cache_block_indices = NULL; 00897 00898 HeapFree(GetProcessHeap(), 0, h); 00899 } 00900 } 00901 00902 /* 00903 * helper methods for chm_resolve_object 00904 */ 00905 00906 /* skip a compressed dword */ 00907 static void _chm_skip_cword(UChar **pEntry) 00908 { 00909 while (*(*pEntry)++ >= 0x80) 00910 ; 00911 } 00912 00913 /* skip the data from a PMGL entry */ 00914 static void _chm_skip_PMGL_entry_data(UChar **pEntry) 00915 { 00916 _chm_skip_cword(pEntry); 00917 _chm_skip_cword(pEntry); 00918 _chm_skip_cword(pEntry); 00919 } 00920 00921 /* parse a compressed dword */ 00922 static UInt64 _chm_parse_cword(UChar **pEntry) 00923 { 00924 UInt64 accum = 0; 00925 UChar temp; 00926 while ((temp=*(*pEntry)++) >= 0x80) 00927 { 00928 accum <<= 7; 00929 accum += temp & 0x7f; 00930 } 00931 00932 return (accum << 7) + temp; 00933 } 00934 00935 /* parse a utf-8 string into an ASCII char buffer */ 00936 static int _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path) 00937 { 00938 /* MJM - Modified to return real Unicode strings */ 00939 while (count != 0) 00940 { 00941 *path++ = (*(*pEntry)++); 00942 --count; 00943 } 00944 00945 *path = '\0'; 00946 return 1; 00947 } 00948 00949 /* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */ 00950 static int _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui) 00951 { 00952 UInt64 strLen; 00953 00954 /* parse str len */ 00955 strLen = _chm_parse_cword(pEntry); 00956 if (strLen > CHM_MAX_PATHLEN) 00957 return 0; 00958 00959 /* parse path */ 00960 if (! _chm_parse_UTF8(pEntry, strLen, ui->path)) 00961 return 0; 00962 00963 /* parse info */ 00964 ui->space = (int)_chm_parse_cword(pEntry); 00965 ui->start = _chm_parse_cword(pEntry); 00966 ui->length = _chm_parse_cword(pEntry); 00967 return 1; 00968 } 00969 00970 /* find an exact entry in PMGL; return NULL if we fail */ 00971 static UChar *_chm_find_in_PMGL(UChar *page_buf, 00972 UInt32 block_len, 00973 const WCHAR *objPath) 00974 { 00975 /* XXX: modify this to do a binary search using the nice index structure 00976 * that is provided for us. 00977 */ 00978 struct chmPmglHeader header; 00979 UInt32 hremain; 00980 UChar *end; 00981 UChar *cur; 00982 UChar *temp; 00983 UInt64 strLen; 00984 WCHAR buffer[CHM_MAX_PATHLEN+1]; 00985 00986 /* figure out where to start and end */ 00987 cur = page_buf; 00988 hremain = _CHM_PMGL_LEN; 00989 if (! _unmarshal_pmgl_header(&cur, &hremain, &header)) 00990 return NULL; 00991 end = page_buf + block_len - (header.free_space); 00992 00993 /* now, scan progressively */ 00994 while (cur < end) 00995 { 00996 /* grab the name */ 00997 temp = cur; 00998 strLen = _chm_parse_cword(&cur); 00999 if (! _chm_parse_UTF8(&cur, strLen, buffer)) 01000 return NULL; 01001 01002 /* check if it is the right name */ 01003 if (! strcmpiW(buffer, objPath)) 01004 return temp; 01005 01006 _chm_skip_PMGL_entry_data(&cur); 01007 } 01008 01009 return NULL; 01010 } 01011 01012 /* find which block should be searched next for the entry; -1 if no block */ 01013 static Int32 _chm_find_in_PMGI(UChar *page_buf, 01014 UInt32 block_len, 01015 const WCHAR *objPath) 01016 { 01017 /* XXX: modify this to do a binary search using the nice index structure 01018 * that is provided for us 01019 */ 01020 struct chmPmgiHeader header; 01021 UInt32 hremain; 01022 int page=-1; 01023 UChar *end; 01024 UChar *cur; 01025 UInt64 strLen; 01026 WCHAR buffer[CHM_MAX_PATHLEN+1]; 01027 01028 /* figure out where to start and end */ 01029 cur = page_buf; 01030 hremain = _CHM_PMGI_LEN; 01031 if (! _unmarshal_pmgi_header(&cur, &hremain, &header)) 01032 return -1; 01033 end = page_buf + block_len - (header.free_space); 01034 01035 /* now, scan progressively */ 01036 while (cur < end) 01037 { 01038 /* grab the name */ 01039 strLen = _chm_parse_cword(&cur); 01040 if (! _chm_parse_UTF8(&cur, strLen, buffer)) 01041 return -1; 01042 01043 /* check if it is the right name */ 01044 if (strcmpiW(buffer, objPath) > 0) 01045 return page; 01046 01047 /* load next value for path */ 01048 page = (int)_chm_parse_cword(&cur); 01049 } 01050 01051 return page; 01052 } 01053 01054 /* resolve a particular object from the archive */ 01055 int chm_resolve_object(struct chmFile *h, 01056 const WCHAR *objPath, 01057 struct chmUnitInfo *ui) 01058 { 01059 /* 01060 * XXX: implement caching scheme for dir pages 01061 */ 01062 01063 Int32 curPage; 01064 01065 /* buffer to hold whatever page we're looking at */ 01066 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len); 01067 01068 /* starting page */ 01069 curPage = h->index_root; 01070 01071 /* until we have either returned or given up */ 01072 while (curPage != -1) 01073 { 01074 01075 /* try to fetch the index page */ 01076 if (_chm_fetch_bytes(h, page_buf, 01077 h->dir_offset + (UInt64)curPage*h->block_len, 01078 h->block_len) != h->block_len) 01079 { 01080 HeapFree(GetProcessHeap(), 0, page_buf); 01081 return CHM_RESOLVE_FAILURE; 01082 } 01083 01084 /* now, if it is a leaf node: */ 01085 if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0) 01086 { 01087 /* scan block */ 01088 UChar *pEntry = _chm_find_in_PMGL(page_buf, 01089 h->block_len, 01090 objPath); 01091 if (pEntry == NULL) 01092 { 01093 HeapFree(GetProcessHeap(), 0, page_buf); 01094 return CHM_RESOLVE_FAILURE; 01095 } 01096 01097 /* parse entry and return */ 01098 _chm_parse_PMGL_entry(&pEntry, ui); 01099 HeapFree(GetProcessHeap(), 0, page_buf); 01100 return CHM_RESOLVE_SUCCESS; 01101 } 01102 01103 /* else, if it is a branch node: */ 01104 else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0) 01105 curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath); 01106 01107 /* else, we are confused. give up. */ 01108 else 01109 { 01110 HeapFree(GetProcessHeap(), 0, page_buf); 01111 return CHM_RESOLVE_FAILURE; 01112 } 01113 } 01114 01115 /* didn't find anything. fail. */ 01116 HeapFree(GetProcessHeap(), 0, page_buf); 01117 return CHM_RESOLVE_FAILURE; 01118 } 01119 01120 /* 01121 * utility methods for dealing with compressed data 01122 */ 01123 01124 /* get the bounds of a compressed block. return 0 on failure */ 01125 static int _chm_get_cmpblock_bounds(struct chmFile *h, 01126 UInt64 block, 01127 UInt64 *start, 01128 Int64 *len) 01129 { 01130 UChar buffer[8], *dummy; 01131 UInt32 remain; 01132 01133 /* for all but the last block, use the reset table */ 01134 if (block < h->reset_table.block_count-1) 01135 { 01136 /* unpack the start address */ 01137 dummy = buffer; 01138 remain = 8; 01139 if (_chm_fetch_bytes(h, buffer, 01140 h->data_offset 01141 + h->rt_unit.start 01142 + h->reset_table.table_offset 01143 + block*8, 01144 remain) != remain || 01145 !_unmarshal_uint64(&dummy, &remain, start)) 01146 return 0; 01147 01148 /* unpack the end address */ 01149 dummy = buffer; 01150 remain = 8; 01151 if (_chm_fetch_bytes(h, buffer, 01152 h->data_offset 01153 + h->rt_unit.start 01154 + h->reset_table.table_offset 01155 + block*8 + 8, 01156 remain) != remain || 01157 !_unmarshal_int64(&dummy, &remain, len)) 01158 return 0; 01159 } 01160 01161 /* for the last block, use the span in addition to the reset table */ 01162 else 01163 { 01164 /* unpack the start address */ 01165 dummy = buffer; 01166 remain = 8; 01167 if (_chm_fetch_bytes(h, buffer, 01168 h->data_offset 01169 + h->rt_unit.start 01170 + h->reset_table.table_offset 01171 + block*8, 01172 remain) != remain || 01173 !_unmarshal_uint64(&dummy, &remain, start)) 01174 return 0; 01175 01176 *len = h->reset_table.compressed_len; 01177 } 01178 01179 /* compute the length and absolute start address */ 01180 *len -= *start; 01181 *start += h->data_offset + h->cn_unit.start; 01182 01183 return 1; 01184 } 01185 01186 /* decompress the block. must have lzx_mutex. */ 01187 static Int64 _chm_decompress_block(struct chmFile *h, 01188 UInt64 block, 01189 UChar **ubuffer) 01190 { 01191 UChar *cbuffer = HeapAlloc( GetProcessHeap(), 0, 01192 ((unsigned int)h->reset_table.block_len + 6144)); 01193 UInt64 cmpStart; /* compressed start */ 01194 Int64 cmpLen; /* compressed len */ 01195 int indexSlot; /* cache index slot */ 01196 UChar *lbuffer; /* local buffer ptr */ 01197 UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset interval align */ 01198 UInt32 i; /* local loop index */ 01199 01200 /* let the caching system pull its weight! */ 01201 if (block - blockAlign <= h->lzx_last_block && 01202 block >= h->lzx_last_block) 01203 blockAlign = (block - h->lzx_last_block); 01204 01205 /* check if we need previous blocks */ 01206 if (blockAlign != 0) 01207 { 01208 /* fetch all required previous blocks since last reset */ 01209 for (i = blockAlign; i > 0; i--) 01210 { 01211 UInt32 curBlockIdx = block - i; 01212 01213 /* check if we most recently decompressed the previous block */ 01214 if (h->lzx_last_block != curBlockIdx) 01215 { 01216 if ((curBlockIdx % h->reset_blkcount) == 0) 01217 { 01218 #ifdef CHM_DEBUG 01219 fprintf(stderr, "***RESET (1)***\n"); 01220 #endif 01221 LZXreset(h->lzx_state); 01222 } 01223 01224 indexSlot = (int)((curBlockIdx) % h->cache_num_blocks); 01225 h->cache_block_indices[indexSlot] = curBlockIdx; 01226 if (! h->cache_blocks[indexSlot]) 01227 h->cache_blocks[indexSlot] = 01228 HeapAlloc(GetProcessHeap(), 0, 01229 (unsigned int)(h->reset_table.block_len)); 01230 lbuffer = h->cache_blocks[indexSlot]; 01231 01232 /* decompress the previous block */ 01233 #ifdef CHM_DEBUG 01234 fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx); 01235 #endif 01236 if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) || 01237 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || 01238 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, 01239 (int)h->reset_table.block_len) != DECR_OK) 01240 { 01241 #ifdef CHM_DEBUG 01242 fprintf(stderr, " (DECOMPRESS FAILED!)\n"); 01243 #endif 01244 HeapFree(GetProcessHeap(), 0, cbuffer); 01245 return 0; 01246 } 01247 01248 h->lzx_last_block = (int)curBlockIdx; 01249 } 01250 } 01251 } 01252 else 01253 { 01254 if ((block % h->reset_blkcount) == 0) 01255 { 01256 #ifdef CHM_DEBUG 01257 fprintf(stderr, "***RESET (2)***\n"); 01258 #endif 01259 LZXreset(h->lzx_state); 01260 } 01261 } 01262 01263 /* allocate slot in cache */ 01264 indexSlot = (int)(block % h->cache_num_blocks); 01265 h->cache_block_indices[indexSlot] = block; 01266 if (! h->cache_blocks[indexSlot]) 01267 h->cache_blocks[indexSlot] = 01268 HeapAlloc(GetProcessHeap(), 0, ((unsigned int)h->reset_table.block_len)); 01269 lbuffer = h->cache_blocks[indexSlot]; 01270 *ubuffer = lbuffer; 01271 01272 /* decompress the block we actually want */ 01273 #ifdef CHM_DEBUG 01274 fprintf(stderr, "Decompressing block #%4d (REAL )\n", block); 01275 #endif 01276 if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) || 01277 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || 01278 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, 01279 (int)h->reset_table.block_len) != DECR_OK) 01280 { 01281 #ifdef CHM_DEBUG 01282 fprintf(stderr, " (DECOMPRESS FAILED!)\n"); 01283 #endif 01284 HeapFree(GetProcessHeap(), 0, cbuffer); 01285 return 0; 01286 } 01287 h->lzx_last_block = (int)block; 01288 01289 /* XXX: modify LZX routines to return the length of the data they 01290 * decompressed and return that instead, for an extra sanity check. 01291 */ 01292 HeapFree(GetProcessHeap(), 0, cbuffer); 01293 return h->reset_table.block_len; 01294 } 01295 01296 /* grab a region from a compressed block */ 01297 static Int64 _chm_decompress_region(struct chmFile *h, 01298 UChar *buf, 01299 UInt64 start, 01300 Int64 len) 01301 { 01302 UInt64 nBlock, nOffset; 01303 UInt64 nLen; 01304 UInt64 gotLen; 01305 UChar *ubuffer = NULL; 01306 01307 if (len <= 0) 01308 return 0; 01309 01310 /* figure out what we need to read */ 01311 nBlock = start / h->reset_table.block_len; 01312 nOffset = start % h->reset_table.block_len; 01313 nLen = len; 01314 if (nLen > (h->reset_table.block_len - nOffset)) 01315 nLen = h->reset_table.block_len - nOffset; 01316 01317 /* if block is cached, return data from it. */ 01318 CHM_ACQUIRE_LOCK(h->lzx_mutex); 01319 CHM_ACQUIRE_LOCK(h->cache_mutex); 01320 if (h->cache_block_indices[nBlock % h->cache_num_blocks] == nBlock && 01321 h->cache_blocks[nBlock % h->cache_num_blocks] != NULL) 01322 { 01323 memcpy(buf, 01324 h->cache_blocks[nBlock % h->cache_num_blocks] + nOffset, 01325 (unsigned int)nLen); 01326 CHM_RELEASE_LOCK(h->cache_mutex); 01327 CHM_RELEASE_LOCK(h->lzx_mutex); 01328 return nLen; 01329 } 01330 CHM_RELEASE_LOCK(h->cache_mutex); 01331 01332 /* data request not satisfied, so... start up the decompressor machine */ 01333 if (! h->lzx_state) 01334 { 01335 int window_size = ffs(h->window_size) - 1; 01336 h->lzx_last_block = -1; 01337 h->lzx_state = LZXinit(window_size); 01338 } 01339 01340 /* decompress some data */ 01341 gotLen = _chm_decompress_block(h, nBlock, &ubuffer); 01342 if (gotLen < nLen) 01343 nLen = gotLen; 01344 memcpy(buf, ubuffer+nOffset, (unsigned int)nLen); 01345 CHM_RELEASE_LOCK(h->lzx_mutex); 01346 return nLen; 01347 } 01348 01349 /* retrieve (part of) an object */ 01350 LONGINT64 chm_retrieve_object(struct chmFile *h, 01351 struct chmUnitInfo *ui, 01352 unsigned char *buf, 01353 LONGUINT64 addr, 01354 LONGINT64 len) 01355 { 01356 /* must be valid file handle */ 01357 if (h == NULL) 01358 return 0; 01359 01360 /* starting address must be in correct range */ 01361 if (addr >= ui->length) 01362 return 0; 01363 01364 /* clip length */ 01365 if (addr + len > ui->length) 01366 len = ui->length - addr; 01367 01368 /* if the file is uncompressed, it's simple */ 01369 if (ui->space == CHM_UNCOMPRESSED) 01370 { 01371 /* read data */ 01372 return _chm_fetch_bytes(h, 01373 buf, 01374 h->data_offset + ui->start + addr, 01375 len); 01376 } 01377 01378 /* else if the file is compressed, it's a little trickier */ 01379 else /* ui->space == CHM_COMPRESSED */ 01380 { 01381 Int64 swath=0, total=0; 01382 01383 /* if compression is not enabled for this file... */ 01384 if (! h->compression_enabled) 01385 return total; 01386 01387 do { 01388 01389 /* swill another mouthful */ 01390 swath = _chm_decompress_region(h, buf, ui->start + addr, len); 01391 01392 /* if we didn't get any... */ 01393 if (swath == 0) 01394 return total; 01395 01396 /* update stats */ 01397 total += swath; 01398 len -= swath; 01399 addr += swath; 01400 buf += swath; 01401 01402 } while (len != 0); 01403 01404 return total; 01405 } 01406 } 01407 01408 int chm_enumerate_dir(struct chmFile *h, 01409 const WCHAR *prefix, 01410 int what, 01411 CHM_ENUMERATOR e, 01412 void *context) 01413 { 01414 /* 01415 * XXX: do this efficiently (i.e. using the tree index) 01416 */ 01417 01418 Int32 curPage; 01419 01420 /* buffer to hold whatever page we're looking at */ 01421 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len); 01422 struct chmPmglHeader header; 01423 UChar *end; 01424 UChar *cur; 01425 unsigned int lenRemain; 01426 01427 /* set to 1 once we've started */ 01428 int it_has_begun=0; 01429 01430 /* the current ui */ 01431 struct chmUnitInfo ui; 01432 int flag; 01433 UInt64 ui_path_len; 01434 01435 /* the length of the prefix */ 01436 WCHAR prefixRectified[CHM_MAX_PATHLEN+1]; 01437 int prefixLen; 01438 WCHAR lastPath[CHM_MAX_PATHLEN]; 01439 int lastPathLen; 01440 01441 /* starting page */ 01442 curPage = h->index_head; 01443 01444 /* initialize pathname state */ 01445 lstrcpynW(prefixRectified, prefix, CHM_MAX_PATHLEN); 01446 prefixLen = strlenW(prefixRectified); 01447 if (prefixLen != 0) 01448 { 01449 if (prefixRectified[prefixLen-1] != '/') 01450 { 01451 prefixRectified[prefixLen] = '/'; 01452 prefixRectified[prefixLen+1] = '\0'; 01453 ++prefixLen; 01454 } 01455 } 01456 lastPath[0] = '\0'; 01457 lastPathLen = -1; 01458 01459 /* until we have either returned or given up */ 01460 while (curPage != -1) 01461 { 01462 01463 /* try to fetch the index page */ 01464 if (_chm_fetch_bytes(h, 01465 page_buf, 01466 h->dir_offset + (UInt64)curPage*h->block_len, 01467 h->block_len) != h->block_len) 01468 { 01469 HeapFree(GetProcessHeap(), 0, page_buf); 01470 return 0; 01471 } 01472 01473 /* figure out start and end for this page */ 01474 cur = page_buf; 01475 lenRemain = _CHM_PMGL_LEN; 01476 if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header)) 01477 { 01478 HeapFree(GetProcessHeap(), 0, page_buf); 01479 return 0; 01480 } 01481 end = page_buf + h->block_len - (header.free_space); 01482 01483 /* loop over this page */ 01484 while (cur < end) 01485 { 01486 if (! _chm_parse_PMGL_entry(&cur, &ui)) 01487 { 01488 HeapFree(GetProcessHeap(), 0, page_buf); 01489 return 0; 01490 } 01491 01492 /* check if we should start */ 01493 if (! it_has_begun) 01494 { 01495 if (ui.length == 0 && strncmpiW(ui.path, prefixRectified, prefixLen) == 0) 01496 it_has_begun = 1; 01497 else 01498 continue; 01499 01500 if (ui.path[prefixLen] == '\0') 01501 continue; 01502 } 01503 01504 /* check if we should stop */ 01505 else 01506 { 01507 if (strncmpiW(ui.path, prefixRectified, prefixLen) != 0) 01508 { 01509 HeapFree(GetProcessHeap(), 0, page_buf); 01510 return 1; 01511 } 01512 } 01513 01514 /* check if we should include this path */ 01515 if (lastPathLen != -1) 01516 { 01517 if (strncmpiW(ui.path, lastPath, lastPathLen) == 0) 01518 continue; 01519 } 01520 strcpyW(lastPath, ui.path); 01521 lastPathLen = strlenW(lastPath); 01522 01523 /* get the length of the path */ 01524 ui_path_len = strlenW(ui.path)-1; 01525 01526 /* check for DIRS */ 01527 if (ui.path[ui_path_len] == '/' && !(what & CHM_ENUMERATE_DIRS)) 01528 continue; 01529 01530 /* check for FILES */ 01531 if (ui.path[ui_path_len] != '/' && !(what & CHM_ENUMERATE_FILES)) 01532 continue; 01533 01534 /* check for NORMAL vs. META */ 01535 if (ui.path[0] == '/') 01536 { 01537 01538 /* check for NORMAL vs. SPECIAL */ 01539 if (ui.path[1] == '#' || ui.path[1] == '$') 01540 flag = CHM_ENUMERATE_SPECIAL; 01541 else 01542 flag = CHM_ENUMERATE_NORMAL; 01543 } 01544 else 01545 flag = CHM_ENUMERATE_META; 01546 if (! (what & flag)) 01547 continue; 01548 01549 /* call the enumerator */ 01550 { 01551 int status = (*e)(h, &ui, context); 01552 switch (status) 01553 { 01554 case CHM_ENUMERATOR_FAILURE: 01555 HeapFree(GetProcessHeap(), 0, page_buf); 01556 return 0; 01557 case CHM_ENUMERATOR_CONTINUE: 01558 break; 01559 case CHM_ENUMERATOR_SUCCESS: 01560 HeapFree(GetProcessHeap(), 0, page_buf); 01561 return 1; 01562 default: 01563 break; 01564 } 01565 } 01566 } 01567 01568 /* advance to next page */ 01569 curPage = header.block_next; 01570 } 01571 01572 HeapFree(GetProcessHeap(), 0, page_buf); 01573 return 1; 01574 } Generated on Sat May 26 2012 04:22:43 for ReactOS by
1.7.6.1
|