mf/session: Forward more events to the application.
[wine/zf.git] / dlls / itss / chm_lib.c
blob91e3fa3d811399c0e2c4fa3b93200a38f220aa8d
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 ***************************************************************************/
33 /***************************************************************************
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.
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.
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
49 ***************************************************************************/
51 /***************************************************************************
52 * *
53 * Adapted for Wine by Mike McCormack *
54 * *
55 ***************************************************************************/
58 #include <stdarg.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
63 #include "windef.h"
64 #include "winbase.h"
65 #include "winnls.h"
67 #include "chm_lib.h"
68 #include "lzx.h"
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)
77 #define CHM_NULL_FD (INVALID_HANDLE_VALUE)
78 #define CHM_CLOSE_FILE(fd) CloseHandle((fd))
81 * defines related to tuning
83 #ifndef CHM_MAX_BLOCKS_CACHED
84 #define CHM_MAX_BLOCKS_CACHED 5
85 #endif
86 #define CHM_PARAM_MAX_BLOCKS_CACHED 0
89 * architecture specific defines
91 * Note: as soon as C99 is more widespread, the below defines should
92 * probably just use the C99 sized-int types.
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.
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;
108 /* utilities for unmarshalling data */
109 static BOOL _unmarshal_char_array(unsigned char **pData,
110 unsigned int *pLenRemain,
111 char *dest,
112 int count)
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;
122 static BOOL _unmarshal_uchar_array(unsigned char **pData,
123 unsigned int *pLenRemain,
124 unsigned char *dest,
125 int count)
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;
135 static BOOL _unmarshal_int32(unsigned char **pData,
136 unsigned int *pLenRemain,
137 Int32 *dest)
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;
147 static BOOL _unmarshal_uint32(unsigned char **pData,
148 unsigned int *pLenRemain,
149 UInt32 *dest)
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;
159 static BOOL _unmarshal_int64(unsigned char **pData,
160 unsigned int *pLenRemain,
161 Int64 *dest)
163 Int64 temp;
164 int i;
165 if (8 > *pLenRemain)
166 return FALSE;
167 temp=0;
168 for(i=8; i>0; i--)
170 temp <<= 8;
171 temp |= (*pData)[i-1];
173 *dest = temp;
174 *pData += 8;
175 *pLenRemain -= 8;
176 return TRUE;
179 static BOOL _unmarshal_uint64(unsigned char **pData,
180 unsigned int *pLenRemain,
181 UInt64 *dest)
183 UInt64 temp;
184 int i;
185 if (8 > *pLenRemain)
186 return FALSE;
187 temp=0;
188 for(i=8; i>0; i--)
190 temp <<= 8;
191 temp |= (*pData)[i-1];
193 *dest = temp;
194 *pData += 8;
195 *pLenRemain -= 8;
196 return TRUE;
199 static BOOL _unmarshal_uuid(unsigned char **pData,
200 unsigned int *pDataLen,
201 unsigned char *dest)
203 return _unmarshal_uchar_array(pData, pDataLen, dest, 16);
207 * structures local to this module
210 /* structure of ITSF headers */
211 #define _CHM_ITSF_V2_LEN (0x58)
212 #define _CHM_ITSF_V3_LEN (0x60)
213 struct chmItsfHeader
215 char signature[4]; /* 0 (ITSF) */
216 Int32 version; /* 4 */
217 Int32 header_len; /* 8 */
218 Int32 unknown_000c; /* c */
219 UInt32 last_modified; /* 10 */
220 UInt32 lang_id; /* 14 */
221 UChar dir_uuid[16]; /* 18 */
222 UChar stream_uuid[16]; /* 28 */
223 UInt64 unknown_offset; /* 38 */
224 UInt64 unknown_len; /* 40 */
225 UInt64 dir_offset; /* 48 */
226 UInt64 dir_len; /* 50 */
227 UInt64 data_offset; /* 58 (Not present before V3) */
228 }; /* __attribute__ ((aligned (1))); */
230 static BOOL _unmarshal_itsf_header(unsigned char **pData,
231 unsigned int *pDataLen,
232 struct chmItsfHeader *dest)
234 /* we only know how to deal with the 0x58 and 0x60 byte structures */
235 if (*pDataLen != _CHM_ITSF_V2_LEN && *pDataLen != _CHM_ITSF_V3_LEN)
236 return FALSE;
238 /* unmarshal common fields */
239 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
240 _unmarshal_int32 (pData, pDataLen, &dest->version);
241 _unmarshal_int32 (pData, pDataLen, &dest->header_len);
242 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c);
243 _unmarshal_uint32 (pData, pDataLen, &dest->last_modified);
244 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id);
245 _unmarshal_uuid (pData, pDataLen, dest->dir_uuid);
246 _unmarshal_uuid (pData, pDataLen, dest->stream_uuid);
247 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_offset);
248 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_len);
249 _unmarshal_uint64 (pData, pDataLen, &dest->dir_offset);
250 _unmarshal_uint64 (pData, pDataLen, &dest->dir_len);
252 /* error check the data */
253 /* XXX: should also check UUIDs, probably, though with a version 3 file,
254 * current MS tools do not seem to use them.
256 if (memcmp(dest->signature, "ITSF", 4) != 0)
257 return FALSE;
258 if (dest->version == 2)
260 if (dest->header_len < _CHM_ITSF_V2_LEN)
261 return FALSE;
263 else if (dest->version == 3)
265 if (dest->header_len < _CHM_ITSF_V3_LEN)
266 return FALSE;
268 else
269 return FALSE;
271 /* now, if we have a V3 structure, unmarshal the rest.
272 * otherwise, compute it
274 if (dest->version == 3)
276 if (*pDataLen != 0)
277 _unmarshal_uint64(pData, pDataLen, &dest->data_offset);
278 else
279 return FALSE;
281 else
282 dest->data_offset = dest->dir_offset + dest->dir_len;
284 return TRUE;
287 /* structure of ITSP headers */
288 #define _CHM_ITSP_V1_LEN (0x54)
289 struct chmItspHeader
291 char signature[4]; /* 0 (ITSP) */
292 Int32 version; /* 4 */
293 Int32 header_len; /* 8 */
294 Int32 unknown_000c; /* c */
295 UInt32 block_len; /* 10 */
296 Int32 blockidx_intvl; /* 14 */
297 Int32 index_depth; /* 18 */
298 Int32 index_root; /* 1c */
299 Int32 index_head; /* 20 */
300 Int32 unknown_0024; /* 24 */
301 UInt32 num_blocks; /* 28 */
302 Int32 unknown_002c; /* 2c */
303 UInt32 lang_id; /* 30 */
304 UChar system_uuid[16]; /* 34 */
305 UChar unknown_0044[16]; /* 44 */
306 }; /* __attribute__ ((aligned (1))); */
308 static BOOL _unmarshal_itsp_header(unsigned char **pData,
309 unsigned int *pDataLen,
310 struct chmItspHeader *dest)
312 /* we only know how to deal with a 0x54 byte structures */
313 if (*pDataLen != _CHM_ITSP_V1_LEN)
314 return FALSE;
316 /* unmarshal fields */
317 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
318 _unmarshal_int32 (pData, pDataLen, &dest->version);
319 _unmarshal_int32 (pData, pDataLen, &dest->header_len);
320 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c);
321 _unmarshal_uint32 (pData, pDataLen, &dest->block_len);
322 _unmarshal_int32 (pData, pDataLen, &dest->blockidx_intvl);
323 _unmarshal_int32 (pData, pDataLen, &dest->index_depth);
324 _unmarshal_int32 (pData, pDataLen, &dest->index_root);
325 _unmarshal_int32 (pData, pDataLen, &dest->index_head);
326 _unmarshal_int32 (pData, pDataLen, &dest->unknown_0024);
327 _unmarshal_uint32 (pData, pDataLen, &dest->num_blocks);
328 _unmarshal_int32 (pData, pDataLen, &dest->unknown_002c);
329 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id);
330 _unmarshal_uuid (pData, pDataLen, dest->system_uuid);
331 _unmarshal_uchar_array(pData, pDataLen, dest->unknown_0044, 16);
333 /* error check the data */
334 if (memcmp(dest->signature, "ITSP", 4) != 0)
335 return FALSE;
336 if (dest->version != 1)
337 return FALSE;
338 if (dest->header_len != _CHM_ITSP_V1_LEN)
339 return FALSE;
341 return TRUE;
344 /* structure of PMGL headers */
345 static const char _chm_pmgl_marker[4] = "PMGL";
346 #define _CHM_PMGL_LEN (0x14)
347 struct chmPmglHeader
349 char signature[4]; /* 0 (PMGL) */
350 UInt32 free_space; /* 4 */
351 UInt32 unknown_0008; /* 8 */
352 Int32 block_prev; /* c */
353 Int32 block_next; /* 10 */
354 }; /* __attribute__ ((aligned (1))); */
356 static BOOL _unmarshal_pmgl_header(unsigned char **pData,
357 unsigned int *pDataLen,
358 struct chmPmglHeader *dest)
360 /* we only know how to deal with a 0x14 byte structures */
361 if (*pDataLen != _CHM_PMGL_LEN)
362 return FALSE;
364 /* unmarshal fields */
365 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
366 _unmarshal_uint32 (pData, pDataLen, &dest->free_space);
367 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_0008);
368 _unmarshal_int32 (pData, pDataLen, &dest->block_prev);
369 _unmarshal_int32 (pData, pDataLen, &dest->block_next);
371 /* check structure */
372 if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0)
373 return FALSE;
375 return TRUE;
378 /* structure of PMGI headers */
379 static const char _chm_pmgi_marker[4] = "PMGI";
380 #define _CHM_PMGI_LEN (0x08)
381 struct chmPmgiHeader
383 char signature[4]; /* 0 (PMGI) */
384 UInt32 free_space; /* 4 */
385 }; /* __attribute__ ((aligned (1))); */
387 static BOOL _unmarshal_pmgi_header(unsigned char **pData,
388 unsigned int *pDataLen,
389 struct chmPmgiHeader *dest)
391 /* we only know how to deal with a 0x8 byte structures */
392 if (*pDataLen != _CHM_PMGI_LEN)
393 return FALSE;
395 /* unmarshal fields */
396 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
397 _unmarshal_uint32 (pData, pDataLen, &dest->free_space);
399 /* check structure */
400 if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0)
401 return FALSE;
403 return TRUE;
406 /* structure of LZXC reset table */
407 #define _CHM_LZXC_RESETTABLE_V1_LEN (0x28)
408 struct chmLzxcResetTable
410 UInt32 version;
411 UInt32 block_count;
412 UInt32 unknown;
413 UInt32 table_offset;
414 UInt64 uncompressed_len;
415 UInt64 compressed_len;
416 UInt64 block_len;
417 }; /* __attribute__ ((aligned (1))); */
419 static BOOL _unmarshal_lzxc_reset_table(unsigned char **pData,
420 unsigned int *pDataLen,
421 struct chmLzxcResetTable *dest)
423 /* we only know how to deal with a 0x28 byte structures */
424 if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN)
425 return FALSE;
427 /* unmarshal fields */
428 _unmarshal_uint32 (pData, pDataLen, &dest->version);
429 _unmarshal_uint32 (pData, pDataLen, &dest->block_count);
430 _unmarshal_uint32 (pData, pDataLen, &dest->unknown);
431 _unmarshal_uint32 (pData, pDataLen, &dest->table_offset);
432 _unmarshal_uint64 (pData, pDataLen, &dest->uncompressed_len);
433 _unmarshal_uint64 (pData, pDataLen, &dest->compressed_len);
434 _unmarshal_uint64 (pData, pDataLen, &dest->block_len);
436 /* check structure */
437 if (dest->version != 2)
438 return FALSE;
440 return TRUE;
443 /* structure of LZXC control data block */
444 #define _CHM_LZXC_MIN_LEN (0x18)
445 #define _CHM_LZXC_V2_LEN (0x1c)
446 struct chmLzxcControlData
448 UInt32 size; /* 0 */
449 char signature[4]; /* 4 (LZXC) */
450 UInt32 version; /* 8 */
451 UInt32 resetInterval; /* c */
452 UInt32 windowSize; /* 10 */
453 UInt32 windowsPerReset; /* 14 */
454 UInt32 unknown_18; /* 18 */
457 static BOOL _unmarshal_lzxc_control_data(unsigned char **pData,
458 unsigned int *pDataLen,
459 struct chmLzxcControlData *dest)
461 /* we want at least 0x18 bytes */
462 if (*pDataLen < _CHM_LZXC_MIN_LEN)
463 return FALSE;
465 /* unmarshal fields */
466 _unmarshal_uint32 (pData, pDataLen, &dest->size);
467 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
468 _unmarshal_uint32 (pData, pDataLen, &dest->version);
469 _unmarshal_uint32 (pData, pDataLen, &dest->resetInterval);
470 _unmarshal_uint32 (pData, pDataLen, &dest->windowSize);
471 _unmarshal_uint32 (pData, pDataLen, &dest->windowsPerReset);
473 if (*pDataLen >= _CHM_LZXC_V2_LEN)
474 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_18);
475 else
476 dest->unknown_18 = 0;
478 if (dest->version == 2)
480 dest->resetInterval *= 0x8000;
481 dest->windowSize *= 0x8000;
483 if (dest->windowSize == 0 || dest->resetInterval == 0)
484 return FALSE;
486 /* for now, only support resetInterval a multiple of windowSize/2 */
487 if (dest->windowSize == 1)
488 return FALSE;
489 if ((dest->resetInterval % (dest->windowSize/2)) != 0)
490 return FALSE;
492 /* check structure */
493 if (memcmp(dest->signature, "LZXC", 4) != 0)
494 return FALSE;
496 return TRUE;
499 /* the structure used for chm file handles */
500 struct chmFile
502 HANDLE fd;
504 CRITICAL_SECTION mutex;
505 CRITICAL_SECTION lzx_mutex;
506 CRITICAL_SECTION cache_mutex;
508 UInt64 dir_offset;
509 UInt64 dir_len;
510 UInt64 data_offset;
511 Int32 index_root;
512 Int32 index_head;
513 UInt32 block_len;
515 UInt64 span;
516 struct chmUnitInfo rt_unit;
517 struct chmUnitInfo cn_unit;
518 struct chmLzxcResetTable reset_table;
520 /* LZX control data */
521 int compression_enabled;
522 UInt32 window_size;
523 UInt32 reset_interval;
524 UInt32 reset_blkcount;
526 /* decompressor state */
527 struct LZXstate *lzx_state;
528 int lzx_last_block;
530 /* cache for decompressed blocks */
531 UChar **cache_blocks;
532 Int64 *cache_block_indices;
533 Int32 cache_num_blocks;
537 * utility functions local to this module
540 /* utility function to handle differences between {pread,read}(64)? */
541 static Int64 _chm_fetch_bytes(struct chmFile *h,
542 UChar *buf,
543 UInt64 os,
544 Int64 len)
546 Int64 readLen=0;
547 if (h->fd == CHM_NULL_FD)
548 return readLen;
550 CHM_ACQUIRE_LOCK(h->mutex);
551 /* NOTE: this might be better done with CreateFileMapping, et cetera... */
553 LARGE_INTEGER old_pos, new_pos;
554 DWORD actualLen=0;
556 /* awkward Win32 Seek/Tell */
557 new_pos.QuadPart = 0;
558 SetFilePointerEx( h->fd, new_pos, &old_pos, FILE_CURRENT );
559 new_pos.QuadPart = os;
560 SetFilePointerEx( h->fd, new_pos, NULL, FILE_BEGIN );
562 /* read the data */
563 if (ReadFile(h->fd,
564 buf,
565 (DWORD)len,
566 &actualLen,
567 NULL))
568 readLen = actualLen;
569 else
570 readLen = 0;
572 /* restore original position */
573 SetFilePointerEx( h->fd, old_pos, NULL, FILE_BEGIN );
575 CHM_RELEASE_LOCK(h->mutex);
576 return readLen;
580 * set a parameter on the file handle.
581 * valid parameter types:
582 * CHM_PARAM_MAX_BLOCKS_CACHED:
583 * how many decompressed blocks should be cached? A simple
584 * caching scheme is used, wherein the index of the block is
585 * used as a hash value, and hash collision results in the
586 * invalidation of the previously cached block.
588 static void chm_set_param(struct chmFile *h,
589 int paramType,
590 int paramVal)
592 switch (paramType)
594 case CHM_PARAM_MAX_BLOCKS_CACHED:
595 CHM_ACQUIRE_LOCK(h->cache_mutex);
596 if (paramVal != h->cache_num_blocks)
598 UChar **newBlocks;
599 Int64 *newIndices;
600 int i;
602 /* allocate new cached blocks */
603 newBlocks = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UChar *));
604 newIndices = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UInt64));
605 for (i=0; i<paramVal; i++)
607 newBlocks[i] = NULL;
608 newIndices[i] = 0;
611 /* re-distribute old cached blocks */
612 if (h->cache_blocks)
614 for (i=0; i<h->cache_num_blocks; i++)
616 int newSlot = (int)(h->cache_block_indices[i] % paramVal);
618 if (h->cache_blocks[i])
620 /* in case of collision, destroy newcomer */
621 if (newBlocks[newSlot])
623 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]);
624 h->cache_blocks[i] = NULL;
626 else
628 newBlocks[newSlot] = h->cache_blocks[i];
629 newIndices[newSlot] =
630 h->cache_block_indices[i];
635 HeapFree(GetProcessHeap(), 0, h->cache_blocks);
636 HeapFree(GetProcessHeap(), 0, h->cache_block_indices);
639 /* now, set new values */
640 h->cache_blocks = newBlocks;
641 h->cache_block_indices = newIndices;
642 h->cache_num_blocks = paramVal;
644 CHM_RELEASE_LOCK(h->cache_mutex);
645 break;
647 default:
648 break;
652 /* open an ITS archive */
653 struct chmFile *chm_openW(const WCHAR *filename)
655 unsigned char sbuffer[256];
656 unsigned int sremain;
657 unsigned char *sbufpos;
658 struct chmFile *newHandle=NULL;
659 struct chmItsfHeader itsfHeader;
660 struct chmItspHeader itspHeader;
661 #if 0
662 struct chmUnitInfo uiSpan;
663 #endif
664 struct chmUnitInfo uiLzxc;
665 struct chmLzxcControlData ctlData;
667 /* allocate handle */
668 newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile));
669 newHandle->fd = CHM_NULL_FD;
670 newHandle->lzx_state = NULL;
671 newHandle->cache_blocks = NULL;
672 newHandle->cache_block_indices = NULL;
673 newHandle->cache_num_blocks = 0;
675 /* open file */
676 if ((newHandle->fd=CreateFileW(filename,
677 GENERIC_READ,
678 FILE_SHARE_READ,
679 NULL,
680 OPEN_EXISTING,
681 FILE_ATTRIBUTE_NORMAL,
682 NULL)) == CHM_NULL_FD)
684 HeapFree(GetProcessHeap(), 0, newHandle);
685 return NULL;
688 /* initialize mutexes, if needed */
689 InitializeCriticalSection(&newHandle->mutex);
690 newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex");
691 InitializeCriticalSection(&newHandle->lzx_mutex);
692 newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex");
693 InitializeCriticalSection(&newHandle->cache_mutex);
694 newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex");
696 /* read and verify header */
697 sremain = _CHM_ITSF_V3_LEN;
698 sbufpos = sbuffer;
699 if (_chm_fetch_bytes(newHandle, sbuffer, 0, sremain) != sremain ||
700 !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader))
702 chm_close(newHandle);
703 return NULL;
706 /* stash important values from header */
707 newHandle->dir_offset = itsfHeader.dir_offset;
708 newHandle->dir_len = itsfHeader.dir_len;
709 newHandle->data_offset = itsfHeader.data_offset;
711 /* now, read and verify the directory header chunk */
712 sremain = _CHM_ITSP_V1_LEN;
713 sbufpos = sbuffer;
714 if (_chm_fetch_bytes(newHandle, sbuffer,
715 itsfHeader.dir_offset, sremain) != sremain ||
716 !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader))
718 chm_close(newHandle);
719 return NULL;
722 /* grab essential information from ITSP header */
723 newHandle->dir_offset += itspHeader.header_len;
724 newHandle->dir_len -= itspHeader.header_len;
725 newHandle->index_root = itspHeader.index_root;
726 newHandle->index_head = itspHeader.index_head;
727 newHandle->block_len = itspHeader.block_len;
729 /* if the index root is -1, this means we don't have any PMGI blocks.
730 * as a result, we must use the sole PMGL block as the index root
732 if (newHandle->index_root == -1)
733 newHandle->index_root = newHandle->index_head;
735 /* initialize cache */
736 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
737 CHM_MAX_BLOCKS_CACHED);
739 /* By default, compression is enabled. */
740 newHandle->compression_enabled = 1;
742 /* prefetch most commonly needed unit infos */
743 if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
744 L"::DataSpace/Storage/MSCompressed/Transform/"
745 "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/"
746 "InstanceData/ResetTable",
747 &newHandle->rt_unit) ||
748 newHandle->rt_unit.space == CHM_COMPRESSED ||
749 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
750 L"::DataSpace/Storage/MSCompressed/Content",
751 &newHandle->cn_unit) ||
752 newHandle->cn_unit.space == CHM_COMPRESSED ||
753 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
754 L"::DataSpace/Storage/MSCompressed/ControlData",
755 &uiLzxc) ||
756 uiLzxc.space == CHM_COMPRESSED)
758 newHandle->compression_enabled = 0;
761 /* read reset table info */
762 if (newHandle->compression_enabled)
764 sremain = _CHM_LZXC_RESETTABLE_V1_LEN;
765 sbufpos = sbuffer;
766 if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer,
767 0, sremain) != sremain ||
768 !_unmarshal_lzxc_reset_table(&sbufpos, &sremain,
769 &newHandle->reset_table))
771 newHandle->compression_enabled = 0;
775 /* read control data */
776 if (newHandle->compression_enabled)
778 sremain = (unsigned long)uiLzxc.length;
779 sbufpos = sbuffer;
780 if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer,
781 0, sremain) != sremain ||
782 !_unmarshal_lzxc_control_data(&sbufpos, &sremain,
783 &ctlData))
785 newHandle->compression_enabled = 0;
787 else
789 newHandle->window_size = ctlData.windowSize;
790 newHandle->reset_interval = ctlData.resetInterval;
791 /* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */
792 /* must be multiplied by this formerly unknown ctrl data field in */
793 /* order to decompress some files. */
794 newHandle->reset_blkcount = newHandle->reset_interval / (newHandle->window_size / 2) *
795 ctlData.windowsPerReset;
799 return newHandle;
802 /* Duplicate an ITS archive handle */
803 struct chmFile *chm_dup(struct chmFile *oldHandle)
805 struct chmFile *newHandle=NULL;
807 newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile));
808 *newHandle = *oldHandle;
810 /* duplicate fd handle */
811 DuplicateHandle(GetCurrentProcess(), oldHandle->fd,
812 GetCurrentProcess(), &(newHandle->fd),
813 0, FALSE, DUPLICATE_SAME_ACCESS);
814 newHandle->lzx_state = NULL;
815 newHandle->cache_blocks = NULL;
816 newHandle->cache_block_indices = NULL;
817 newHandle->cache_num_blocks = 0;
819 /* initialize mutexes, if needed */
820 InitializeCriticalSection(&newHandle->mutex);
821 newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex");
822 InitializeCriticalSection(&newHandle->lzx_mutex);
823 newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex");
824 InitializeCriticalSection(&newHandle->cache_mutex);
825 newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex");
827 /* initialize cache */
828 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
829 CHM_MAX_BLOCKS_CACHED);
831 return newHandle;
834 /* close an ITS archive */
835 void chm_close(struct chmFile *h)
837 if (h != NULL)
839 if (h->fd != CHM_NULL_FD)
840 CHM_CLOSE_FILE(h->fd);
841 h->fd = CHM_NULL_FD;
843 h->mutex.DebugInfo->Spare[0] = 0;
844 DeleteCriticalSection(&h->mutex);
845 h->lzx_mutex.DebugInfo->Spare[0] = 0;
846 DeleteCriticalSection(&h->lzx_mutex);
847 h->cache_mutex.DebugInfo->Spare[0] = 0;
848 DeleteCriticalSection(&h->cache_mutex);
850 if (h->lzx_state)
851 LZXteardown(h->lzx_state);
852 h->lzx_state = NULL;
854 if (h->cache_blocks)
856 int i;
857 for (i=0; i<h->cache_num_blocks; i++)
859 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]);
861 HeapFree(GetProcessHeap(), 0, h->cache_blocks);
862 h->cache_blocks = NULL;
865 HeapFree(GetProcessHeap(), 0, h->cache_block_indices);
866 h->cache_block_indices = NULL;
868 HeapFree(GetProcessHeap(), 0, h);
873 * helper methods for chm_resolve_object
876 /* skip a compressed dword */
877 static void _chm_skip_cword(UChar **pEntry)
879 while (*(*pEntry)++ >= 0x80)
883 /* skip the data from a PMGL entry */
884 static void _chm_skip_PMGL_entry_data(UChar **pEntry)
886 _chm_skip_cword(pEntry);
887 _chm_skip_cword(pEntry);
888 _chm_skip_cword(pEntry);
891 /* parse a compressed dword */
892 static UInt64 _chm_parse_cword(UChar **pEntry)
894 UInt64 accum = 0;
895 UChar temp;
896 while ((temp=*(*pEntry)++) >= 0x80)
898 accum <<= 7;
899 accum += temp & 0x7f;
902 return (accum << 7) + temp;
905 /* parse a utf-8 string into an ASCII char buffer */
906 static BOOL _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path)
908 DWORD length = MultiByteToWideChar(CP_UTF8, 0, (char *)*pEntry, count, path, CHM_MAX_PATHLEN);
909 path[length] = '\0';
910 *pEntry += count;
911 return !!length;
914 /* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */
915 static BOOL _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui)
917 UInt64 strLen;
919 /* parse str len */
920 strLen = _chm_parse_cword(pEntry);
921 if (strLen > CHM_MAX_PATHLEN)
922 return FALSE;
924 /* parse path */
925 if (! _chm_parse_UTF8(pEntry, strLen, ui->path))
926 return FALSE;
928 /* parse info */
929 ui->space = (int)_chm_parse_cword(pEntry);
930 ui->start = _chm_parse_cword(pEntry);
931 ui->length = _chm_parse_cword(pEntry);
932 return TRUE;
935 /* find an exact entry in PMGL; return NULL if we fail */
936 static UChar *_chm_find_in_PMGL(UChar *page_buf,
937 UInt32 block_len,
938 const WCHAR *objPath)
940 /* XXX: modify this to do a binary search using the nice index structure
941 * that is provided for us.
943 struct chmPmglHeader header;
944 UInt32 hremain;
945 UChar *end;
946 UChar *cur;
947 UChar *temp;
948 UInt64 strLen;
949 WCHAR buffer[CHM_MAX_PATHLEN+1];
951 /* figure out where to start and end */
952 cur = page_buf;
953 hremain = _CHM_PMGL_LEN;
954 if (! _unmarshal_pmgl_header(&cur, &hremain, &header))
955 return NULL;
956 end = page_buf + block_len - (header.free_space);
958 /* now, scan progressively */
959 while (cur < end)
961 /* grab the name */
962 temp = cur;
963 strLen = _chm_parse_cword(&cur);
964 if (! _chm_parse_UTF8(&cur, strLen, buffer))
965 return NULL;
967 /* check if it is the right name */
968 if (! wcsicmp(buffer, objPath))
969 return temp;
971 _chm_skip_PMGL_entry_data(&cur);
974 return NULL;
977 /* find which block should be searched next for the entry; -1 if no block */
978 static Int32 _chm_find_in_PMGI(UChar *page_buf,
979 UInt32 block_len,
980 const WCHAR *objPath)
982 /* XXX: modify this to do a binary search using the nice index structure
983 * that is provided for us
985 struct chmPmgiHeader header;
986 UInt32 hremain;
987 int page=-1;
988 UChar *end;
989 UChar *cur;
990 UInt64 strLen;
991 WCHAR buffer[CHM_MAX_PATHLEN+1];
993 /* figure out where to start and end */
994 cur = page_buf;
995 hremain = _CHM_PMGI_LEN;
996 if (! _unmarshal_pmgi_header(&cur, &hremain, &header))
997 return -1;
998 end = page_buf + block_len - (header.free_space);
1000 /* now, scan progressively */
1001 while (cur < end)
1003 /* grab the name */
1004 strLen = _chm_parse_cword(&cur);
1005 if (! _chm_parse_UTF8(&cur, strLen, buffer))
1006 return -1;
1008 /* check if it is the right name */
1009 if (wcsicmp(buffer, objPath) > 0)
1010 return page;
1012 /* load next value for path */
1013 page = (int)_chm_parse_cword(&cur);
1016 return page;
1019 /* resolve a particular object from the archive */
1020 int chm_resolve_object(struct chmFile *h,
1021 const WCHAR *objPath,
1022 struct chmUnitInfo *ui)
1025 * XXX: implement caching scheme for dir pages
1028 Int32 curPage;
1030 /* buffer to hold whatever page we're looking at */
1031 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len);
1033 /* starting page */
1034 curPage = h->index_root;
1036 /* until we have either returned or given up */
1037 while (curPage != -1)
1040 /* try to fetch the index page */
1041 if (_chm_fetch_bytes(h, page_buf,
1042 h->dir_offset + (UInt64)curPage*h->block_len,
1043 h->block_len) != h->block_len)
1045 HeapFree(GetProcessHeap(), 0, page_buf);
1046 return CHM_RESOLVE_FAILURE;
1049 /* now, if it is a leaf node: */
1050 if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0)
1052 /* scan block */
1053 UChar *pEntry = _chm_find_in_PMGL(page_buf,
1054 h->block_len,
1055 objPath);
1056 if (pEntry == NULL)
1058 HeapFree(GetProcessHeap(), 0, page_buf);
1059 return CHM_RESOLVE_FAILURE;
1062 /* parse entry and return */
1063 _chm_parse_PMGL_entry(&pEntry, ui);
1064 HeapFree(GetProcessHeap(), 0, page_buf);
1065 return CHM_RESOLVE_SUCCESS;
1068 /* else, if it is a branch node: */
1069 else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0)
1070 curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath);
1072 /* else, we are confused. give up. */
1073 else
1075 HeapFree(GetProcessHeap(), 0, page_buf);
1076 return CHM_RESOLVE_FAILURE;
1080 /* didn't find anything. fail. */
1081 HeapFree(GetProcessHeap(), 0, page_buf);
1082 return CHM_RESOLVE_FAILURE;
1086 * utility methods for dealing with compressed data
1089 /* get the bounds of a compressed block. Returns FALSE on failure */
1090 static BOOL _chm_get_cmpblock_bounds(struct chmFile *h,
1091 UInt64 block,
1092 UInt64 *start,
1093 Int64 *len)
1095 UChar buffer[8], *dummy;
1096 UInt32 remain;
1098 /* for all but the last block, use the reset table */
1099 if (block < h->reset_table.block_count-1)
1101 /* unpack the start address */
1102 dummy = buffer;
1103 remain = 8;
1104 if (_chm_fetch_bytes(h, buffer,
1105 h->data_offset
1106 + h->rt_unit.start
1107 + h->reset_table.table_offset
1108 + block*8,
1109 remain) != remain ||
1110 !_unmarshal_uint64(&dummy, &remain, start))
1111 return FALSE;
1113 /* unpack the end address */
1114 dummy = buffer;
1115 remain = 8;
1116 if (_chm_fetch_bytes(h, buffer,
1117 h->data_offset
1118 + h->rt_unit.start
1119 + h->reset_table.table_offset
1120 + block*8 + 8,
1121 remain) != remain ||
1122 !_unmarshal_int64(&dummy, &remain, len))
1123 return FALSE;
1126 /* for the last block, use the span in addition to the reset table */
1127 else
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;
1141 *len = h->reset_table.compressed_len;
1144 /* compute the length and absolute start address */
1145 *len -= *start;
1146 *start += h->data_offset + h->cn_unit.start;
1148 return TRUE;
1151 /* decompress the block. must have lzx_mutex. */
1152 static Int64 _chm_decompress_block(struct chmFile *h,
1153 UInt64 block,
1154 UChar **ubuffer)
1156 UChar *cbuffer = HeapAlloc( GetProcessHeap(), 0,
1157 ((unsigned int)h->reset_table.block_len + 6144));
1158 UInt64 cmpStart; /* compressed start */
1159 Int64 cmpLen; /* compressed len */
1160 int indexSlot; /* cache index slot */
1161 UChar *lbuffer; /* local buffer ptr */
1162 UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset interval align */
1163 UInt32 i; /* local loop index */
1165 /* let the caching system pull its weight! */
1166 if (block - blockAlign <= h->lzx_last_block &&
1167 block >= h->lzx_last_block)
1168 blockAlign = (block - h->lzx_last_block);
1170 /* check if we need previous blocks */
1171 if (blockAlign != 0)
1173 /* fetch all required previous blocks since last reset */
1174 for (i = blockAlign; i > 0; i--)
1176 UInt32 curBlockIdx = block - i;
1178 /* check if we most recently decompressed the previous block */
1179 if (h->lzx_last_block != curBlockIdx)
1181 if ((curBlockIdx % h->reset_blkcount) == 0)
1183 #ifdef CHM_DEBUG
1184 fprintf(stderr, "***RESET (1)***\n");
1185 #endif
1186 LZXreset(h->lzx_state);
1189 indexSlot = (int)((curBlockIdx) % h->cache_num_blocks);
1190 h->cache_block_indices[indexSlot] = curBlockIdx;
1191 if (! h->cache_blocks[indexSlot])
1192 h->cache_blocks[indexSlot] =
1193 HeapAlloc(GetProcessHeap(), 0,
1194 (unsigned int)(h->reset_table.block_len));
1195 lbuffer = h->cache_blocks[indexSlot];
1197 /* decompress the previous block */
1198 #ifdef CHM_DEBUG
1199 fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx);
1200 #endif
1201 if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) ||
1202 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen ||
1203 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
1204 (int)h->reset_table.block_len) != DECR_OK)
1206 #ifdef CHM_DEBUG
1207 fprintf(stderr, " (DECOMPRESS FAILED!)\n");
1208 #endif
1209 HeapFree(GetProcessHeap(), 0, cbuffer);
1210 return 0;
1213 h->lzx_last_block = (int)curBlockIdx;
1217 else
1219 if ((block % h->reset_blkcount) == 0)
1221 #ifdef CHM_DEBUG
1222 fprintf(stderr, "***RESET (2)***\n");
1223 #endif
1224 LZXreset(h->lzx_state);
1228 /* allocate slot in cache */
1229 indexSlot = (int)(block % h->cache_num_blocks);
1230 h->cache_block_indices[indexSlot] = block;
1231 if (! h->cache_blocks[indexSlot])
1232 h->cache_blocks[indexSlot] =
1233 HeapAlloc(GetProcessHeap(), 0, ((unsigned int)h->reset_table.block_len));
1234 lbuffer = h->cache_blocks[indexSlot];
1235 *ubuffer = lbuffer;
1237 /* decompress the block we actually want */
1238 #ifdef CHM_DEBUG
1239 fprintf(stderr, "Decompressing block #%4d (REAL )\n", block);
1240 #endif
1241 if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) ||
1242 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen ||
1243 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
1244 (int)h->reset_table.block_len) != DECR_OK)
1246 #ifdef CHM_DEBUG
1247 fprintf(stderr, " (DECOMPRESS FAILED!)\n");
1248 #endif
1249 HeapFree(GetProcessHeap(), 0, cbuffer);
1250 return 0;
1252 h->lzx_last_block = (int)block;
1254 /* XXX: modify LZX routines to return the length of the data they
1255 * decompressed and return that instead, for an extra sanity check.
1257 HeapFree(GetProcessHeap(), 0, cbuffer);
1258 return h->reset_table.block_len;
1261 /* grab a region from a compressed block */
1262 static Int64 _chm_decompress_region(struct chmFile *h,
1263 UChar *buf,
1264 UInt64 start,
1265 Int64 len)
1267 UInt64 nBlock, nOffset;
1268 UInt64 nLen;
1269 UInt64 gotLen;
1270 UChar *ubuffer = NULL;
1272 if (len <= 0)
1273 return 0;
1275 /* figure out what we need to read */
1276 nBlock = start / h->reset_table.block_len;
1277 nOffset = start % h->reset_table.block_len;
1278 nLen = len;
1279 if (nLen > (h->reset_table.block_len - nOffset))
1280 nLen = h->reset_table.block_len - nOffset;
1282 /* if block is cached, return data from it. */
1283 CHM_ACQUIRE_LOCK(h->lzx_mutex);
1284 CHM_ACQUIRE_LOCK(h->cache_mutex);
1285 if (h->cache_block_indices[nBlock % h->cache_num_blocks] == nBlock &&
1286 h->cache_blocks[nBlock % h->cache_num_blocks] != NULL)
1288 memcpy(buf,
1289 h->cache_blocks[nBlock % h->cache_num_blocks] + nOffset,
1290 (unsigned int)nLen);
1291 CHM_RELEASE_LOCK(h->cache_mutex);
1292 CHM_RELEASE_LOCK(h->lzx_mutex);
1293 return nLen;
1295 CHM_RELEASE_LOCK(h->cache_mutex);
1297 /* data request not satisfied, so... start up the decompressor machine */
1298 if (! h->lzx_state)
1300 h->lzx_last_block = -1;
1301 h->lzx_state = LZXinit(h->window_size);
1304 /* decompress some data */
1305 gotLen = _chm_decompress_block(h, nBlock, &ubuffer);
1306 if (gotLen < nLen)
1307 nLen = gotLen;
1308 memcpy(buf, ubuffer+nOffset, (unsigned int)nLen);
1309 CHM_RELEASE_LOCK(h->lzx_mutex);
1310 return nLen;
1313 /* retrieve (part of) an object */
1314 LONGINT64 chm_retrieve_object(struct chmFile *h,
1315 struct chmUnitInfo *ui,
1316 unsigned char *buf,
1317 LONGUINT64 addr,
1318 LONGINT64 len)
1320 /* must be valid file handle */
1321 if (h == NULL)
1322 return 0;
1324 /* starting address must be in correct range */
1325 if (addr >= ui->length)
1326 return 0;
1328 /* clip length */
1329 if (addr + len > ui->length)
1330 len = ui->length - addr;
1332 /* if the file is uncompressed, it's simple */
1333 if (ui->space == CHM_UNCOMPRESSED)
1335 /* read data */
1336 return _chm_fetch_bytes(h,
1337 buf,
1338 h->data_offset + ui->start + addr,
1339 len);
1342 /* else if the file is compressed, it's a little trickier */
1343 else /* ui->space == CHM_COMPRESSED */
1345 Int64 swath=0, total=0;
1347 /* if compression is not enabled for this file... */
1348 if (! h->compression_enabled)
1349 return total;
1351 do {
1353 /* swill another mouthful */
1354 swath = _chm_decompress_region(h, buf, ui->start + addr, len);
1356 /* if we didn't get any... */
1357 if (swath == 0)
1358 return total;
1360 /* update stats */
1361 total += swath;
1362 len -= swath;
1363 addr += swath;
1364 buf += swath;
1366 } while (len != 0);
1368 return total;
1372 BOOL chm_enumerate_dir(struct chmFile *h,
1373 const WCHAR *prefix,
1374 int what,
1375 CHM_ENUMERATOR e,
1376 void *context)
1379 * XXX: do this efficiently (i.e. using the tree index)
1382 Int32 curPage;
1384 /* buffer to hold whatever page we're looking at */
1385 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len);
1386 struct chmPmglHeader header;
1387 UChar *end;
1388 UChar *cur;
1389 unsigned int lenRemain;
1391 /* set to TRUE once we've started */
1392 BOOL it_has_begun = FALSE;
1394 /* the current ui */
1395 struct chmUnitInfo ui;
1396 int flag;
1397 UInt64 ui_path_len;
1399 /* the length of the prefix */
1400 WCHAR prefixRectified[CHM_MAX_PATHLEN+1];
1401 int prefixLen;
1402 WCHAR lastPath[CHM_MAX_PATHLEN];
1403 int lastPathLen;
1405 /* starting page */
1406 curPage = h->index_head;
1408 /* initialize pathname state */
1409 lstrcpynW(prefixRectified, prefix, CHM_MAX_PATHLEN);
1410 prefixLen = lstrlenW(prefixRectified);
1411 if (prefixLen != 0)
1413 if (prefixRectified[prefixLen-1] != '/')
1415 prefixRectified[prefixLen] = '/';
1416 prefixRectified[prefixLen+1] = '\0';
1417 ++prefixLen;
1420 lastPath[0] = '\0';
1421 lastPathLen = -1;
1423 /* until we have either returned or given up */
1424 while (curPage != -1)
1427 /* try to fetch the index page */
1428 if (_chm_fetch_bytes(h,
1429 page_buf,
1430 h->dir_offset + (UInt64)curPage*h->block_len,
1431 h->block_len) != h->block_len)
1433 HeapFree(GetProcessHeap(), 0, page_buf);
1434 return FALSE;
1437 /* figure out start and end for this page */
1438 cur = page_buf;
1439 lenRemain = _CHM_PMGL_LEN;
1440 if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header))
1442 HeapFree(GetProcessHeap(), 0, page_buf);
1443 return FALSE;
1445 end = page_buf + h->block_len - (header.free_space);
1447 /* loop over this page */
1448 while (cur < end)
1450 if (! _chm_parse_PMGL_entry(&cur, &ui))
1452 HeapFree(GetProcessHeap(), 0, page_buf);
1453 return FALSE;
1456 /* check if we should start */
1457 if (! it_has_begun)
1459 if (ui.length == 0 && wcsnicmp(ui.path, prefixRectified, prefixLen) == 0)
1460 it_has_begun = TRUE;
1461 else
1462 continue;
1464 if (ui.path[prefixLen] == '\0')
1465 continue;
1468 /* check if we should stop */
1469 else
1471 if (wcsnicmp(ui.path, prefixRectified, prefixLen) != 0)
1473 HeapFree(GetProcessHeap(), 0, page_buf);
1474 return TRUE;
1478 /* check if we should include this path */
1479 if (lastPathLen != -1)
1481 if (wcsnicmp(ui.path, lastPath, lastPathLen) == 0)
1482 continue;
1484 lstrcpyW(lastPath, ui.path);
1485 lastPathLen = lstrlenW(lastPath);
1487 /* get the length of the path */
1488 ui_path_len = lstrlenW(ui.path)-1;
1490 /* check for DIRS */
1491 if (ui.path[ui_path_len] == '/' && !(what & CHM_ENUMERATE_DIRS))
1492 continue;
1494 /* check for FILES */
1495 if (ui.path[ui_path_len] != '/' && !(what & CHM_ENUMERATE_FILES))
1496 continue;
1498 /* check for NORMAL vs. META */
1499 if (ui.path[0] == '/')
1502 /* check for NORMAL vs. SPECIAL */
1503 if (ui.path[1] == '#' || ui.path[1] == '$')
1504 flag = CHM_ENUMERATE_SPECIAL;
1505 else
1506 flag = CHM_ENUMERATE_NORMAL;
1508 else
1509 flag = CHM_ENUMERATE_META;
1510 if (! (what & flag))
1511 continue;
1513 /* call the enumerator */
1515 int status = (*e)(h, &ui, context);
1516 switch (status)
1518 case CHM_ENUMERATOR_FAILURE:
1519 HeapFree(GetProcessHeap(), 0, page_buf);
1520 return FALSE;
1521 case CHM_ENUMERATOR_CONTINUE:
1522 break;
1523 case CHM_ENUMERATOR_SUCCESS:
1524 HeapFree(GetProcessHeap(), 0, page_buf);
1525 return TRUE;
1526 default:
1527 break;
1532 /* advance to next page */
1533 curPage = header.block_next;
1536 HeapFree(GetProcessHeap(), 0, page_buf);
1537 return TRUE;