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