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