ReactOS  0.4.14-dev-583-g2a1ba2c
chm_lib.c
Go to the documentation of this file.
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;
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)
239 {
240  char signature[4]; /* 0 (ITSF) */
241  Int32 version; /* 4 */
242  Int32 header_len; /* 8 */
245  UInt32 lang_id; /* 14 */
246  UChar dir_uuid[16]; /* 18 */
247  UChar stream_uuid[16]; /* 28 */
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)
315 {
316  char signature[4]; /* 0 (ITSP) */
317  Int32 version; /* 4 */
318  Int32 header_len; /* 8 */
320  UInt32 block_len; /* 10 */
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)
373 {
374  char signature[4]; /* 0 (PMGL) */
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)
407 {
408  char signature[4]; /* 0 (PMGI) */
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)
434 {
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)
472 {
473  UInt32 size; /* 0 */
474  char signature[4]; /* 4 (LZXC) */
475  UInt32 version; /* 8 */
477  UInt32 windowSize; /* 10 */
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 {
528 
532 
539 
544 
545  /* LZX control data */
550 
551  /* decompressor state */
554 
555  /* cache for decompressed blocks */
559 };
560 
561 /*
562  * utility functions local to this module
563  */
564 
565 /* utility function to handle differences between {pread,read}(64)? */
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  {
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 */
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,
704  NULL,
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 */
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,
770  &newHandle->rt_unit) ||
771  newHandle->rt_unit.space == CHM_COMPRESSED ||
774  &newHandle->cn_unit) ||
775  newHandle->cn_unit.space == CHM_COMPRESSED ||
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),
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");
851  newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex");
853  newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex");
854 
855  /* initialize cache */
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 */
935 {
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,
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;
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 
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;
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 */
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 */
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. */
1181  UInt64 block,
1182  UChar **ubuffer)
1183 {
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] =
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
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
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  */
1286  return h->reset_table.block_len;
1287 }
1288 
1289 /* grab a region from a compressed block */
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 */
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 
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] == '$')
1533  else
1535  }
1536  else
1538  if (! (what & flag))
1539  continue;
1540 
1541  /* call the enumerator */
1542  {
1543  int status = (*e)(h, &ui, context);
1544  switch (status)
1545  {
1547  HeapFree(GetProcessHeap(), 0, page_buf);
1548  return FALSE;
1550  break;
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 }
static unsigned int block
Definition: xmlmemory.c:118
BOOL WINAPI SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
Definition: fileinfo.c:327
struct LZXstate * lzx_state
Definition: chm_lib.c:552
#define CHM_MAX_PATHLEN
Definition: chm_lib.h:66
UInt32 free_space
Definition: chm_lib.c:375
LONGLONG LONGINT64
Definition: chm_lib.h:54
#define TRUE
Definition: types.h:120
char signature[4]
Definition: chm_lib.c:474
UInt64 unknown_len
Definition: chm_lib.c:249
struct LZXstate * LZXinit(int wndsize)
Definition: lzx.c:172
int memcmp(void *Buffer1, void *Buffer2, ACPI_SIZE Count)
Definition: utclib.c:112
CRITICAL_SECTION lzx_mutex
Definition: chm_lib.c:530
#define DWORD_PTR
Definition: treelist.c:76
UChar stream_uuid[16]
Definition: chm_lib.c:247
UInt64 dir_offset
Definition: chm_lib.c:533
#define CHM_ENUMERATE_DIRS
Definition: chm_lib.h:103
UInt32 last_modified
Definition: chm_lib.c:244
Definition: http.c:7098
CRITICAL_SECTION cache_mutex
Definition: chm_lib.c:531
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
static BOOL _unmarshal_itsp_header(unsigned char **pData, unsigned int *pDataLen, struct chmItspHeader *dest)
Definition: chm_lib.c:333
static BOOL _unmarshal_uint32(unsigned char **pData, unsigned int *pLenRemain, UInt32 *dest)
Definition: chm_lib.c:147
DWORD UInt32
Definition: chm_lib.c:104
Int32 unknown_000c
Definition: chm_lib.c:319
static BOOL _unmarshal_int32(unsigned char **pData, unsigned int *pLenRemain, Int32 *dest)
Definition: chm_lib.c:135
GLuint GLuint GLsizei count
Definition: gl.h:1545
#define CHM_ENUMERATE_META
Definition: chm_lib.h:100
static BOOL _unmarshal_pmgi_header(unsigned char **pData, unsigned int *pDataLen, struct chmPmgiHeader *dest)
Definition: chm_lib.c:412
struct chmUnitInfo cn_unit
Definition: chm_lib.c:542
#define FILE_CURRENT
Definition: winbase.h:113
#define _CHM_LZXC_V2_LEN
Definition: chm_lib.c:470
UInt32 unknown
Definition: chm_lib.c:437
UINT ui
Definition: oleauto.h:49
Int64 * cache_block_indices
Definition: chm_lib.c:557
Int32 unknown_002c
Definition: chm_lib.c:327
_Check_return_ _CRTIMP int __cdecl _wcsnicmp(_In_reads_or_z_(_MaxCount) const wchar_t *_Str1, _In_reads_or_z_(_MaxCount) const wchar_t *_Str2, _In_ size_t _MaxCount)
ULONGLONG UInt64
Definition: chm_lib.c:106
void LZXteardown(struct LZXstate *pState)
Definition: lzx.c:209
static BOOL _unmarshal_uint64(unsigned char **pData, unsigned int *pLenRemain, UInt64 *dest)
Definition: chm_lib.c:179
UInt32 lang_id
Definition: chm_lib.c:245
UChar ** cache_blocks
Definition: chm_lib.c:556
Int32 header_len
Definition: chm_lib.c:318
GLuint buffer
Definition: glext.h:5915
char signature[4]
Definition: chm_lib.c:408
HANDLE fd
Definition: chm_lib.c:527
#define CHM_RELEASE_LOCK(a)
Definition: chm_lib.c:73
BYTE UChar
Definition: chm_lib.c:100
UChar unknown_0044[16]
Definition: chm_lib.c:330
GLuint GLuint end
Definition: gl.h:1545
static BOOL _unmarshal_lzxc_control_data(unsigned char **pData, unsigned int *pDataLen, struct chmLzxcControlData *dest)
Definition: chm_lib.c:482
UInt32 reset_blkcount
Definition: chm_lib.c:549
USHORT UInt16
Definition: chm_lib.c:102
Int32 unknown_0024
Definition: chm_lib.c:325
Int32 version
Definition: chm_lib.c:241
Int32 cache_num_blocks
Definition: chm_lib.c:558
const char * filename
Definition: ioapi.h:135
#define _CHM_LZXC_RESETTABLE_V1_LEN
Definition: chm_lib.c:432
#define _CHM_PMGI_LEN
Definition: chm_lib.c:405
char signature[4]
Definition: chm_lib.c:240
UInt64 span
Definition: chm_lib.c:540
#define CHM_CLOSE_FILE(fd)
Definition: chm_lib.c:78
#define lstrlenW
Definition: compat.h:415
static Int32 _chm_find_in_PMGI(UChar *page_buf, UInt32 block_len, const WCHAR *objPath)
Definition: chm_lib.c:1006
Int32 index_root
Definition: chm_lib.c:323
Int32 index_depth
Definition: chm_lib.c:322
#define FILE_SHARE_READ
Definition: compat.h:125
#define lstrcpynW
Definition: compat.h:405
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
UInt64 dir_offset
Definition: chm_lib.c:250
int lzx_last_block
Definition: chm_lib.c:553
UInt64 data_offset
Definition: chm_lib.c:535
#define CHM_NULL_FD
Definition: chm_lib.c:77
void chm_close(struct chmFile *h)
Definition: chm_lib.c:863
#define DECR_OK
Definition: mszip.h:79
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define DUPLICATE_SAME_ACCESS
#define CP_UTF8
Definition: nls.h:20
unsigned int BOOL
Definition: ntddk_ex.h:94
long LONG
Definition: pedump.c:60
_Check_return_opt_ _CRTIMP int __cdecl fprintf(_Inout_ FILE *_File, _In_z_ _Printf_format_string_ const char *_Format,...)
#define e
Definition: ke_i.h:82
short SHORT
Definition: pedump.c:59
static BOOL _unmarshal_int64(unsigned char **pData, unsigned int *pLenRemain, Int64 *dest)
Definition: chm_lib.c:159
static BOOL _unmarshal_lzxc_reset_table(unsigned char **pData, unsigned int *pDataLen, struct chmLzxcResetTable *dest)
Definition: chm_lib.c:444
char signature[4]
Definition: chm_lib.c:374
#define _CHM_ITSF_V3_LEN
Definition: chm_lib.c:237
static Int64 _chm_decompress_block(struct chmFile *h, UInt64 block, UChar **ubuffer)
Definition: chm_lib.c:1180
#define CHM_PARAM_MAX_BLOCKS_CACHED
Definition: chm_lib.c:86
#define _CHM_ITSP_V1_LEN
Definition: chm_lib.c:313
VOID WINAPI InitializeCriticalSection(OUT LPCRITICAL_SECTION lpCriticalSection)
Definition: synch.c:751
UInt64 dir_len
Definition: chm_lib.c:534
smooth NULL
Definition: ftsmooth.c:416
UInt32 lang_id
Definition: chm_lib.c:328
struct chmUnitInfo rt_unit
Definition: chm_lib.c:541
int chm_resolve_object(struct chmFile *h, const WCHAR *objPath, struct chmUnitInfo *ui)
Definition: chm_lib.c:1048
#define CHM_ENUMERATOR_FAILURE
Definition: chm_lib.h:105
Definition: module.h:566
Int32 index_head
Definition: chm_lib.c:537
UInt32 unknown_0008
Definition: chm_lib.c:376
#define OPEN_EXISTING
Definition: compat.h:434
LONGINT64 chm_retrieve_object(struct chmFile *h, struct chmUnitInfo *ui, unsigned char *buf, LONGUINT64 addr, LONGINT64 len)
Definition: chm_lib.c:1342
UInt32 windowSize
Definition: chm_lib.c:477
int64_t LONGLONG
Definition: typedefs.h:66
static Int64 _chm_decompress_region(struct chmFile *h, UChar *buf, UInt64 start, Int64 len)
Definition: chm_lib.c:1290
UInt32 table_offset
Definition: chm_lib.c:438
UInt64 dir_len
Definition: chm_lib.c:251
Int32 index_root
Definition: chm_lib.c:536
#define GetProcessHeap()
Definition: compat.h:403
UInt64 data_offset
Definition: chm_lib.c:252
PVOID WINAPI HeapAlloc(HANDLE, DWORD, SIZE_T)
UInt32 windowsPerReset
Definition: chm_lib.c:478
static void _chm_skip_PMGL_entry_data(UChar **pEntry)
Definition: chm_lib.c:912
if(!(yy_init))
Definition: macro.lex.yy.c:714
CRITICAL_SECTION mutex
Definition: chm_lib.c:529
UInt64 uncompressed_len
Definition: chm_lib.c:439
char signature[4]
Definition: chm_lib.c:316
UInt32 block_len
Definition: chm_lib.c:320
__wchar_t WCHAR
Definition: xmlstorage.h:180
void WINAPI DeleteCriticalSection(PCRITICAL_SECTION)
int space
Definition: chm_lib.h:71
GLenum GLuint GLenum GLsizei length
Definition: glext.h:5579
uint64_t ULONGLONG
Definition: typedefs.h:65
HANDLE WINAPI GetCurrentProcess(VOID)
Definition: proc.c:1138
ULONGLONG LONGUINT64
Definition: chm_lib.h:53
UInt32 free_space
Definition: chm_lib.c:409
#define CHM_RESOLVE_SUCCESS
Definition: chm_lib.h:82
#define CHM_RESOLVE_FAILURE
Definition: chm_lib.h:83
unsigned long DWORD
Definition: ntddk_ex.h:95
static const char _chm_pmgi_marker[4]
Definition: chm_lib.c:404
static char * cbuffer
Definition: parser.yy.c:760
int(* CHM_ENUMERATOR)(struct chmFile *h, struct chmUnitInfo *ui, void *context)
Definition: chm_lib.h:96
struct chmFile * chm_openW(const WCHAR *filename)
Definition: chm_lib.c:678
int LZXreset(struct LZXstate *pState)
Definition: lzx.c:218
UChar dir_uuid[16]
Definition: chm_lib.c:246
PCRITICAL_SECTION_DEBUG DebugInfo
Definition: winbase.h:866
#define CHM_ENUMERATE_FILES
Definition: chm_lib.h:102
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:126
Int32 unknown_000c
Definition: chm_lib.c:243
GLenum const GLvoid * addr
Definition: glext.h:9621
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean flag
Definition: glfuncs.h:52
static BOOL _chm_get_cmpblock_bounds(struct chmFile *h, UInt64 block, UInt64 *start, Int64 *len)
Definition: chm_lib.c:1118
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
GLenum GLsizei len
Definition: glext.h:6722
unsigned char BYTE
Definition: mem.h:68
#define GENERIC_READ
Definition: compat.h:124
static Int64 _chm_fetch_bytes(struct chmFile *h, UChar *buf, UInt64 os, Int64 len)
Definition: chm_lib.c:566
static BOOL _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path)
Definition: chm_lib.c:934
UInt32 window_size
Definition: chm_lib.c:547
Int32 blockidx_intvl
Definition: chm_lib.c:321
#define wcsicmp
Definition: string.h:1152
UInt32 block_count
Definition: chm_lib.c:436
#define CHM_UNCOMPRESSED
Definition: chm_lib.h:59
#define _CHM_PMGL_LEN
Definition: chm_lib.c:371
UInt64 block_len
Definition: chm_lib.c:441
static BOOL _unmarshal_pmgl_header(unsigned char **pData, unsigned int *pDataLen, struct chmPmglHeader *dest)
Definition: chm_lib.c:381
LONGLONG Int64
Definition: chm_lib.c:105
#define _CHM_ITSF_V2_LEN
Definition: chm_lib.c:236
static const WCHAR _CHMU_CONTENT[]
Definition: chm_lib.c:224
static BOOL _unmarshal_uchar_array(unsigned char **pData, unsigned int *pLenRemain, unsigned char *dest, int count)
Definition: chm_lib.c:122
#define FILE_BEGIN
Definition: winbase.h:112
static void chm_set_param(struct chmFile *h, int paramType, int paramVal)
Definition: chm_lib.c:613
#define CHM_MAX_BLOCKS_CACHED
Definition: chm_lib.c:84
#define lstrcpyW
Definition: compat.h:414
LONGUINT64 length
Definition: chm_lib.h:70
UChar system_uuid[16]
Definition: chm_lib.c:329
UInt32 version
Definition: chm_lib.c:435
UInt32 num_blocks
Definition: chm_lib.c:326
unsigned short USHORT
Definition: pedump.c:61
static UInt64 _chm_parse_cword(UChar **pEntry)
Definition: chm_lib.c:920
static calc_node_t temp
Definition: rpn_ieee.c:38
GLuint start
Definition: gl.h:1545
unsigned char dummy
Definition: maze.c:118
#define CHM_ACQUIRE_LOCK(a)
Definition: chm_lib.c:70
#define long
Definition: qsort.c:33
SHORT Int16
Definition: chm_lib.c:101
static UChar * _chm_find_in_PMGL(UChar *page_buf, UInt32 block_len, const WCHAR *objPath)
Definition: chm_lib.c:964
Int32 header_len
Definition: chm_lib.c:242
BOOL chm_enumerate_dir(struct chmFile *h, const WCHAR *prefix, int what, CHM_ENUMERATOR e, void *context)
Definition: chm_lib.c:1400
Int32 block_prev
Definition: chm_lib.c:377
static const WCHAR _CHMU_RESET_TABLE[]
Definition: chm_lib.c:207
#define MultiByteToWideChar
Definition: compat.h:100
UInt32 reset_interval
Definition: chm_lib.c:548
static BOOL _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui)
Definition: chm_lib.c:943
#define CreateFileW
Definition: compat.h:408
LONG Int32
Definition: chm_lib.c:103
Int32 version
Definition: chm_lib.c:317
UInt64 compressed_len
Definition: chm_lib.c:440
static BOOL _unmarshal_char_array(unsigned char **pData, unsigned int *pLenRemain, char *dest, int count)
Definition: chm_lib.c:109
static const WCHAR _CHMU_LZXC_CONTROLDATA[]
Definition: chm_lib.c:218
#define CHM_ENUMERATOR_CONTINUE
Definition: chm_lib.h:106
UInt32 block_len
Definition: chm_lib.c:538
int compression_enabled
Definition: chm_lib.c:546
static BOOL _unmarshal_uuid(unsigned char **pData, unsigned int *pDataLen, unsigned char *dest)
Definition: chm_lib.c:199
static BOOL _unmarshal_itsf_header(unsigned char **pData, unsigned int *pDataLen, struct chmItsfHeader *dest)
Definition: chm_lib.c:255
FILE * stderr
struct chmLzxcResetTable reset_table
Definition: chm_lib.c:543
Int32 index_head
Definition: chm_lib.c:324
int LZXdecompress(struct LZXstate *pState, unsigned char *inpos, unsigned char *outpos, int inlen, int outlen)
Definition: lzx.c:461
static char * dest
Definition: rtl.c:135
#define CHM_COMPRESSED
Definition: chm_lib.h:60
UInt32 unknown_18
Definition: chm_lib.c:479
static const char _chm_pmgl_marker[4]
Definition: chm_lib.c:370
#define CHM_ENUMERATE_SPECIAL
Definition: chm_lib.h:101
TW_UINT32 TW_UINT16 TW_UINT16 TW_MEMREF pData
Definition: twain.h:1827
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
UInt32 resetInterval
Definition: chm_lib.c:476
UInt64 unknown_offset
Definition: chm_lib.c:248
Int32 block_next
Definition: chm_lib.c:378
#define CHM_ENUMERATE_NORMAL
Definition: chm_lib.h:99
static void _chm_skip_cword(UChar **pEntry)
Definition: chm_lib.c:905
#define HeapFree(x, y, z)
Definition: compat.h:402
#define _CHM_LZXC_MIN_LEN
Definition: chm_lib.c:469
BOOL WINAPI DuplicateHandle(IN HANDLE hSourceProcessHandle, IN HANDLE hSourceHandle, IN HANDLE hTargetProcessHandle, OUT LPHANDLE lpTargetHandle, IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN DWORD dwOptions)
Definition: handle.c:149
#define CHM_ENUMERATOR_SUCCESS
Definition: chm_lib.h:107
LONGLONG QuadPart
Definition: typedefs.h:112
DWORD_PTR Spare[8/sizeof(DWORD_PTR)]
Definition: winbase.h:859
struct chmFile * chm_dup(struct chmFile *oldHandle)
Definition: chm_lib.c:831
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31
Definition: ps.c:97