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