itss: Declare a function static.
[wine/wine64.git] / dlls / itss / chm_lib.c
blob438bbe46b602a21f0de3cad80f77cfe0b73494e6
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 /***************************************************************************
34 * *
35 * This library is free software; you can redistribute it and/or modify *
36 * it under the terms of the GNU Lesser General Public License as *
37 * published by the Free Software Foundation; either version 2.1 of the *
38 * License, or (at your option) any later version. *
39 * *
40 ***************************************************************************/
42 /***************************************************************************
43 * *
44 * Adapted for Wine by Mike McCormack *
45 * *
46 ***************************************************************************/
48 #include "config.h"
49 #include "wine/port.h"
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wine/unicode.h"
60 #include "chm_lib.h"
61 #include "lzx.h"
63 #define CHM_ACQUIRE_LOCK(a) do { \
64 EnterCriticalSection(&(a)); \
65 } while(0)
66 #define CHM_RELEASE_LOCK(a) do { \
67 LeaveCriticalSection(&(a)); \
68 } while(0)
70 #define CHM_NULL_FD (INVALID_HANDLE_VALUE)
71 #define CHM_CLOSE_FILE(fd) CloseHandle((fd))
74 * defines related to tuning
76 #ifndef CHM_MAX_BLOCKS_CACHED
77 #define CHM_MAX_BLOCKS_CACHED 5
78 #endif
79 #define CHM_PARAM_MAX_BLOCKS_CACHED 0
82 * architecture specific defines
84 * Note: as soon as C99 is more widespread, the below defines should
85 * probably just use the C99 sized-int types.
87 * The following settings will probably work for many platforms. The sizes
88 * don't have to be exactly correct, but the types must accommodate at least as
89 * many bits as they specify.
92 /* i386, 32-bit, Windows */
93 typedef BYTE UChar;
94 typedef SHORT Int16;
95 typedef USHORT UInt16;
96 typedef LONG Int32;
97 typedef DWORD UInt32;
98 typedef LONGLONG Int64;
99 typedef ULONGLONG UInt64;
101 /* utilities for unmarshalling data */
102 static int _unmarshal_char_array(unsigned char **pData,
103 unsigned int *pLenRemain,
104 char *dest,
105 int count)
107 if (count <= 0 || (unsigned int)count > *pLenRemain)
108 return 0;
109 memcpy(dest, (*pData), count);
110 *pData += count;
111 *pLenRemain -= count;
112 return 1;
115 static int _unmarshal_uchar_array(unsigned char **pData,
116 unsigned int *pLenRemain,
117 unsigned char *dest,
118 int count)
120 if (count <= 0 || (unsigned int)count > *pLenRemain)
121 return 0;
122 memcpy(dest, (*pData), count);
123 *pData += count;
124 *pLenRemain -= count;
125 return 1;
128 static int _unmarshal_int32(unsigned char **pData,
129 unsigned int *pLenRemain,
130 Int32 *dest)
132 if (4 > *pLenRemain)
133 return 0;
134 *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24;
135 *pData += 4;
136 *pLenRemain -= 4;
137 return 1;
140 static int _unmarshal_uint32(unsigned char **pData,
141 unsigned int *pLenRemain,
142 UInt32 *dest)
144 if (4 > *pLenRemain)
145 return 0;
146 *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24;
147 *pData += 4;
148 *pLenRemain -= 4;
149 return 1;
152 static int _unmarshal_int64(unsigned char **pData,
153 unsigned int *pLenRemain,
154 Int64 *dest)
156 Int64 temp;
157 int i;
158 if (8 > *pLenRemain)
159 return 0;
160 temp=0;
161 for(i=8; i>0; i--)
163 temp <<= 8;
164 temp |= (*pData)[i-1];
166 *dest = temp;
167 *pData += 8;
168 *pLenRemain -= 8;
169 return 1;
172 static int _unmarshal_uint64(unsigned char **pData,
173 unsigned int *pLenRemain,
174 UInt64 *dest)
176 UInt64 temp;
177 int i;
178 if (8 > *pLenRemain)
179 return 0;
180 temp=0;
181 for(i=8; i>0; i--)
183 temp <<= 8;
184 temp |= (*pData)[i-1];
186 *dest = temp;
187 *pData += 8;
188 *pLenRemain -= 8;
189 return 1;
192 static int _unmarshal_uuid(unsigned char **pData,
193 unsigned int *pDataLen,
194 unsigned char *dest)
196 return _unmarshal_uchar_array(pData, pDataLen, dest, 16);
199 /* names of sections essential to decompression */
200 static const WCHAR _CHMU_RESET_TABLE[] = {
201 ':',':','D','a','t','a','S','p','a','c','e','/',
202 'S','t','o','r','a','g','e','/',
203 'M','S','C','o','m','p','r','e','s','s','e','d','/',
204 'T','r','a','n','s','f','o','r','m','/',
205 '{','7','F','C','2','8','9','4','0','-','9','D','3','1',
206 '-','1','1','D','0','-','9','B','2','7','-',
207 '0','0','A','0','C','9','1','E','9','C','7','C','}','/',
208 'I','n','s','t','a','n','c','e','D','a','t','a','/',
209 'R','e','s','e','t','T','a','b','l','e',0
211 static const WCHAR _CHMU_LZXC_CONTROLDATA[] = {
212 ':',':','D','a','t','a','S','p','a','c','e','/',
213 'S','t','o','r','a','g','e','/',
214 'M','S','C','o','m','p','r','e','s','s','e','d','/',
215 'C','o','n','t','r','o','l','D','a','t','a',0
217 static const WCHAR _CHMU_CONTENT[] = {
218 ':',':','D','a','t','a','S','p','a','c','e','/',
219 'S','t','o','r','a','g','e','/',
220 'M','S','C','o','m','p','r','e','s','s','e','d','/',
221 'C','o','n','t','e','n','t',0
223 static const WCHAR _CHMU_SPANINFO[] = {
224 ':',':','D','a','t','a','S','p','a','c','e','/',
225 'S','t','o','r','a','g','e','/',
226 'M','S','C','o','m','p','r','e','s','s','e','d','/',
227 'S','p','a','n','I','n','f','o',
231 * structures local to this module
234 /* structure of ITSF headers */
235 #define _CHM_ITSF_V2_LEN (0x58)
236 #define _CHM_ITSF_V3_LEN (0x60)
237 struct chmItsfHeader
239 char signature[4]; /* 0 (ITSF) */
240 Int32 version; /* 4 */
241 Int32 header_len; /* 8 */
242 Int32 unknown_000c; /* c */
243 UInt32 last_modified; /* 10 */
244 UInt32 lang_id; /* 14 */
245 UChar dir_uuid[16]; /* 18 */
246 UChar stream_uuid[16]; /* 28 */
247 UInt64 unknown_offset; /* 38 */
248 UInt64 unknown_len; /* 40 */
249 UInt64 dir_offset; /* 48 */
250 UInt64 dir_len; /* 50 */
251 UInt64 data_offset; /* 58 (Not present before V3) */
252 }; /* __attribute__ ((aligned (1))); */
254 static int _unmarshal_itsf_header(unsigned char **pData,
255 unsigned int *pDataLen,
256 struct chmItsfHeader *dest)
258 /* we only know how to deal with the 0x58 and 0x60 byte structures */
259 if (*pDataLen != _CHM_ITSF_V2_LEN && *pDataLen != _CHM_ITSF_V3_LEN)
260 return 0;
262 /* unmarshal common fields */
263 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
264 _unmarshal_int32 (pData, pDataLen, &dest->version);
265 _unmarshal_int32 (pData, pDataLen, &dest->header_len);
266 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c);
267 _unmarshal_uint32 (pData, pDataLen, &dest->last_modified);
268 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id);
269 _unmarshal_uuid (pData, pDataLen, dest->dir_uuid);
270 _unmarshal_uuid (pData, pDataLen, dest->stream_uuid);
271 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_offset);
272 _unmarshal_uint64 (pData, pDataLen, &dest->unknown_len);
273 _unmarshal_uint64 (pData, pDataLen, &dest->dir_offset);
274 _unmarshal_uint64 (pData, pDataLen, &dest->dir_len);
276 /* error check the data */
277 /* XXX: should also check UUIDs, probably, though with a version 3 file,
278 * current MS tools do not seem to use them.
280 if (memcmp(dest->signature, "ITSF", 4) != 0)
281 return 0;
282 if (dest->version == 2)
284 if (dest->header_len < _CHM_ITSF_V2_LEN)
285 return 0;
287 else if (dest->version == 3)
289 if (dest->header_len < _CHM_ITSF_V3_LEN)
290 return 0;
292 else
293 return 0;
295 /* now, if we have a V3 structure, unmarshal the rest.
296 * otherwise, compute it
298 if (dest->version == 3)
300 if (*pDataLen != 0)
301 _unmarshal_uint64(pData, pDataLen, &dest->data_offset);
302 else
303 return 0;
305 else
306 dest->data_offset = dest->dir_offset + dest->dir_len;
308 return 1;
311 /* structure of ITSP headers */
312 #define _CHM_ITSP_V1_LEN (0x54)
313 struct chmItspHeader
315 char signature[4]; /* 0 (ITSP) */
316 Int32 version; /* 4 */
317 Int32 header_len; /* 8 */
318 Int32 unknown_000c; /* c */
319 UInt32 block_len; /* 10 */
320 Int32 blockidx_intvl; /* 14 */
321 Int32 index_depth; /* 18 */
322 Int32 index_root; /* 1c */
323 Int32 index_head; /* 20 */
324 Int32 unknown_0024; /* 24 */
325 UInt32 num_blocks; /* 28 */
326 Int32 unknown_002c; /* 2c */
327 UInt32 lang_id; /* 30 */
328 UChar system_uuid[16]; /* 34 */
329 UChar unknown_0044[16]; /* 44 */
330 }; /* __attribute__ ((aligned (1))); */
332 static int _unmarshal_itsp_header(unsigned char **pData,
333 unsigned int *pDataLen,
334 struct chmItspHeader *dest)
336 /* we only know how to deal with a 0x54 byte structures */
337 if (*pDataLen != _CHM_ITSP_V1_LEN)
338 return 0;
340 /* unmarshal fields */
341 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
342 _unmarshal_int32 (pData, pDataLen, &dest->version);
343 _unmarshal_int32 (pData, pDataLen, &dest->header_len);
344 _unmarshal_int32 (pData, pDataLen, &dest->unknown_000c);
345 _unmarshal_uint32 (pData, pDataLen, &dest->block_len);
346 _unmarshal_int32 (pData, pDataLen, &dest->blockidx_intvl);
347 _unmarshal_int32 (pData, pDataLen, &dest->index_depth);
348 _unmarshal_int32 (pData, pDataLen, &dest->index_root);
349 _unmarshal_int32 (pData, pDataLen, &dest->index_head);
350 _unmarshal_int32 (pData, pDataLen, &dest->unknown_0024);
351 _unmarshal_uint32 (pData, pDataLen, &dest->num_blocks);
352 _unmarshal_int32 (pData, pDataLen, &dest->unknown_002c);
353 _unmarshal_uint32 (pData, pDataLen, &dest->lang_id);
354 _unmarshal_uuid (pData, pDataLen, dest->system_uuid);
355 _unmarshal_uchar_array(pData, pDataLen, dest->unknown_0044, 16);
357 /* error check the data */
358 if (memcmp(dest->signature, "ITSP", 4) != 0)
359 return 0;
360 if (dest->version != 1)
361 return 0;
362 if (dest->header_len != _CHM_ITSP_V1_LEN)
363 return 0;
365 return 1;
368 /* structure of PMGL headers */
369 static const char _chm_pmgl_marker[4] = "PMGL";
370 #define _CHM_PMGL_LEN (0x14)
371 struct chmPmglHeader
373 char signature[4]; /* 0 (PMGL) */
374 UInt32 free_space; /* 4 */
375 UInt32 unknown_0008; /* 8 */
376 Int32 block_prev; /* c */
377 Int32 block_next; /* 10 */
378 }; /* __attribute__ ((aligned (1))); */
380 static int _unmarshal_pmgl_header(unsigned char **pData,
381 unsigned int *pDataLen,
382 struct chmPmglHeader *dest)
384 /* we only know how to deal with a 0x14 byte structures */
385 if (*pDataLen != _CHM_PMGL_LEN)
386 return 0;
388 /* unmarshal fields */
389 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
390 _unmarshal_uint32 (pData, pDataLen, &dest->free_space);
391 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_0008);
392 _unmarshal_int32 (pData, pDataLen, &dest->block_prev);
393 _unmarshal_int32 (pData, pDataLen, &dest->block_next);
395 /* check structure */
396 if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0)
397 return 0;
399 return 1;
402 /* structure of PMGI headers */
403 static const char _chm_pmgi_marker[4] = "PMGI";
404 #define _CHM_PMGI_LEN (0x08)
405 struct chmPmgiHeader
407 char signature[4]; /* 0 (PMGI) */
408 UInt32 free_space; /* 4 */
409 }; /* __attribute__ ((aligned (1))); */
411 static int _unmarshal_pmgi_header(unsigned char **pData,
412 unsigned int *pDataLen,
413 struct chmPmgiHeader *dest)
415 /* we only know how to deal with a 0x8 byte structures */
416 if (*pDataLen != _CHM_PMGI_LEN)
417 return 0;
419 /* unmarshal fields */
420 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
421 _unmarshal_uint32 (pData, pDataLen, &dest->free_space);
423 /* check structure */
424 if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0)
425 return 0;
427 return 1;
430 /* structure of LZXC reset table */
431 #define _CHM_LZXC_RESETTABLE_V1_LEN (0x28)
432 struct chmLzxcResetTable
434 UInt32 version;
435 UInt32 block_count;
436 UInt32 unknown;
437 UInt32 table_offset;
438 UInt64 uncompressed_len;
439 UInt64 compressed_len;
440 UInt64 block_len;
441 }; /* __attribute__ ((aligned (1))); */
443 static int _unmarshal_lzxc_reset_table(unsigned char **pData,
444 unsigned int *pDataLen,
445 struct chmLzxcResetTable *dest)
447 /* we only know how to deal with a 0x28 byte structures */
448 if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN)
449 return 0;
451 /* unmarshal fields */
452 _unmarshal_uint32 (pData, pDataLen, &dest->version);
453 _unmarshal_uint32 (pData, pDataLen, &dest->block_count);
454 _unmarshal_uint32 (pData, pDataLen, &dest->unknown);
455 _unmarshal_uint32 (pData, pDataLen, &dest->table_offset);
456 _unmarshal_uint64 (pData, pDataLen, &dest->uncompressed_len);
457 _unmarshal_uint64 (pData, pDataLen, &dest->compressed_len);
458 _unmarshal_uint64 (pData, pDataLen, &dest->block_len);
460 /* check structure */
461 if (dest->version != 2)
462 return 0;
464 return 1;
467 /* structure of LZXC control data block */
468 #define _CHM_LZXC_MIN_LEN (0x18)
469 #define _CHM_LZXC_V2_LEN (0x1c)
470 struct chmLzxcControlData
472 UInt32 size; /* 0 */
473 char signature[4]; /* 4 (LZXC) */
474 UInt32 version; /* 8 */
475 UInt32 resetInterval; /* c */
476 UInt32 windowSize; /* 10 */
477 UInt32 windowsPerReset; /* 14 */
478 UInt32 unknown_18; /* 18 */
481 static int _unmarshal_lzxc_control_data(unsigned char **pData,
482 unsigned int *pDataLen,
483 struct chmLzxcControlData *dest)
485 /* we want at least 0x18 bytes */
486 if (*pDataLen < _CHM_LZXC_MIN_LEN)
487 return 0;
489 /* unmarshal fields */
490 _unmarshal_uint32 (pData, pDataLen, &dest->size);
491 _unmarshal_char_array(pData, pDataLen, dest->signature, 4);
492 _unmarshal_uint32 (pData, pDataLen, &dest->version);
493 _unmarshal_uint32 (pData, pDataLen, &dest->resetInterval);
494 _unmarshal_uint32 (pData, pDataLen, &dest->windowSize);
495 _unmarshal_uint32 (pData, pDataLen, &dest->windowsPerReset);
497 if (*pDataLen >= _CHM_LZXC_V2_LEN)
498 _unmarshal_uint32 (pData, pDataLen, &dest->unknown_18);
499 else
500 dest->unknown_18 = 0;
502 if (dest->version == 2)
504 dest->resetInterval *= 0x8000;
505 dest->windowSize *= 0x8000;
507 if (dest->windowSize == 0 || dest->resetInterval == 0)
508 return 0;
510 /* for now, only support resetInterval a multiple of windowSize/2 */
511 if (dest->windowSize == 1)
512 return 0;
513 if ((dest->resetInterval % (dest->windowSize/2)) != 0)
514 return 0;
516 /* check structure */
517 if (memcmp(dest->signature, "LZXC", 4) != 0)
518 return 0;
520 return 1;
523 /* the structure used for chm file handles */
524 struct chmFile
526 HANDLE fd;
528 CRITICAL_SECTION mutex;
529 CRITICAL_SECTION lzx_mutex;
530 CRITICAL_SECTION cache_mutex;
532 UInt64 dir_offset;
533 UInt64 dir_len;
534 UInt64 data_offset;
535 Int32 index_root;
536 Int32 index_head;
537 UInt32 block_len;
539 UInt64 span;
540 struct chmUnitInfo rt_unit;
541 struct chmUnitInfo cn_unit;
542 struct chmLzxcResetTable reset_table;
544 /* LZX control data */
545 int compression_enabled;
546 UInt32 window_size;
547 UInt32 reset_interval;
548 UInt32 reset_blkcount;
550 /* decompressor state */
551 struct LZXstate *lzx_state;
552 int lzx_last_block;
554 /* cache for decompressed blocks */
555 UChar **cache_blocks;
556 Int64 *cache_block_indices;
557 Int32 cache_num_blocks;
561 * utility functions local to this module
564 /* utility function to handle differences between {pread,read}(64)? */
565 static Int64 _chm_fetch_bytes(struct chmFile *h,
566 UChar *buf,
567 UInt64 os,
568 Int64 len)
570 Int64 readLen=0;
571 if (h->fd == CHM_NULL_FD)
572 return readLen;
574 CHM_ACQUIRE_LOCK(h->mutex);
575 /* NOTE: this might be better done with CreateFileMapping, et cetera... */
577 LARGE_INTEGER old_pos, new_pos;
578 DWORD actualLen=0;
580 /* awkward Win32 Seek/Tell */
581 new_pos.QuadPart = 0;
582 SetFilePointerEx( h->fd, new_pos, &old_pos, FILE_CURRENT );
583 new_pos.QuadPart = os;
584 SetFilePointerEx( h->fd, new_pos, NULL, FILE_BEGIN );
586 /* read the data */
587 if (ReadFile(h->fd,
588 buf,
589 (DWORD)len,
590 &actualLen,
591 NULL))
592 readLen = actualLen;
593 else
594 readLen = 0;
596 /* restore original position */
597 SetFilePointerEx( h->fd, old_pos, NULL, FILE_BEGIN );
599 CHM_RELEASE_LOCK(h->mutex);
600 return readLen;
604 * set a parameter on the file handle.
605 * valid parameter types:
606 * CHM_PARAM_MAX_BLOCKS_CACHED:
607 * how many decompressed blocks should be cached? A simple
608 * caching scheme is used, wherein the index of the block is
609 * used as a hash value, and hash collision results in the
610 * invalidation of the previously cached block.
612 static void chm_set_param(struct chmFile *h,
613 int paramType,
614 int paramVal)
616 switch (paramType)
618 case CHM_PARAM_MAX_BLOCKS_CACHED:
619 CHM_ACQUIRE_LOCK(h->cache_mutex);
620 if (paramVal != h->cache_num_blocks)
622 UChar **newBlocks;
623 Int64 *newIndices;
624 int i;
626 /* allocate new cached blocks */
627 newBlocks = malloc(paramVal * sizeof (UChar *));
628 newIndices = malloc(paramVal * sizeof (UInt64));
629 for (i=0; i<paramVal; i++)
631 newBlocks[i] = NULL;
632 newIndices[i] = 0;
635 /* re-distribute old cached blocks */
636 if (h->cache_blocks)
638 for (i=0; i<h->cache_num_blocks; i++)
640 int newSlot = (int)(h->cache_block_indices[i] % paramVal);
642 if (h->cache_blocks[i])
644 /* in case of collision, destroy newcomer */
645 if (newBlocks[newSlot])
647 free(h->cache_blocks[i]);
648 h->cache_blocks[i] = NULL;
650 else
652 newBlocks[newSlot] = h->cache_blocks[i];
653 newIndices[newSlot] =
654 h->cache_block_indices[i];
659 free(h->cache_blocks);
660 free(h->cache_block_indices);
663 /* now, set new values */
664 h->cache_blocks = newBlocks;
665 h->cache_block_indices = newIndices;
666 h->cache_num_blocks = paramVal;
668 CHM_RELEASE_LOCK(h->cache_mutex);
669 break;
671 default:
672 break;
676 /* open an ITS archive */
677 struct chmFile *chm_openW(const WCHAR *filename)
679 unsigned char sbuffer[256];
680 unsigned int sremain;
681 unsigned char *sbufpos;
682 struct chmFile *newHandle=NULL;
683 struct chmItsfHeader itsfHeader;
684 struct chmItspHeader itspHeader;
685 #if 0
686 struct chmUnitInfo uiSpan;
687 #endif
688 struct chmUnitInfo uiLzxc;
689 struct chmLzxcControlData ctlData;
691 /* allocate handle */
692 newHandle = malloc(sizeof(struct chmFile));
693 newHandle->fd = CHM_NULL_FD;
694 newHandle->lzx_state = NULL;
695 newHandle->cache_blocks = NULL;
696 newHandle->cache_block_indices = NULL;
697 newHandle->cache_num_blocks = 0;
699 /* open file */
700 if ((newHandle->fd=CreateFileW(filename,
701 GENERIC_READ,
702 FILE_SHARE_READ,
703 NULL,
704 OPEN_EXISTING,
705 FILE_ATTRIBUTE_NORMAL,
706 NULL)) == CHM_NULL_FD)
708 free(newHandle);
709 return NULL;
712 /* initialize mutexes, if needed */
713 InitializeCriticalSection(&newHandle->mutex);
714 InitializeCriticalSection(&newHandle->lzx_mutex);
715 InitializeCriticalSection(&newHandle->cache_mutex);
717 /* read and verify header */
718 sremain = _CHM_ITSF_V3_LEN;
719 sbufpos = sbuffer;
720 if (_chm_fetch_bytes(newHandle, sbuffer, (UInt64)0, sremain) != sremain ||
721 !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader))
723 chm_close(newHandle);
724 return NULL;
727 /* stash important values from header */
728 newHandle->dir_offset = itsfHeader.dir_offset;
729 newHandle->dir_len = itsfHeader.dir_len;
730 newHandle->data_offset = itsfHeader.data_offset;
732 /* now, read and verify the directory header chunk */
733 sremain = _CHM_ITSP_V1_LEN;
734 sbufpos = sbuffer;
735 if (_chm_fetch_bytes(newHandle, sbuffer,
736 (UInt64)itsfHeader.dir_offset, sremain) != sremain ||
737 !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader))
739 chm_close(newHandle);
740 return NULL;
743 /* grab essential information from ITSP header */
744 newHandle->dir_offset += itspHeader.header_len;
745 newHandle->dir_len -= itspHeader.header_len;
746 newHandle->index_root = itspHeader.index_root;
747 newHandle->index_head = itspHeader.index_head;
748 newHandle->block_len = itspHeader.block_len;
750 /* if the index root is -1, this means we don't have any PMGI blocks.
751 * as a result, we must use the sole PMGL block as the index root
753 if (newHandle->index_root == -1)
754 newHandle->index_root = newHandle->index_head;
756 /* By default, compression is enabled. */
757 newHandle->compression_enabled = 1;
759 /* Jed, Sun Jun 27: 'span' doesn't seem to be used anywhere?! */
760 #if 0
761 /* fetch span */
762 if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
763 _CHMU_SPANINFO,
764 &uiSpan) ||
765 uiSpan.space == CHM_COMPRESSED)
767 chm_close(newHandle);
768 return NULL;
771 /* N.B.: we've already checked that uiSpan is in the uncompressed section,
772 * so this should not require attempting to decompress, which may
773 * rely on having a valid "span"
775 sremain = 8;
776 sbufpos = sbuffer;
777 if (chm_retrieve_object(newHandle, &uiSpan, sbuffer,
778 0, sremain) != sremain ||
779 !_unmarshal_uint64(&sbufpos, &sremain, &newHandle->span))
781 chm_close(newHandle);
782 return NULL;
784 #endif
786 /* prefetch most commonly needed unit infos */
787 if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
788 _CHMU_RESET_TABLE,
789 &newHandle->rt_unit) ||
790 newHandle->rt_unit.space == CHM_COMPRESSED ||
791 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
792 _CHMU_CONTENT,
793 &newHandle->cn_unit) ||
794 newHandle->cn_unit.space == CHM_COMPRESSED ||
795 CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
796 _CHMU_LZXC_CONTROLDATA,
797 &uiLzxc) ||
798 uiLzxc.space == CHM_COMPRESSED)
800 newHandle->compression_enabled = 0;
803 /* read reset table info */
804 if (newHandle->compression_enabled)
806 sremain = _CHM_LZXC_RESETTABLE_V1_LEN;
807 sbufpos = sbuffer;
808 if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer,
809 0, sremain) != sremain ||
810 !_unmarshal_lzxc_reset_table(&sbufpos, &sremain,
811 &newHandle->reset_table))
813 newHandle->compression_enabled = 0;
817 /* read control data */
818 if (newHandle->compression_enabled)
820 sremain = (unsigned long)uiLzxc.length;
821 sbufpos = sbuffer;
822 if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer,
823 0, sremain) != sremain ||
824 !_unmarshal_lzxc_control_data(&sbufpos, &sremain,
825 &ctlData))
827 newHandle->compression_enabled = 0;
830 newHandle->window_size = ctlData.windowSize;
831 newHandle->reset_interval = ctlData.resetInterval;
833 /* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */
834 /* must be multiplied by this formerly unknown ctrl data field in */
835 /* order to decompress some files. */
836 #if 0
837 newHandle->reset_blkcount = newHandle->reset_interval /
838 (newHandle->window_size / 2);
839 #else
840 newHandle->reset_blkcount = newHandle->reset_interval /
841 (newHandle->window_size / 2) *
842 ctlData.windowsPerReset;
843 #endif
846 /* initialize cache */
847 chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
848 CHM_MAX_BLOCKS_CACHED);
850 return newHandle;
853 /* close an ITS archive */
854 void chm_close(struct chmFile *h)
856 if (h != NULL)
858 if (h->fd != CHM_NULL_FD)
859 CHM_CLOSE_FILE(h->fd);
860 h->fd = CHM_NULL_FD;
862 DeleteCriticalSection(&h->mutex);
863 DeleteCriticalSection(&h->lzx_mutex);
864 DeleteCriticalSection(&h->cache_mutex);
866 if (h->lzx_state)
867 LZXteardown(h->lzx_state);
868 h->lzx_state = NULL;
870 if (h->cache_blocks)
872 int i;
873 for (i=0; i<h->cache_num_blocks; i++)
875 if (h->cache_blocks[i])
876 free(h->cache_blocks[i]);
878 free(h->cache_blocks);
879 h->cache_blocks = NULL;
882 free(h->cache_block_indices);
883 h->cache_block_indices = NULL;
885 free(h);
890 * helper methods for chm_resolve_object
893 /* skip a compressed dword */
894 static void _chm_skip_cword(UChar **pEntry)
896 while (*(*pEntry)++ >= 0x80)
900 /* skip the data from a PMGL entry */
901 static void _chm_skip_PMGL_entry_data(UChar **pEntry)
903 _chm_skip_cword(pEntry);
904 _chm_skip_cword(pEntry);
905 _chm_skip_cword(pEntry);
908 /* parse a compressed dword */
909 static UInt64 _chm_parse_cword(UChar **pEntry)
911 UInt64 accum = 0;
912 UChar temp;
913 while ((temp=*(*pEntry)++) >= 0x80)
915 accum <<= 7;
916 accum += temp & 0x7f;
919 return (accum << 7) + temp;
922 /* parse a utf-8 string into an ASCII char buffer */
923 static int _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path)
925 /* MJM - Modified to return real Unicode strings */
926 while (count != 0)
928 *path++ = (*(*pEntry)++);
929 --count;
932 *path = '\0';
933 return 1;
936 /* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */
937 static int _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui)
939 UInt64 strLen;
941 /* parse str len */
942 strLen = _chm_parse_cword(pEntry);
943 if (strLen > CHM_MAX_PATHLEN)
944 return 0;
946 /* parse path */
947 if (! _chm_parse_UTF8(pEntry, strLen, ui->path))
948 return 0;
950 /* parse info */
951 ui->space = (int)_chm_parse_cword(pEntry);
952 ui->start = _chm_parse_cword(pEntry);
953 ui->length = _chm_parse_cword(pEntry);
954 return 1;
957 /* find an exact entry in PMGL; return NULL if we fail */
958 static UChar *_chm_find_in_PMGL(UChar *page_buf,
959 UInt32 block_len,
960 const WCHAR *objPath)
962 /* XXX: modify this to do a binary search using the nice index structure
963 * that is provided for us.
965 struct chmPmglHeader header;
966 UInt32 hremain;
967 UChar *end;
968 UChar *cur;
969 UChar *temp;
970 UInt64 strLen;
971 WCHAR buffer[CHM_MAX_PATHLEN+1];
973 /* figure out where to start and end */
974 cur = page_buf;
975 hremain = _CHM_PMGL_LEN;
976 if (! _unmarshal_pmgl_header(&cur, &hremain, &header))
977 return NULL;
978 end = page_buf + block_len - (header.free_space);
980 /* now, scan progressively */
981 while (cur < end)
983 /* grab the name */
984 temp = cur;
985 strLen = _chm_parse_cword(&cur);
986 if (! _chm_parse_UTF8(&cur, strLen, buffer))
987 return NULL;
989 /* check if it is the right name */
990 if (! strcmpiW(buffer, objPath))
991 return temp;
993 _chm_skip_PMGL_entry_data(&cur);
996 return NULL;
999 /* find which block should be searched next for the entry; -1 if no block */
1000 static Int32 _chm_find_in_PMGI(UChar *page_buf,
1001 UInt32 block_len,
1002 const WCHAR *objPath)
1004 /* XXX: modify this to do a binary search using the nice index structure
1005 * that is provided for us
1007 struct chmPmgiHeader header;
1008 UInt32 hremain;
1009 int page=-1;
1010 UChar *end;
1011 UChar *cur;
1012 UInt64 strLen;
1013 WCHAR buffer[CHM_MAX_PATHLEN+1];
1015 /* figure out where to start and end */
1016 cur = page_buf;
1017 hremain = _CHM_PMGI_LEN;
1018 if (! _unmarshal_pmgi_header(&cur, &hremain, &header))
1019 return -1;
1020 end = page_buf + block_len - (header.free_space);
1022 /* now, scan progressively */
1023 while (cur < end)
1025 /* grab the name */
1026 strLen = _chm_parse_cword(&cur);
1027 if (! _chm_parse_UTF8(&cur, strLen, buffer))
1028 return -1;
1030 /* check if it is the right name */
1031 if (strcmpiW(buffer, objPath) > 0)
1032 return page;
1034 /* load next value for path */
1035 page = (int)_chm_parse_cword(&cur);
1038 return page;
1041 /* resolve a particular object from the archive */
1042 int chm_resolve_object(struct chmFile *h,
1043 const WCHAR *objPath,
1044 struct chmUnitInfo *ui)
1047 * XXX: implement caching scheme for dir pages
1050 Int32 curPage;
1052 /* buffer to hold whatever page we're looking at */
1053 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len);
1055 /* starting page */
1056 curPage = h->index_root;
1058 /* until we have either returned or given up */
1059 while (curPage != -1)
1062 /* try to fetch the index page */
1063 if (_chm_fetch_bytes(h, page_buf,
1064 (UInt64)h->dir_offset + (UInt64)curPage*h->block_len,
1065 h->block_len) != h->block_len)
1067 HeapFree(GetProcessHeap(), 0, page_buf);
1068 return CHM_RESOLVE_FAILURE;
1071 /* now, if it is a leaf node: */
1072 if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0)
1074 /* scan block */
1075 UChar *pEntry = _chm_find_in_PMGL(page_buf,
1076 h->block_len,
1077 objPath);
1078 if (pEntry == NULL)
1080 HeapFree(GetProcessHeap(), 0, page_buf);
1081 return CHM_RESOLVE_FAILURE;
1084 /* parse entry and return */
1085 _chm_parse_PMGL_entry(&pEntry, ui);
1086 HeapFree(GetProcessHeap(), 0, page_buf);
1087 return CHM_RESOLVE_SUCCESS;
1090 /* else, if it is a branch node: */
1091 else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0)
1092 curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath);
1094 /* else, we are confused. give up. */
1095 else
1097 HeapFree(GetProcessHeap(), 0, page_buf);
1098 return CHM_RESOLVE_FAILURE;
1102 /* didn't find anything. fail. */
1103 HeapFree(GetProcessHeap(), 0, page_buf);
1104 return CHM_RESOLVE_FAILURE;
1108 * utility methods for dealing with compressed data
1111 /* get the bounds of a compressed block. return 0 on failure */
1112 static int _chm_get_cmpblock_bounds(struct chmFile *h,
1113 UInt64 block,
1114 UInt64 *start,
1115 Int64 *len)
1117 UChar buffer[8], *dummy;
1118 UInt32 remain;
1120 /* for all but the last block, use the reset table */
1121 if (block < h->reset_table.block_count-1)
1123 /* unpack the start address */
1124 dummy = buffer;
1125 remain = 8;
1126 if (_chm_fetch_bytes(h, buffer,
1127 (UInt64)h->data_offset
1128 + (UInt64)h->rt_unit.start
1129 + (UInt64)h->reset_table.table_offset
1130 + (UInt64)block*8,
1131 remain) != remain ||
1132 !_unmarshal_uint64(&dummy, &remain, start))
1133 return 0;
1135 /* unpack the end address */
1136 dummy = buffer;
1137 remain = 8;
1138 if (_chm_fetch_bytes(h, buffer,
1139 (UInt64)h->data_offset
1140 + (UInt64)h->rt_unit.start
1141 + (UInt64)h->reset_table.table_offset
1142 + (UInt64)block*8 + 8,
1143 remain) != remain ||
1144 !_unmarshal_int64(&dummy, &remain, len))
1145 return 0;
1148 /* for the last block, use the span in addition to the reset table */
1149 else
1151 /* unpack the start address */
1152 dummy = buffer;
1153 remain = 8;
1154 if (_chm_fetch_bytes(h, buffer,
1155 (UInt64)h->data_offset
1156 + (UInt64)h->rt_unit.start
1157 + (UInt64)h->reset_table.table_offset
1158 + (UInt64)block*8,
1159 remain) != remain ||
1160 !_unmarshal_uint64(&dummy, &remain, start))
1161 return 0;
1163 *len = h->reset_table.compressed_len;
1166 /* compute the length and absolute start address */
1167 *len -= *start;
1168 *start += h->data_offset + h->cn_unit.start;
1170 return 1;
1173 /* decompress the block. must have lzx_mutex. */
1174 static Int64 _chm_decompress_block(struct chmFile *h,
1175 UInt64 block,
1176 UChar **ubuffer)
1178 UChar *cbuffer = HeapAlloc( GetProcessHeap(), 0,
1179 ((unsigned int)h->reset_table.block_len + 6144));
1180 UInt64 cmpStart; /* compressed start */
1181 Int64 cmpLen; /* compressed len */
1182 int indexSlot; /* cache index slot */
1183 UChar *lbuffer; /* local buffer ptr */
1184 UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset intvl. aln. */
1185 UInt32 i; /* local loop index */
1187 /* let the caching system pull its weight! */
1188 if (block - blockAlign <= h->lzx_last_block &&
1189 block >= h->lzx_last_block)
1190 blockAlign = (block - h->lzx_last_block);
1192 /* check if we need previous blocks */
1193 if (blockAlign != 0)
1195 /* fetch all required previous blocks since last reset */
1196 for (i = blockAlign; i > 0; i--)
1198 UInt32 curBlockIdx = block - i;
1200 /* check if we most recently decompressed the previous block */
1201 if (h->lzx_last_block != curBlockIdx)
1203 if ((curBlockIdx % h->reset_blkcount) == 0)
1205 #ifdef CHM_DEBUG
1206 fprintf(stderr, "***RESET (1)***\n");
1207 #endif
1208 LZXreset(h->lzx_state);
1211 indexSlot = (int)((curBlockIdx) % h->cache_num_blocks);
1212 h->cache_block_indices[indexSlot] = curBlockIdx;
1213 if (! h->cache_blocks[indexSlot])
1214 h->cache_blocks[indexSlot] = malloc( (unsigned int)(h->reset_table.block_len));
1215 lbuffer = h->cache_blocks[indexSlot];
1217 /* decompress the previous block */
1218 #ifdef CHM_DEBUG
1219 fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx);
1220 #endif
1221 if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) ||
1222 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen ||
1223 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
1224 (int)h->reset_table.block_len) != DECR_OK)
1226 #ifdef CHM_DEBUG
1227 fprintf(stderr, " (DECOMPRESS FAILED!)\n");
1228 #endif
1229 HeapFree(GetProcessHeap(), 0, cbuffer);
1230 return (Int64)0;
1233 h->lzx_last_block = (int)curBlockIdx;
1237 else
1239 if ((block % h->reset_blkcount) == 0)
1241 #ifdef CHM_DEBUG
1242 fprintf(stderr, "***RESET (2)***\n");
1243 #endif
1244 LZXreset(h->lzx_state);
1248 /* allocate slot in cache */
1249 indexSlot = (int)(block % h->cache_num_blocks);
1250 h->cache_block_indices[indexSlot] = block;
1251 if (! h->cache_blocks[indexSlot])
1252 h->cache_blocks[indexSlot] = malloc( ((unsigned int)h->reset_table.block_len));
1253 lbuffer = h->cache_blocks[indexSlot];
1254 *ubuffer = lbuffer;
1256 /* decompress the block we actually want */
1257 #ifdef CHM_DEBUG
1258 fprintf(stderr, "Decompressing block #%4d (REAL )\n", block);
1259 #endif
1260 if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen) ||
1261 _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen ||
1262 LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
1263 (int)h->reset_table.block_len) != DECR_OK)
1265 #ifdef CHM_DEBUG
1266 fprintf(stderr, " (DECOMPRESS FAILED!)\n");
1267 #endif
1268 HeapFree(GetProcessHeap(), 0, cbuffer);
1269 return (Int64)0;
1271 h->lzx_last_block = (int)block;
1273 /* XXX: modify LZX routines to return the length of the data they
1274 * decompressed and return that instead, for an extra sanity check.
1276 HeapFree(GetProcessHeap(), 0, cbuffer);
1277 return h->reset_table.block_len;
1280 /* grab a region from a compressed block */
1281 static Int64 _chm_decompress_region(struct chmFile *h,
1282 UChar *buf,
1283 UInt64 start,
1284 Int64 len)
1286 UInt64 nBlock, nOffset;
1287 UInt64 nLen;
1288 UInt64 gotLen;
1289 UChar *ubuffer = NULL;
1291 if (len <= 0)
1292 return (Int64)0;
1294 /* figure out what we need to read */
1295 nBlock = start / h->reset_table.block_len;
1296 nOffset = start % h->reset_table.block_len;
1297 nLen = len;
1298 if (nLen > (h->reset_table.block_len - nOffset))
1299 nLen = h->reset_table.block_len - nOffset;
1301 /* if block is cached, return data from it. */
1302 CHM_ACQUIRE_LOCK(h->lzx_mutex);
1303 CHM_ACQUIRE_LOCK(h->cache_mutex);
1304 if (h->cache_block_indices[nBlock % h->cache_num_blocks] == nBlock &&
1305 h->cache_blocks[nBlock % h->cache_num_blocks] != NULL)
1307 memcpy(buf,
1308 h->cache_blocks[nBlock % h->cache_num_blocks] + nOffset,
1309 (unsigned int)nLen);
1310 CHM_RELEASE_LOCK(h->cache_mutex);
1311 CHM_RELEASE_LOCK(h->lzx_mutex);
1312 return nLen;
1314 CHM_RELEASE_LOCK(h->cache_mutex);
1316 /* data request not satisfied, so... start up the decompressor machine */
1317 if (! h->lzx_state)
1319 int window_size = ffs(h->window_size) - 1;
1320 h->lzx_last_block = -1;
1321 h->lzx_state = LZXinit(window_size);
1324 /* decompress some data */
1325 gotLen = _chm_decompress_block(h, nBlock, &ubuffer);
1326 if (gotLen < nLen)
1327 nLen = gotLen;
1328 memcpy(buf, ubuffer+nOffset, (unsigned int)nLen);
1329 CHM_RELEASE_LOCK(h->lzx_mutex);
1330 return nLen;
1333 /* retrieve (part of) an object */
1334 LONGINT64 chm_retrieve_object(struct chmFile *h,
1335 struct chmUnitInfo *ui,
1336 unsigned char *buf,
1337 LONGUINT64 addr,
1338 LONGINT64 len)
1340 /* must be valid file handle */
1341 if (h == NULL)
1342 return (Int64)0;
1344 /* starting address must be in correct range */
1345 if (addr < 0 || addr >= ui->length)
1346 return (Int64)0;
1348 /* clip length */
1349 if (addr + len > ui->length)
1350 len = ui->length - addr;
1352 /* if the file is uncompressed, it's simple */
1353 if (ui->space == CHM_UNCOMPRESSED)
1355 /* read data */
1356 return _chm_fetch_bytes(h,
1357 buf,
1358 (UInt64)h->data_offset + (UInt64)ui->start + (UInt64)addr,
1359 len);
1362 /* else if the file is compressed, it's a little trickier */
1363 else /* ui->space == CHM_COMPRESSED */
1365 Int64 swath=0, total=0;
1367 /* if compression is not enabled for this file... */
1368 if (! h->compression_enabled)
1369 return total;
1371 do {
1373 /* swill another mouthful */
1374 swath = _chm_decompress_region(h, buf, ui->start + addr, len);
1376 /* if we didn't get any... */
1377 if (swath == 0)
1378 return total;
1380 /* update stats */
1381 total += swath;
1382 len -= swath;
1383 addr += swath;
1384 buf += swath;
1386 } while (len != 0);
1388 return total;
1392 /* enumerate the objects in the .chm archive */
1393 int chm_enumerate(struct chmFile *h,
1394 int what,
1395 CHM_ENUMERATOR e,
1396 void *context)
1398 Int32 curPage;
1400 /* buffer to hold whatever page we're looking at */
1401 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, (unsigned int)h->block_len);
1402 struct chmPmglHeader header;
1403 UChar *end;
1404 UChar *cur;
1405 unsigned int lenRemain;
1406 UInt64 ui_path_len;
1408 /* the current ui */
1409 struct chmUnitInfo ui;
1410 int flag;
1412 /* starting page */
1413 curPage = h->index_head;
1415 /* until we have either returned or given up */
1416 while (curPage != -1)
1419 /* try to fetch the index page */
1420 if (_chm_fetch_bytes(h,
1421 page_buf,
1422 (UInt64)h->dir_offset + (UInt64)curPage*h->block_len,
1423 h->block_len) != h->block_len)
1425 HeapFree(GetProcessHeap(), 0, page_buf);
1426 return 0;
1429 /* figure out start and end for this page */
1430 cur = page_buf;
1431 lenRemain = _CHM_PMGL_LEN;
1432 if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header))
1434 HeapFree(GetProcessHeap(), 0, page_buf);
1435 return 0;
1437 end = page_buf + h->block_len - (header.free_space);
1439 /* loop over this page */
1440 while (cur < end)
1442 if (! _chm_parse_PMGL_entry(&cur, &ui))
1444 HeapFree(GetProcessHeap(), 0, page_buf);
1445 return 0;
1448 /* get the length of the path */
1449 ui_path_len = strlenW(ui.path)-1;
1451 /* check for DIRS */
1452 if (ui.path[ui_path_len] == '/' && !(what & CHM_ENUMERATE_DIRS))
1453 continue;
1455 /* check for FILES */
1456 if (ui.path[ui_path_len] != '/' && !(what & CHM_ENUMERATE_FILES))
1457 continue;
1459 /* check for NORMAL vs. META */
1460 if (ui.path[0] == '/')
1463 /* check for NORMAL vs. SPECIAL */
1464 if (ui.path[1] == '#' || ui.path[1] == '$')
1465 flag = CHM_ENUMERATE_SPECIAL;
1466 else
1467 flag = CHM_ENUMERATE_NORMAL;
1469 else
1470 flag = CHM_ENUMERATE_META;
1471 if (! (what & flag))
1472 continue;
1474 /* call the enumerator */
1476 int status = (*e)(h, &ui, context);
1477 switch (status)
1479 case CHM_ENUMERATOR_FAILURE:
1480 HeapFree(GetProcessHeap(), 0, page_buf);
1481 return 0;
1482 case CHM_ENUMERATOR_CONTINUE:
1483 break;
1484 case CHM_ENUMERATOR_SUCCESS:
1485 HeapFree(GetProcessHeap(), 0, page_buf);
1486 return 1;
1487 default:
1488 break;
1493 /* advance to next page */
1494 curPage = header.block_next;
1497 HeapFree(GetProcessHeap(), 0, page_buf);
1498 return 1;
1501 int chm_enumerate_dir(struct chmFile *h,
1502 const WCHAR *prefix,
1503 int what,
1504 CHM_ENUMERATOR e,
1505 void *context)
1508 * XXX: do this efficiently (i.e. using the tree index)
1511 Int32 curPage;
1513 /* buffer to hold whatever page we're looking at */
1514 UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, (unsigned int)h->block_len);
1515 struct chmPmglHeader header;
1516 UChar *end;
1517 UChar *cur;
1518 unsigned int lenRemain;
1520 /* set to 1 once we've started */
1521 int it_has_begun=0;
1523 /* the current ui */
1524 struct chmUnitInfo ui;
1525 int flag;
1526 UInt64 ui_path_len;
1528 /* the length of the prefix */
1529 WCHAR prefixRectified[CHM_MAX_PATHLEN+1];
1530 int prefixLen;
1531 WCHAR lastPath[CHM_MAX_PATHLEN];
1532 int lastPathLen;
1534 /* starting page */
1535 curPage = h->index_head;
1537 /* initialize pathname state */
1538 lstrcpynW(prefixRectified, prefix, CHM_MAX_PATHLEN);
1539 prefixLen = strlenW(prefixRectified);
1540 if (prefixLen != 0)
1542 if (prefixRectified[prefixLen-1] != '/')
1544 prefixRectified[prefixLen] = '/';
1545 prefixRectified[prefixLen+1] = '\0';
1546 ++prefixLen;
1549 lastPath[0] = '\0';
1550 lastPathLen = -1;
1552 /* until we have either returned or given up */
1553 while (curPage != -1)
1556 /* try to fetch the index page */
1557 if (_chm_fetch_bytes(h,
1558 page_buf,
1559 (UInt64)h->dir_offset + (UInt64)curPage*h->block_len,
1560 h->block_len) != h->block_len)
1562 HeapFree(GetProcessHeap(), 0, page_buf);
1563 return 0;
1566 /* figure out start and end for this page */
1567 cur = page_buf;
1568 lenRemain = _CHM_PMGL_LEN;
1569 if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header))
1571 HeapFree(GetProcessHeap(), 0, page_buf);
1572 return 0;
1574 end = page_buf + h->block_len - (header.free_space);
1576 /* loop over this page */
1577 while (cur < end)
1579 if (! _chm_parse_PMGL_entry(&cur, &ui))
1581 HeapFree(GetProcessHeap(), 0, page_buf);
1582 return 0;
1585 /* check if we should start */
1586 if (! it_has_begun)
1588 if (ui.length == 0 && strncmpiW(ui.path, prefixRectified, prefixLen) == 0)
1589 it_has_begun = 1;
1590 else
1591 continue;
1593 if (ui.path[prefixLen] == '\0')
1594 continue;
1597 /* check if we should stop */
1598 else
1600 if (strncmpiW(ui.path, prefixRectified, prefixLen) != 0)
1602 HeapFree(GetProcessHeap(), 0, page_buf);
1603 return 1;
1607 /* check if we should include this path */
1608 if (lastPathLen != -1)
1610 if (strncmpiW(ui.path, lastPath, lastPathLen) == 0)
1611 continue;
1613 strcpyW(lastPath, ui.path);
1614 lastPathLen = strlenW(lastPath);
1616 /* get the length of the path */
1617 ui_path_len = strlenW(ui.path)-1;
1619 /* check for DIRS */
1620 if (ui.path[ui_path_len] == '/' && !(what & CHM_ENUMERATE_DIRS))
1621 continue;
1623 /* check for FILES */
1624 if (ui.path[ui_path_len] != '/' && !(what & CHM_ENUMERATE_FILES))
1625 continue;
1627 /* check for NORMAL vs. META */
1628 if (ui.path[0] == '/')
1631 /* check for NORMAL vs. SPECIAL */
1632 if (ui.path[1] == '#' || ui.path[1] == '$')
1633 flag = CHM_ENUMERATE_SPECIAL;
1634 else
1635 flag = CHM_ENUMERATE_NORMAL;
1637 else
1638 flag = CHM_ENUMERATE_META;
1639 if (! (what & flag))
1640 continue;
1642 /* call the enumerator */
1644 int status = (*e)(h, &ui, context);
1645 switch (status)
1647 case CHM_ENUMERATOR_FAILURE:
1648 HeapFree(GetProcessHeap(), 0, page_buf);
1649 return 0;
1650 case CHM_ENUMERATOR_CONTINUE:
1651 break;
1652 case CHM_ENUMERATOR_SUCCESS:
1653 HeapFree(GetProcessHeap(), 0, page_buf);
1654 return 1;
1655 default:
1656 break;
1661 /* advance to next page */
1662 curPage = header.block_next;
1665 HeapFree(GetProcessHeap(), 0, page_buf);
1666 return 1;