1 /*************************************************************************** 2 * chm_lib.c - CHM archive manipulation routines * 3 * ------------------- * 4 * * 5 * author: Jed Wing <jedwin@ugcs.caltech.edu> * 6 * version: 0.3 * 7 * notes: These routines are meant for the manipulation of microsoft * 8 * .chm (compiled html help) files, but may likely be used * 9 * for the manipulation of any ITSS archive, if ever ITSS * 10 * archives are used for any other purpose. * 11 * * 12 * Note also that the section names are statically handled. * 13 * To be entirely correct, the section names should be read * 14 * from the section names meta-file, and then the various * 15 * content sections and the "transforms" to apply to the data * 16 * they contain should be inferred from the section name and * 17 * the meta-files referenced using that name; however, all of * 18 * the files I've been able to get my hands on appear to have * 19 * only two sections: Uncompressed and MSCompressed. * 20 * Additionally, the ITSS.DLL file included with Windows does * 21 * not appear to handle any different transforms than the * 22 * simple LZX-transform. Furthermore, the list of transforms * 23 * to apply is broken, in that only half the required space * 24 * is allocated for the list. (It appears as though the * 25 * space is allocated for ASCII strings, but the strings are * 26 * written as unicode. As a result, only the first half of * 27 * the string appears.) So this is probably not too big of * 28 * a deal, at least until CHM v4 (MS .lit files), which also * 29 * incorporate encryption, of some description. * 30 * * 31 ***************************************************************************/ 32 33 /*************************************************************************** 34 * 35 * This library is free software; you can redistribute it and/or 36 * modify it under the terms of the GNU Lesser General Public 37 * License as published by the Free Software Foundation; either 38 * version 2.1 of the License, or (at your option) any later version. 39 * 40 * This library is distributed in the hope that it will be useful, 41 * but WITHOUT ANY WARRANTY; without even the implied warranty of 42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 43 * Lesser General Public License for more details. 44 * 45 * You should have received a copy of the GNU Lesser General Public 46 * License along with this library; if not, write to the Free Software 47 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 48 * 49 ***************************************************************************/ 50 51 /*************************************************************************** 52 * * 53 * Adapted for Wine by Mike McCormack * 54 * * 55 ***************************************************************************/ 56 57 #include "config.h" 58 #include "wine/port.h" 59 60 #include <stdarg.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 65 #include "windef.h" 66 #include "winbase.h" 67 #include "wine/unicode.h" 68 69 #include "chm_lib.h" 70 #include "lzx.h" 71 72 #define CHM_ACQUIRE_LOCK(a) do { \ 73 EnterCriticalSection(&(a)); \ 74 } while(0) 75 #define CHM_RELEASE_LOCK(a) do { \ 76 LeaveCriticalSection(&(a)); \ 77 } while(0) 78 79 #define CHM_NULL_FD (INVALID_HANDLE_VALUE) 80 #define CHM_CLOSE_FILE(fd) CloseHandle((fd)) 81 82 /* 83 * defines related to tuning 84 */ 85 #ifndef CHM_MAX_BLOCKS_CACHED 86 #define CHM_MAX_BLOCKS_CACHED 5 87 #endif 88 #define CHM_PARAM_MAX_BLOCKS_CACHED 0 89 90 /* 91 * architecture specific defines 92 * 93 * Note: as soon as C99 is more widespread, the below defines should 94 * probably just use the C99 sized-int types. 95 * 96 * The following settings will probably work for many platforms. The sizes 97 * don't have to be exactly correct, but the types must accommodate at least as 98 * many bits as they specify. 99 */ 100 101 /* i386, 32-bit, Windows */ 102 typedef BYTE UChar; 103 typedef SHORT Int16; 104 typedef USHORT UInt16; 105 typedef LONG Int32; 106 typedef DWORD UInt32; 107 typedef LONGLONG Int64; 108 typedef ULONGLONG UInt64; 109 110 /* utilities for unmarshalling data */ 111 static BOOL _unmarshal_char_array(unsigned char **pData, 112 unsigned int *pLenRemain, 113 char *dest, 114 int count) 115 { 116 if (count <= 0 || (unsigned int)count > *pLenRemain) 117 return FALSE; 118 memcpy(dest, (*pData), count); 119 *pData += count; 120 *pLenRemain -= count; 121 return TRUE; 122 } 123 124 static BOOL _unmarshal_uchar_array(unsigned char **pData, 125 unsigned int *pLenRemain, 126 unsigned char *dest, 127 int count) 128 { 129 if (count <= 0 || (unsigned int)count > *pLenRemain) 130 return FALSE; 131 memcpy(dest, (*pData), count); 132 *pData += count; 133 *pLenRemain -= count; 134 return TRUE; 135 } 136 137 static BOOL _unmarshal_int32(unsigned char **pData, 138 unsigned int *pLenRemain, 139 Int32 *dest) 140 { 141 if (4 > *pLenRemain) 142 return FALSE; 143 *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24; 144 *pData += 4; 145 *pLenRemain -= 4; 146 return TRUE; 147 } 148 149 static BOOL _unmarshal_uint32(unsigned char **pData, 150 unsigned int *pLenRemain, 151 UInt32 *dest) 152 { 153 if (4 > *pLenRemain) 154 return FALSE; 155 *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24; 156 *pData += 4; 157 *pLenRemain -= 4; 158 return TRUE; 159 } 160 161 static BOOL _unmarshal_int64(unsigned char **pData, 162 unsigned int *pLenRemain, 163 Int64 *dest) 164 { 165 Int64 temp; 166 int i; 167 if (8 > *pLenRemain) 168 return FALSE; 169 temp=0; 170 for(i=8; i>0; i--) 171 { 172 temp <<= 8; 173 temp |= (*pData)[i-1]; 174 } 175 *dest = temp; 176 *pData += 8; 177 *pLenRemain -= 8; 178 return TRUE; 179 } 180 181 static BOOL _unmarshal_uint64(unsigned char **pData, 182 unsigned int *pLenRemain, 183 UInt64 *dest) 184 { 185 UInt64 temp; 186 int i; 187 if (8 > *pLenRemain) 188 return FALSE; 189 temp=0; 190 for(i=8; i>0; i--) 191 { 192 temp <<= 8; 193 temp |= (*pData)[i-1]; 194 } 195 *dest = temp; 196 *pData += 8; 197 *pLenRemain -= 8; 198 return TRUE; 199 } 200 201 static BOOL _unmarshal_uuid(unsigned char **pData, 202 unsigned int *pDataLen, 203 unsigned char *dest) 204 { 205 return _unmarshal_uchar_array(pData, pDataLen, dest, 16); 206 } 207 208 /* names of sections essential to decompression */ 209 static const WCHAR _CHMU_RESET_TABLE[] = { 210 ':',':','D','a','t','a','S','p','a','c','e','/', 211 'S','t','o','r','a','g','e','/', 212 'M','S','C','o','m','p','r','e','s','s','e','d','/', 213 'T','r','a','n','s','f','o','r','m','/', 214 '{','7','F','C','2','8','9','4','0','-','9','D','3','1', 215 '-','1','1','D','0','-','9','B','2','7','-', 216 '0','0','A','0','C','9','1','E','9','C','7','C','}','/', 217 'I','n','s','t','a','n','c','e','D','a','t','a','/', 218 'R','e','s','e','t','T','a','b','l','e',0 219 }; 220 static const WCHAR _CHMU_LZXC_CONTROLDATA[] = { 221 ':',':','D','a','t','a','S','p','a','c','e','/', 222 'S','t','o','r','a','g','e','/', 223 'M','S','C','o','m','p','r','e','s','s','e','d','/', 224 'C','o','n','t','r','o','l','D','a','t','a',0 225 }; 226 static const WCHAR _CHMU_CONTENT[] = { 227 ':',':','D','a','t','a','S','p','a','c','e','/', 228 'S','t','o','r','a','g','e','/', 229 'M','S','C','o','m','p','r','e','s','s','e','d','/', 230 'C','o','n','t','e','n','t',0 231 }; 232 233 /* 234 * structures local to this module 235 */ 236 237 /* structure of ITSF headers */ 238 #define _CHM_ITSF_V2_LEN (0x58) 239 #define _CHM_ITSF_V3_LEN (0x60) 240 struct chmItsfHeader 241 { 242 char signature[4]; /* 0 (ITSF) */ 243 Int32 version; /* 4 */ 244 Int32 header_len; /* 8 */ 245 Int32 unknown_000c; /* c */ 246 UInt32 last_modified; /* 10 */ 247 UInt32 lang_id; /* 14 */ 248 UChar dir_uuid[16]; /* 18 */ 249 UChar stream_uuid[16]; /* 28 */ 250 UInt64 unknown_offset; /* 38 */ 251 UInt64 unknown_len; /* 40 */ 252 UInt64 dir_offset; /* 48 */ 253 UInt64 dir_len; /* 50 */ 254 UInt64 data_offset; /* 58 (Not present before V3) */ 255 }; /* __attribute__ ((aligned (1))); */ 256 257 static BOOL _unmarshal_itsf_header(unsigned char **pData, 258 unsigned int *pDataLen, 259 struct chmItsfHeader *dest) 260 { 261 /* we only know how to deal with the 0x58 and 0x60 byte structures */ 262 if (*pDataLen != _CHM_ITSF_V2_LEN && *pDataLen != _CHM_ITSF_V3_LEN) 263 return FALSE; 264 265 /* unmarshal common fields */ 266 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 267 _unmarshal_int32 (pData, pDataLen, &dest->version); 268 _unmarshal_int32 (pData, pDataLen, &dest->header_len); 269 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c); 270 _unmarshal_uint32 (pData, pDataLen, &dest->last_modified); 271 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id); 272 _unmarshal_uuid (pData, pDataLen, dest->dir_uuid); 273 _unmarshal_uuid (pData, pDataLen, dest->stream_uuid); 274 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_offset); 275 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_len); 276 _unmarshal_uint64 (pData, pDataLen, &dest->dir_offset); 277 _unmarshal_uint64 (pData, pDataLen, &dest->dir_len); 278 279 /* error check the data */ 280 /* XXX: should also check UUIDs, probably, though with a version 3 file, 281 * current MS tools do not seem to use them. 282 */ 283 if (memcmp(dest->signature, "ITSF", 4) != 0) 284 return FALSE; 285 if (dest->version == 2) 286 { 287 if (dest->header_len < _CHM_ITSF_V2_LEN) 288 return FALSE; 289 } 290 else if (dest->version == 3) 291 { 292 if (dest->header_len < _CHM_ITSF_V3_LEN) 293 return FALSE; 294 } 295 else 296 return FALSE; 297 298 /* now, if we have a V3 structure, unmarshal the rest. 299 * otherwise, compute it 300 */ 301 if (dest->version == 3) 302 { 303 if (*pDataLen != 0) 304 _unmarshal_uint64(pData, pDataLen, &dest->data_offset); 305 else 306 return FALSE; 307 } 308 else 309 dest->data_offset = dest->dir_offset + dest->dir_len; 310 311 return TRUE; 312 } 313 314 /* structure of ITSP headers */ 315 #define _CHM_ITSP_V1_LEN (0x54) 316 struct chmItspHeader 317 { 318 char signature[4]; /* 0 (ITSP) */ 319 Int32 version; /* 4 */ 320 Int32 header_len; /* 8 */ 321 Int32 unknown_000c; /* c */ 322 UInt32 block_len; /* 10 */ 323 Int32 blockidx_intvl; /* 14 */ 324 Int32 index_depth; /* 18 */ 325 Int32 index_root; /* 1c */ 326 Int32 index_head; /* 20 */ 327 Int32 unknown_0024; /* 24 */ 328 UInt32 num_blocks; /* 28 */ 329 Int32 unknown_002c; /* 2c */ 330 UInt32 lang_id; /* 30 */ 331 UChar system_uuid[16]; /* 34 */ 332 UChar unknown_0044[16]; /* 44 */ 333 }; /* __attribute__ ((aligned (1))); */ 334 335 static BOOL _unmarshal_itsp_header(unsigned char **pData, 336 unsigned int *pDataLen, 337 struct chmItspHeader *dest) 338 { 339 /* we only know how to deal with a 0x54 byte structures */ 340 if (*pDataLen != _CHM_ITSP_V1_LEN) 341 return FALSE; 342 343 /* unmarshal fields */ 344 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 345 _unmarshal_int32 (pData, pDataLen, &dest->version); 346 _unmarshal_int32 (pData, pDataLen, &dest->header_len); 347 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c); 348 _unmarshal_uint32 (pData, pDataLen, &dest->block_len); 349 _unmarshal_int32 (pData, pDataLen, &dest->blockidx_intvl); 350 _unmarshal_int32 (pData, pDataLen, &dest->index_depth); 351 _unmarshal_int32 (pData, pDataLen, &dest->index_root); 352 _unmarshal_int32 (pData, pDataLen, &dest->index_head); 353 _unmarshal_int32 (pData, pDataLen, &dest->unknown_0024); 354 _unmarshal_uint32 (pData, pDataLen, &dest->num_blocks); 355 _unmarshal_int32 (pData, pDataLen, &dest->unknown_002c); 356 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id); 357 _unmarshal_uuid (pData, pDataLen, dest->system_uuid); 358 _unmarshal_uchar_array(pData, pDataLen, dest->unknown_0044, 16); 359 360 /* error check the data */ 361 if (memcmp(dest->signature, "ITSP", 4) != 0) 362 return FALSE; 363 if (dest->version != 1) 364 return FALSE; 365 if (dest->header_len != _CHM_ITSP_V1_LEN) 366 return FALSE; 367 368 return TRUE; 369 } 370 371 /* structure of PMGL headers */ 372 static const char _chm_pmgl_marker[4] = "PMGL"; 373 #define _CHM_PMGL_LEN (0x14) 374 struct chmPmglHeader 375 { 376 char signature[4]; /* 0 (PMGL) */ 377 UInt32 free_space; /* 4 */ 378 UInt32 unknown_0008; /* 8 */ 379 Int32 block_prev; /* c */ 380 Int32 block_next; /* 10 */ 381 }; /* __attribute__ ((aligned (1))); */ 382 383 static BOOL _unmarshal_pmgl_header(unsigned char **pData, 384 unsigned int *pDataLen, 385 struct chmPmglHeader *dest) 386 { 387 /* we only know how to deal with a 0x14 byte structures */ 388 if (*pDataLen != _CHM_PMGL_LEN) 389 return FALSE; 390 391 /* unmarshal fields */ 392 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 393 _unmarshal_uint32 (pData, pDataLen, &dest->free_space); 394 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_0008); 395 _unmarshal_int32 (pData, pDataLen, &dest->block_prev); 396 _unmarshal_int32 (pData, pDataLen, &dest->block_next); 397 398 /* check structure */ 399 if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0) 400 return FALSE; 401 402 return TRUE; 403 } 404 405 /* structure of PMGI headers */ 406 static const char _chm_pmgi_marker[4] = "PMGI"; 407 #define _CHM_PMGI_LEN (0x08) 408 struct chmPmgiHeader 409 { 410 char signature[4]; /* 0 (PMGI) */ 411 UInt32 free_space; /* 4 */ 412 }; /* __attribute__ ((aligned (1))); */ 413 414 static BOOL _unmarshal_pmgi_header(unsigned char **pData, 415 unsigned int *pDataLen, 416 struct chmPmgiHeader *dest) 417 { 418 /* we only know how to deal with a 0x8 byte structures */ 419 if (*pDataLen != _CHM_PMGI_LEN) 420 return FALSE; 421 422 /* unmarshal fields */ 423 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 424 _unmarshal_uint32 (pData, pDataLen, &dest->free_space); 425 426 /* check structure */ 427 if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0) 428 return FALSE; 429 430 return TRUE; 431 } 432 433 /* structure of LZXC reset table */ 434 #define _CHM_LZXC_RESETTABLE_V1_LEN (0x28) 435 struct chmLzxcResetTable 436 { 437 UInt32 version; 438 UInt32 block_count; 439 UInt32 unknown; 440 UInt32 table_offset; 441 UInt64 uncompressed_len; 442 UInt64 compressed_len; 443 UInt64 block_len; 444 }; /* __attribute__ ((aligned (1))); */ 445 446 static BOOL _unmarshal_lzxc_reset_table(unsigned char **pData, 447 unsigned int *pDataLen, 448 struct chmLzxcResetTable *dest) 449 { 450 /* we only know how to deal with a 0x28 byte structures */ 451 if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN) 452 return FALSE; 453 454 /* unmarshal fields */ 455 _unmarshal_uint32 (pData, pDataLen, &dest->version); 456 _unmarshal_uint32 (pData, pDataLen, &dest->block_count); 457 _unmarshal_uint32 (pData, pDataLen, &dest->unknown); 458 _unmarshal_uint32 (pData, pDataLen, &dest->table_offset); 459 _unmarshal_uint64 (pData, pDataLen, &dest->uncompressed_len); 460 _unmarshal_uint64 (pData, pDataLen, &dest->compressed_len); 461 _unmarshal_uint64 (pData, pDataLen, &dest->block_len); 462 463 /* check structure */ 464 if (dest->version != 2) 465 return FALSE; 466 467 return TRUE; 468 } 469 470 /* structure of LZXC control data block */ 471 #define _CHM_LZXC_MIN_LEN (0x18) 472 #define _CHM_LZXC_V2_LEN (0x1c) 473 struct chmLzxcControlData 474 { 475 UInt32 size; /* 0 */ 476 char signature[4]; /* 4 (LZXC) */ 477 UInt32 version; /* 8 */ 478 UInt32 resetInterval; /* c */ 479 UInt32 windowSize; /* 10 */ 480 UInt32 windowsPerReset; /* 14 */ 481 UInt32 unknown_18; /* 18 */ 482 }; 483 484 static BOOL _unmarshal_lzxc_control_data(unsigned char **pData, 485 unsigned int *pDataLen, 486 struct chmLzxcControlData *dest) 487 { 488 /* we want at least 0x18 bytes */ 489 if (*pDataLen < _CHM_LZXC_MIN_LEN) 490 return FALSE; 491 492 /* unmarshal fields */ 493 _unmarshal_uint32 (pData, pDataLen, &dest->size); 494 _unmarshal_char_array(pData, pDataLen, dest->signature, 4); 495 _unmarshal_uint32 (pData, pDataLen, &dest->version); 496 _unmarshal_uint32 (pData, pDataLen, &dest->resetInterval); 497 _unmarshal_uint32 (pData, pDataLen, &dest->windowSize); 498 _unmarshal_uint32 (pData, pDataLen, &dest->windowsPerReset); 499 500 if (*pDataLen >= _CHM_LZXC_V2_LEN) 501 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_18); 502 else 503 dest->unknown_18 = 0; 504 505 if (dest->version == 2) 506 { 507 dest->resetInterval *= 0x8000; 508 dest->windowSize *= 0x8000; 509 } 510 if (dest->windowSize == 0 || dest->resetInterval == 0) 511 return FALSE; 512 513 /* for now, only support resetInterval a multiple of windowSize/2 */ 514 if (dest->windowSize == 1) 515 return FALSE; 516 if ((dest->resetInterval % (dest->windowSize/2)) != 0) 517 return FALSE; 518 519 /* check structure */ 520 if (memcmp(dest->signature, "LZXC", 4) != 0) 521 return FALSE; 522 523 return TRUE; 524 } 525 526 /* the structure used for chm file handles */ 527 struct chmFile 528 { 529 HANDLE fd; 530 531 CRITICAL_SECTION mutex; 532 CRITICAL_SECTION lzx_mutex; 533 CRITICAL_SECTION cache_mutex; 534 535 UInt64 dir_offset; 536 UInt64 dir_len; 537 UInt64 data_offset; 538 Int32 index_root; 539 Int32 index_head; 540 UInt32 block_len; 541 542 UInt64 span; 543 struct chmUnitInfo rt_unit; 544 struct chmUnitInfo cn_unit; 545 struct chmLzxcResetTable reset_table; 546 547 /* LZX control data */ 548 int compression_enabled; 549 UInt32 window_size; 550 UInt32 reset_interval; 551 UInt32 reset_blkcount; 552 553 /* decompressor state */ 554 struct LZXstate *lzx_state; 555 int lzx_last_block; 556 557 /* cache for decompressed blocks */ 558 UChar **cache_blocks; 559 Int64 *cache_block_indices; 560 Int32 cache_num_blocks; 561 }; 562 563 /* 564 * utility functions local to this module 565 */ 566 567 /* utility function to handle differences between {pread,read}(64)? */ 568 static Int64 _chm_fetch_bytes(struct chmFile *h, 569 UChar *buf, 570 UInt64 os, 571 Int64 len) 572 { 573 Int64 readLen=0; 574 if (h->fd == CHM_NULL_FD) 575 return readLen; 576 577 CHM_ACQUIRE_LOCK(h->mutex); 578 /* NOTE: this might be better done with CreateFileMapping, et cetera... */ 579 { 580 LARGE_INTEGER old_pos, new_pos; 581 DWORD actualLen=0; 582 583 /* awkward Win32 Seek/Tell */ 584 new_pos.QuadPart = 0; 585 SetFilePointerEx( h->fd, new_pos, &old_pos, FILE_CURRENT ); 586 new_pos.QuadPart = os; 587 SetFilePointerEx( h->fd, new_pos, NULL, FILE_BEGIN ); 588 589 /* read the data */ 590 if (ReadFile(h->fd, 591 buf, 592 (DWORD)len, 593 &actualLen, 594 NULL)) 595 readLen = actualLen; 596 else 597 readLen = 0; 598 599 /* restore original position */ 600 SetFilePointerEx( h->fd, old_pos, NULL, FILE_BEGIN ); 601 } 602 CHM_RELEASE_LOCK(h->mutex); 603 return readLen; 604 } 605 606 /* 607 * set a parameter on the file handle. 608 * valid parameter types: 609 * CHM_PARAM_MAX_BLOCKS_CACHED: 610 * how many decompressed blocks should be cached? A simple 611 * caching scheme is used, wherein the index of the block is 612 * used as a hash value, and hash collision results in the 613 * invalidation of the previously cached block. 614 */ 615 static void chm_set_param(struct chmFile *h, 616 int paramType, 617 int paramVal) 618 { 619 switch (paramType) 620 { 621 case CHM_PARAM_MAX_BLOCKS_CACHED: 622 CHM_ACQUIRE_LOCK(h->cache_mutex); 623 if (paramVal != h->cache_num_blocks) 624 { 625 UChar **newBlocks; 626 Int64 *newIndices; 627 int i; 628 629 /* allocate new cached blocks */ 630 newBlocks = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UChar *)); 631 newIndices = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UInt64)); 632 for (i=0; i<paramVal; i++) 633 { 634 newBlocks[i] = NULL; 635 newIndices[i] = 0; 636 } 637 638 /* re-distribute old cached blocks */ 639 if (h->cache_blocks) 640 { 641 for (i=0; i<h->cache_num_blocks; i++) 642 { 643 int newSlot = (int)(h->cache_block_indices[i] % paramVal); 644 645 if (h->cache_blocks[i]) 646 { 647 /* in case of collision, destroy newcomer */ 648 if (newBlocks[newSlot]) 649 { 650 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]); 651 h->cache_blocks[i] = NULL; 652 } 653 else 654 { 655 newBlocks[newSlot] = h->cache_blocks[i]; 656 newIndices[newSlot] = 657 h->cache_block_indices[i]; 658 } 659 } 660 } 661 662 HeapFree(GetProcessHeap(), 0, h->cache_blocks); 663 HeapFree(GetProcessHeap(), 0, h->cache_block_indices); 664 } 665 666 /* now, set new values */ 667 h->cache_blocks = newBlocks; 668 h->cache_block_indices = newIndices; 669 h->cache_num_blocks = paramVal; 670 } 671 CHM_RELEASE_LOCK(h->cache_mutex); 672 break; 673 674 default: 675 break; 676 } 677 } 678 679 /* open an ITS archive */ 680 struct chmFile *chm_openW(const WCHAR *filename) 681 { 682 unsigned char sbuffer[256]; 683 unsigned int sremain; 684 unsigned char *sbufpos; 685 struct chmFile *newHandle=NULL; 686 struct chmItsfHeader itsfHeader; 687 struct chmItspHeader itspHeader; 688 #if 0 689 struct chmUnitInfo uiSpan; 690 #endif 691 struct chmUnitInfo uiLzxc; 692 struct chmLzxcControlData ctlData; 693 694 /* allocate handle */ 695 newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile)); 696 newHandle->fd = CHM_NULL_FD; 697 newHandle->lzx_state = NULL; 698 newHandle->cache_blocks = NULL; 699 newHandle->cache_block_indices = NULL; 700 newHandle->cache_num_blocks = 0; 701 702 /* open file */ 703 if ((newHandle->fd=CreateFileW(filename, 704 GENERIC_READ, 705 FILE_SHARE_READ, 706 NULL, 707 OPEN_EXISTING, 708 FILE_ATTRIBUTE_NORMAL, 709 NULL)) == CHM_NULL_FD) 710 { 711 HeapFree(GetProcessHeap(), 0, newHandle); 712 return NULL; 713 } 714 715 /* initialize mutexes, if needed */ 716 InitializeCriticalSection(&newHandle->mutex); 717 newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex"); 718 InitializeCriticalSection(&newHandle->lzx_mutex); 719 newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex"); 720 InitializeCriticalSection(&newHandle->cache_mutex); 721 newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex"); 722 723 /* read and verify header */ 724 sremain = _CHM_ITSF_V3_LEN; 725 sbufpos = sbuffer; 726 if (_chm_fetch_bytes(newHandle, sbuffer, 0, sremain) != sremain || 727 !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader)) 728 { 729 chm_close(newHandle); 730 return NULL; 731 } 732 733 /* stash important values from header */ 734 newHandle->dir_offset = itsfHeader.dir_offset; 735 newHandle->dir_len = itsfHeader.dir_len; 736 newHandle->data_offset = itsfHeader.data_offset; 737 738 /* now, read and verify the directory header chunk */ 739 sremain = _CHM_ITSP_V1_LEN; 740 sbufpos = sbuffer; 741 if (_chm_fetch_bytes(newHandle, sbuffer, 742 itsfHeader.dir_offset, sremain) != sremain || 743 !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader)) 744 { 745 chm_close(newHandle); 746 return NULL; 747 } 748 749 /* grab essential information from ITSP header */ 750 newHandle->dir_offset += itspHeader.header_len; 751 newHandle->dir_len -= itspHeader.header_len; 752 newHandle->index_root = itspHeader.index_root; 753 newHandle->index_head = itspHeader.index_head; 754 newHandle->block_len = itspHeader.block_len; 755 756 /* if the index root is -1, this means we don't have any PMGI blocks. 757 * as a result, we must use the sole PMGL block as the index root 758 */ 759 if (newHandle->index_root == -1) 760 newHandle->index_root = newHandle->index_head; 761 762 /* initialize cache */ 763 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED, 764 CHM_MAX_BLOCKS_CACHED); 765 766 /* By default, compression is enabled. */ 767 newHandle->compression_enabled = 1; 768 769 /* prefetch most commonly needed unit infos */ 770 if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, 771 _CHMU_RESET_TABLE, 772 &newHandle->rt_unit) || 773 newHandle->rt_unit.space == CHM_COMPRESSED || 774 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, 775 _CHMU_CONTENT, 776 &newHandle->cn_unit) || 777 newHandle->cn_unit.space == CHM_COMPRESSED || 778 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle, 779 _CHMU_LZXC_CONTROLDATA, 780 &uiLzxc) || 781 uiLzxc.space == CHM_COMPRESSED) 782 { 783 newHandle->compression_enabled = 0; 784 } 785 786 /* read reset table info */ 787 if (newHandle->compression_enabled) 788 { 789 sremain = _CHM_LZXC_RESETTABLE_V1_LEN; 790 sbufpos = sbuffer; 791 if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer, 792 0, sremain) != sremain || 793 !_unmarshal_lzxc_reset_table(&sbufpos, &sremain, 794 &newHandle->reset_table)) 795 { 796 newHandle->compression_enabled = 0; 797 } 798 } 799 800 /* read control data */ 801 if (newHandle->compression_enabled) 802 { 803 sremain = (unsigned long)uiLzxc.length; 804 sbufpos = sbuffer; 805 if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer, 806 0, sremain) != sremain || 807 !_unmarshal_lzxc_control_data(&sbufpos, &sremain, 808 &ctlData)) 809 { 810 newHandle->compression_enabled = 0; 811 } 812 813 newHandle->window_size = ctlData.windowSize; 814 newHandle->reset_interval = ctlData.resetInterval; 815 816 /* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */ 817 /* must be multiplied by this formerly unknown ctrl data field in */ 818 /* order to decompress some files. */ 819 #if 0 820 newHandle->reset_blkcount = newHandle->reset_interval / 821 (newHandle->window_size / 2); 822 #else 823 newHandle->reset_blkcount = newHandle->reset_interval / 824 (newHandle->window_size / 2) * 825 ctlData.windowsPerReset; 826 #endif 827 } 828 829 return newHandle; 830 } 831 832 /* Duplicate an ITS archive handle */ 833 struct chmFile *chm_dup(struct chmFile *oldHandle) 834 { 835 struct chmFile *newHandle=NULL; 836 837 newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile)); 838 *newHandle = *oldHandle; 839 840 /* duplicate fd handle */ 841 DuplicateHandle(GetCurrentProcess(), oldHandle->fd, 842 GetCurrentProcess(), &(newHandle->fd), 843 0, FALSE, DUPLICATE_SAME_ACCESS); 844 newHandle->lzx_state = NULL; 845 newHandle->cache_blocks = NULL; 846 newHandle->cache_block_indices = NULL; 847 newHandle->cache_num_blocks = 0; 848 849 /* initialize mutexes, if needed */ 850 InitializeCriticalSection(&newHandle->mutex); 851 newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex"); 852 InitializeCriticalSection(&newHandle->lzx_mutex); 853 newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex"); 854 InitializeCriticalSection(&newHandle->cache_mutex); 855 newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex"); 856 857 /* initialize cache */ 858 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED, 859 CHM_MAX_BLOCKS_CACHED); 860 861 return newHandle; 862 } 863 864 /* close an ITS archive */ 865 void chm_close(struct chmFile *h) 866 { 867 if (h != NULL) 868 { 869 if (h->fd != CHM_NULL_FD) 870 CHM_CLOSE_FILE(h->fd); 871 h->fd = CHM_NULL_FD; 872 873 h->mutex.DebugInfo->Spare[0] = 0; 874 DeleteCriticalSection(&h->mutex); 875 h->lzx_mutex.DebugInfo->Spare[0] = 0; 876 DeleteCriticalSection(&h->lzx_mutex); 877 h->cache_mutex.DebugInfo->Spare[0] = 0; 878 DeleteCriticalSection(&h->cache_mutex); 879 880 if (h->lzx_state) 881 LZXteardown(h->lzx_state); 882 h->lzx_state = NULL; 883 884 if (h->cache_blocks) 885 { 886 int i; 887 for (i=0; i<h->cache_num_blocks; i++) 888 { 889 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]); 890 } 891 HeapFree(GetProcessHeap(), 0, h->cache_blocks); 892 h->cache_blocks = NULL; 893 } 894 895 HeapFree(GetProcessHeap(), 0, h->cache_block_indices); 896 h->cache_block_indices = NULL; 897 898 HeapFree(GetProcessHeap(), 0, h); 899 } 900 } 901 902 /* 903 * helper methods for chm_resolve_object 904 */ 905 906 /* skip a compressed dword */ 907 static void _chm_skip_cword(UChar **pEntry) 908 { 909 while (*(*pEntry)++ >= 0x80) 910 ; 911 } 912 913 /* skip the data from a PMGL entry */ 914 static void _chm_skip_PMGL_entry_data(UChar **pEntry) 915 { 916 _chm_skip_cword(pEntry); 917 _chm_skip_cword(pEntry); 918 _chm_skip_cword(pEntry); 919 } 920 921 /* parse a compressed dword */ 922 static UInt64 _chm_parse_cword(UChar **pEntry) 923 { 924 UInt64 accum = 0; 925 UChar temp; 926 while ((temp=*(*pEntry)++) >= 0x80) 927 { 928 accum <<= 7; 929 accum += temp & 0x7f; 930 } 931 932 return (accum << 7) + temp; 933 } 934 935 /* parse a utf-8 string into an ASCII char buffer */ 936 static BOOL _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path) 937 { 938 DWORD length = MultiByteToWideChar(CP_UTF8, 0, (char *)*pEntry, count, path, CHM_MAX_PATHLEN); 939 path[length] = '\0'; 940 *pEntry += count; 941 return !!length; 942 } 943 944 /* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */ 945 static BOOL _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui) 946 { 947 UInt64 strLen; 948 949 /* parse str len */ 950 strLen = _chm_parse_cword(pEntry); 951 if (strLen > CHM_MAX_PATHLEN) 952 return FALSE; 953 954 /* parse path */ 955 if (! _chm_parse_UTF8(pEntry, strLen, ui->path)) 956 return FALSE; 957 958 /* parse info */ 959 ui->space = (int)_chm_parse_cword(pEntry); 960 ui->start = _chm_parse_cword(pEntry); 961 ui->length = _chm_parse_cword(pEntry); 962 return TRUE; 963 } 964 965 /* find an exact entry in PMGL; return NULL if we fail */ 966 static UChar *_chm_find_in_PMGL(UChar *page_buf, 967 UInt32 block_len, 968 const WCHAR *objPath) 969 { 970 /* XXX: modify this to do a binary search using the nice index structure 971 * that is provided for us. 972 */ 973 struct chmPmglHeader header; 974 UInt32 hremain; 975 UChar *end; 976 UChar *cur; 977 UChar *temp; 978 UInt64 strLen; 979 WCHAR buffer[CHM_MAX_PATHLEN+1]; 980 981 /* figure out where to start and end */ 982 cur = page_buf; 983 hremain = _CHM_PMGL_LEN; 984 if (! _unmarshal_pmgl_header(&cur, &hremain, &header)) 985 return NULL; 986 end = page_buf + block_len - (header.free_space); 987 988 /* now, scan progressively */ 989 while (cur < end) 990 { 991 /* grab the name */ 992 temp = cur; 993 strLen = _chm_parse_cword(&cur); 994 if (! _chm_parse_UTF8(&cur, strLen, buffer)) 995 return NULL; 996 997 /* check if it is the right name */ 998 if (! strcmpiW(buffer, objPath)) 999 return temp; 1000 1001 _chm_skip_PMGL_entry_data(&cur); 1002 } 1003 1004 return NULL; 1005 } 1006 1007 /* find which block should be searched next for the entry; -1 if no block */ 1008 static Int32 _chm_find_in_PMGI(UChar *page_buf, 1009 UInt32 block_len, 1010 const WCHAR *objPath) 1011 { 1012 /* XXX: modify this to do a binary search using the nice index structure 1013 * that is provided for us 1014 */ 1015 struct chmPmgiHeader header; 1016 UInt32 hremain; 1017 int page=-1; 1018 UChar *end; 1019 UChar *cur; 1020 UInt64 strLen; 1021 WCHAR buffer[CHM_MAX_PATHLEN+1]; 1022 1023 /* figure out where to start and end */ 1024 cur = page_buf; 1025 hremain = _CHM_PMGI_LEN; 1026 if (! _unmarshal_pmgi_header(&cur, &hremain, &header)) 1027 return -1; 1028 end = page_buf + block_len - (header.free_space); 1029 1030 /* now, scan progressively */ 1031 while (cur < end) 1032 { 1033 /* grab the name */ 1034 strLen = _chm_parse_cword(&cur); 1035 if (! _chm_parse_UTF8(&cur, strLen, buffer)) 1036 return -1; 1037 1038 /* check if it is the right name */ 1039 if (strcmpiW(buffer, objPath) > 0) 1040 return page; 1041 1042 /* load next value for path */ 1043 page = (int)_chm_parse_cword(&cur); 1044 } 1045 1046 return page; 1047 } 1048 1049 /* resolve a particular object from the archive */ 1050 int chm_resolve_object(struct chmFile *h, 1051 const WCHAR *objPath, 1052 struct chmUnitInfo *ui) 1053 { 1054 /* 1055 * XXX: implement caching scheme for dir pages 1056 */ 1057 1058 Int32 curPage; 1059 1060 /* buffer to hold whatever page we're looking at */ 1061 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len); 1062 1063 /* starting page */ 1064 curPage = h->index_root; 1065 1066 /* until we have either returned or given up */ 1067 while (curPage != -1) 1068 { 1069 1070 /* try to fetch the index page */ 1071 if (_chm_fetch_bytes(h, page_buf, 1072 h->dir_offset + (UInt64)curPage*h->block_len, 1073 h->block_len) != h->block_len) 1074 { 1075 HeapFree(GetProcessHeap(), 0, page_buf); 1076 return CHM_RESOLVE_FAILURE; 1077 } 1078 1079 /* now, if it is a leaf node: */ 1080 if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0) 1081 { 1082 /* scan block */ 1083 UChar *pEntry = _chm_find_in_PMGL(page_buf, 1084 h->block_len, 1085 objPath); 1086 if (pEntry == NULL) 1087 { 1088 HeapFree(GetProcessHeap(), 0, page_buf); 1089 return CHM_RESOLVE_FAILURE; 1090 } 1091 1092 /* parse entry and return */ 1093 _chm_parse_PMGL_entry(&pEntry, ui); 1094 HeapFree(GetProcessHeap(), 0, page_buf); 1095 return CHM_RESOLVE_SUCCESS; 1096 } 1097 1098 /* else, if it is a branch node: */ 1099 else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0) 1100 curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath); 1101 1102 /* else, we are confused. give up. */ 1103 else 1104 { 1105 HeapFree(GetProcessHeap(), 0, page_buf); 1106 return CHM_RESOLVE_FAILURE; 1107 } 1108 } 1109 1110 /* didn't find anything. fail. */ 1111 HeapFree(GetProcessHeap(), 0, page_buf); 1112 return CHM_RESOLVE_FAILURE; 1113 } 1114 1115 /* 1116 * utility methods for dealing with compressed data 1117 */ 1118 1119 /* get the bounds of a compressed block. Returns FALSE on failure */ 1120 static BOOL _chm_get_cmpblock_bounds(struct chmFile *h, 1121 UInt64 block, 1122 UInt64 *start, 1123 Int64 *len) 1124 { 1125 UChar buffer[8], *dummy; 1126 UInt32 remain; 1127 1128 /* for all but the last block, use the reset table */ 1129 if (block < h->reset_table.block_count-1) 1130 { 1131 /* unpack the start address */ 1132 dummy = buffer; 1133 remain = 8; 1134 if (_chm_fetch_bytes(h, buffer, 1135 h->data_offset 1136 + h->rt_unit.start 1137 + h->reset_table.table_offset 1138 + block*8, 1139 remain) != remain || 1140 !_unmarshal_uint64(&dummy, &remain, start)) 1141 return FALSE; 1142 1143 /* unpack the end address */ 1144 dummy = buffer; 1145 remain = 8; 1146 if (_chm_fetch_bytes(h, buffer, 1147 h->data_offset 1148 + h->rt_unit.start 1149 + h->reset_table.table_offset 1150 + block*8 + 8, 1151 remain) != remain || 1152 !_unmarshal_int64(&dummy, &remain, len)) 1153 return FALSE; 1154 } 1155 1156 /* for the last block, use the span in addition to the reset table */ 1157 else 1158 { 1159 /* unpack the start address */ 1160 dummy = buffer; 1161 remain = 8; 1162 if (_chm_fetch_bytes(h, buffer, 1163 h->data_offset 1164 + h->rt_unit.start 1165 + h->reset_table.table_offset 1166 + block*8, 1167 remain) != remain || 1168 !_unmarshal_uint64(&dummy, &remain, start)) 1169 return FALSE; 1170 1171 *len = h->reset_table.compressed_len; 1172 } 1173 1174 /* compute the length and absolute start address */ 1175 *len -= *start; 1176 *start += h->data_offset + h->cn_unit.start; 1177 1178 return TRUE; 1179 } 1180 1181 /* decompress the block. must have lzx_mutex. */ 1182 static Int64 _chm_decompress_block(struct chmFile *h, 1183 UInt64 block, 1184 UChar **ubuffer) 1185 { 1186 UChar *cbuffer = HeapAlloc( GetProcessHeap(), 0, 1187 ((unsigned int)h->reset_table.block_len + 6144)); 1188 UInt64 cmpStart; /* compressed start */ 1189 Int64 cmpLen; /* compressed len */ 1190 int indexSlot; /* cache index slot */ 1191 UChar *lbuffer; /* local buffer ptr */ 1192 UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset interval align */ 1193 UInt32 i; /* local loop index */ 1194 1195 /* let the caching system pull its weight! */ 1196 if (block - blockAlign <= h->lzx_last_block && 1197 block >= h->lzx_last_block) 1198 blockAlign = (block - h->lzx_last_block); 1199 1200 /* check if we need previous blocks */ 1201 if (blockAlign != 0) 1202 { 1203 /* fetch all required previous blocks since last reset */ 1204 for (i = blockAlign; i > 0; i--) 1205 { 1206 UInt32 curBlockIdx = block - i; 1207 1208 /* check if we most recently decompressed the previous block */ 1209 if (h->lzx_last_block != curBlockIdx) 1210 { 1211 if ((curBlockIdx % h->reset_blkcount) == 0) 1212 { 1213 #ifdef CHM_DEBUG 1214 fprintf(stderr, "***RESET (1)***\n"); 1215 #endif 1216 LZXreset(h->lzx_state); 1217 } 1218 1219 indexSlot = (int)((curBlockIdx) % h->cache_num_blocks); 1220 h->cache_block_indices[indexSlot] = curBlockIdx; 1221 if (! h->cache_blocks[indexSlot]) 1222 h->cache_blocks[indexSlot] = 1223 HeapAlloc(GetProcessHeap(), 0, 1224 (unsigned int)(h->reset_table.block_len)); 1225 lbuffer = h->cache_blocks[indexSlot]; 1226 1227 /* decompress the previous block */ 1228 #ifdef CHM_DEBUG 1229 fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx); 1230 #endif 1231 if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) || 1232 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || 1233 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, 1234 (int)h->reset_table.block_len) != DECR_OK) 1235 { 1236 #ifdef CHM_DEBUG 1237 fprintf(stderr, " (DECOMPRESS FAILED!)\n"); 1238 #endif 1239 HeapFree(GetProcessHeap(), 0, cbuffer); 1240 return 0; 1241 } 1242 1243 h->lzx_last_block = (int)curBlockIdx; 1244 } 1245 } 1246 } 1247 else 1248 { 1249 if ((block % h->reset_blkcount) == 0) 1250 { 1251 #ifdef CHM_DEBUG 1252 fprintf(stderr, "***RESET (2)***\n"); 1253 #endif 1254 LZXreset(h->lzx_state); 1255 } 1256 } 1257 1258 /* allocate slot in cache */ 1259 indexSlot = (int)(block % h->cache_num_blocks); 1260 h->cache_block_indices[indexSlot] = block; 1261 if (! h->cache_blocks[indexSlot]) 1262 h->cache_blocks[indexSlot] = 1263 HeapAlloc(GetProcessHeap(), 0, ((unsigned int)h->reset_table.block_len)); 1264 lbuffer = h->cache_blocks[indexSlot]; 1265 *ubuffer = lbuffer; 1266 1267 /* decompress the block we actually want */ 1268 #ifdef CHM_DEBUG 1269 fprintf(stderr, "Decompressing block #%4d (REAL )\n", block); 1270 #endif 1271 if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) || 1272 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen || 1273 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen, 1274 (int)h->reset_table.block_len) != DECR_OK) 1275 { 1276 #ifdef CHM_DEBUG 1277 fprintf(stderr, " (DECOMPRESS FAILED!)\n"); 1278 #endif 1279 HeapFree(GetProcessHeap(), 0, cbuffer); 1280 return 0; 1281 } 1282 h->lzx_last_block = (int)block; 1283 1284 /* XXX: modify LZX routines to return the length of the data they 1285 * decompressed and return that instead, for an extra sanity check. 1286 */ 1287 HeapFree(GetProcessHeap(), 0, cbuffer); 1288 return h->reset_table.block_len; 1289 } 1290 1291 /* grab a region from a compressed block */ 1292 static Int64 _chm_decompress_region(struct chmFile *h, 1293 UChar *buf, 1294 UInt64 start, 1295 Int64 len) 1296 { 1297 UInt64 nBlock, nOffset; 1298 UInt64 nLen; 1299 UInt64 gotLen; 1300 UChar *ubuffer = NULL; 1301 1302 if (len <= 0) 1303 return 0; 1304 1305 /* figure out what we need to read */ 1306 nBlock = start / h->reset_table.block_len; 1307 nOffset = start % h->reset_table.block_len; 1308 nLen = len; 1309 if (nLen > (h->reset_table.block_len - nOffset)) 1310 nLen = h->reset_table.block_len - nOffset; 1311 1312 /* if block is cached, return data from it. */ 1313 CHM_ACQUIRE_LOCK(h->lzx_mutex); 1314 CHM_ACQUIRE_LOCK(h->cache_mutex); 1315 if (h->cache_block_indices[nBlock % h->cache_num_blocks] == nBlock && 1316 h->cache_blocks[nBlock % h->cache_num_blocks] != NULL) 1317 { 1318 memcpy(buf, 1319 h->cache_blocks[nBlock % h->cache_num_blocks] + nOffset, 1320 (unsigned int)nLen); 1321 CHM_RELEASE_LOCK(h->cache_mutex); 1322 CHM_RELEASE_LOCK(h->lzx_mutex); 1323 return nLen; 1324 } 1325 CHM_RELEASE_LOCK(h->cache_mutex); 1326 1327 /* data request not satisfied, so... start up the decompressor machine */ 1328 if (! h->lzx_state) 1329 { 1330 int window_size = ffs(h->window_size) - 1; 1331 h->lzx_last_block = -1; 1332 h->lzx_state = LZXinit(window_size); 1333 } 1334 1335 /* decompress some data */ 1336 gotLen = _chm_decompress_block(h, nBlock, &ubuffer); 1337 if (gotLen < nLen) 1338 nLen = gotLen; 1339 memcpy(buf, ubuffer+nOffset, (unsigned int)nLen); 1340 CHM_RELEASE_LOCK(h->lzx_mutex); 1341 return nLen; 1342 } 1343 1344 /* retrieve (part of) an object */ 1345 LONGINT64 chm_retrieve_object(struct chmFile *h, 1346 struct chmUnitInfo *ui, 1347 unsigned char *buf, 1348 LONGUINT64 addr, 1349 LONGINT64 len) 1350 { 1351 /* must be valid file handle */ 1352 if (h == NULL) 1353 return 0; 1354 1355 /* starting address must be in correct range */ 1356 if (addr >= ui->length) 1357 return 0; 1358 1359 /* clip length */ 1360 if (addr + len > ui->length) 1361 len = ui->length - addr; 1362 1363 /* if the file is uncompressed, it's simple */ 1364 if (ui->space == CHM_UNCOMPRESSED) 1365 { 1366 /* read data */ 1367 return _chm_fetch_bytes(h, 1368 buf, 1369 h->data_offset + ui->start + addr, 1370 len); 1371 } 1372 1373 /* else if the file is compressed, it's a little trickier */ 1374 else /* ui->space == CHM_COMPRESSED */ 1375 { 1376 Int64 swath=0, total=0; 1377 1378 /* if compression is not enabled for this file... */ 1379 if (! h->compression_enabled) 1380 return total; 1381 1382 do { 1383 1384 /* swill another mouthful */ 1385 swath = _chm_decompress_region(h, buf, ui->start + addr, len); 1386 1387 /* if we didn't get any... */ 1388 if (swath == 0) 1389 return total; 1390 1391 /* update stats */ 1392 total += swath; 1393 len -= swath; 1394 addr += swath; 1395 buf += swath; 1396 1397 } while (len != 0); 1398 1399 return total; 1400 } 1401 } 1402 1403 BOOL chm_enumerate_dir(struct chmFile *h, 1404 const WCHAR *prefix, 1405 int what, 1406 CHM_ENUMERATOR e, 1407 void *context) 1408 { 1409 /* 1410 * XXX: do this efficiently (i.e. using the tree index) 1411 */ 1412 1413 Int32 curPage; 1414 1415 /* buffer to hold whatever page we're looking at */ 1416 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len); 1417 struct chmPmglHeader header; 1418 UChar *end; 1419 UChar *cur; 1420 unsigned int lenRemain; 1421 1422 /* set to TRUE once we've started */ 1423 BOOL it_has_begun = FALSE; 1424 1425 /* the current ui */ 1426 struct chmUnitInfo ui; 1427 int flag; 1428 UInt64 ui_path_len; 1429 1430 /* the length of the prefix */ 1431 WCHAR prefixRectified[CHM_MAX_PATHLEN+1]; 1432 int prefixLen; 1433 WCHAR lastPath[CHM_MAX_PATHLEN]; 1434 int lastPathLen; 1435 1436 /* starting page */ 1437 curPage = h->index_head; 1438 1439 /* initialize pathname state */ 1440 lstrcpynW(prefixRectified, prefix, CHM_MAX_PATHLEN); 1441 prefixLen = strlenW(prefixRectified); 1442 if (prefixLen != 0) 1443 { 1444 if (prefixRectified[prefixLen-1] != '/') 1445 { 1446 prefixRectified[prefixLen] = '/'; 1447 prefixRectified[prefixLen+1] = '\0'; 1448 ++prefixLen; 1449 } 1450 } 1451 lastPath[0] = '\0'; 1452 lastPathLen = -1; 1453 1454 /* until we have either returned or given up */ 1455 while (curPage != -1) 1456 { 1457 1458 /* try to fetch the index page */ 1459 if (_chm_fetch_bytes(h, 1460 page_buf, 1461 h->dir_offset + (UInt64)curPage*h->block_len, 1462 h->block_len) != h->block_len) 1463 { 1464 HeapFree(GetProcessHeap(), 0, page_buf); 1465 return FALSE; 1466 } 1467 1468 /* figure out start and end for this page */ 1469 cur = page_buf; 1470 lenRemain = _CHM_PMGL_LEN; 1471 if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header)) 1472 { 1473 HeapFree(GetProcessHeap(), 0, page_buf); 1474 return FALSE; 1475 } 1476 end = page_buf + h->block_len - (header.free_space); 1477 1478 /* loop over this page */ 1479 while (cur < end) 1480 { 1481 if (! _chm_parse_PMGL_entry(&cur, &ui)) 1482 { 1483 HeapFree(GetProcessHeap(), 0, page_buf); 1484 return FALSE; 1485 } 1486 1487 /* check if we should start */ 1488 if (! it_has_begun) 1489 { 1490 if (ui.length == 0 && strncmpiW(ui.path, prefixRectified, prefixLen) == 0) 1491 it_has_begun = TRUE; 1492 else 1493 continue; 1494 1495 if (ui.path[prefixLen] == '\0') 1496 continue; 1497 } 1498 1499 /* check if we should stop */ 1500 else 1501 { 1502 if (strncmpiW(ui.path, prefixRectified, prefixLen) != 0) 1503 { 1504 HeapFree(GetProcessHeap(), 0, page_buf); 1505 return TRUE; 1506 } 1507 } 1508 1509 /* check if we should include this path */ 1510 if (lastPathLen != -1) 1511 { 1512 if (strncmpiW(ui.path, lastPath, lastPathLen) == 0) 1513 continue; 1514 } 1515 strcpyW(lastPath, ui.path); 1516 lastPathLen = strlenW(lastPath); 1517 1518 /* get the length of the path */ 1519 ui_path_len = strlenW(ui.path)-1; 1520 1521 /* check for DIRS */ 1522 if (ui.path[ui_path_len] == '/' && !(what & CHM_ENUMERATE_DIRS)) 1523 continue; 1524 1525 /* check for FILES */ 1526 if (ui.path[ui_path_len] != '/' && !(what & CHM_ENUMERATE_FILES)) 1527 continue; 1528 1529 /* check for NORMAL vs. META */ 1530 if (ui.path[0] == '/') 1531 { 1532 1533 /* check for NORMAL vs. SPECIAL */ 1534 if (ui.path[1] == '#' || ui.path[1] == '$') 1535 flag = CHM_ENUMERATE_SPECIAL; 1536 else 1537 flag = CHM_ENUMERATE_NORMAL; 1538 } 1539 else 1540 flag = CHM_ENUMERATE_META; 1541 if (! (what & flag)) 1542 continue; 1543 1544 /* call the enumerator */ 1545 { 1546 int status = (*e)(h, &ui, context); 1547 switch (status) 1548 { 1549 case CHM_ENUMERATOR_FAILURE: 1550 HeapFree(GetProcessHeap(), 0, page_buf); 1551 return FALSE; 1552 case CHM_ENUMERATOR_CONTINUE: 1553 break; 1554 case CHM_ENUMERATOR_SUCCESS: 1555 HeapFree(GetProcessHeap(), 0, page_buf); 1556 return TRUE; 1557 default: 1558 break; 1559 } 1560 } 1561 } 1562 1563 /* advance to next page */ 1564 curPage = header.block_next; 1565 } 1566 1567 HeapFree(GetProcessHeap(), 0, page_buf); 1568 return TRUE; 1569 } 1570