ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

chm_lib.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *             chm_lib.c - CHM archive manipulation routines               *
00003  *                           -------------------                           *
00004  *                                                                         *
00005  *  author:     Jed Wing <jedwin@ugcs.caltech.edu>                         *
00006  *  version:    0.3                                                        *
00007  *  notes:      These routines are meant for the manipulation of microsoft *
00008  *              .chm (compiled html help) files, but may likely be used    *
00009  *              for the manipulation of any ITSS archive, if ever ITSS     *
00010  *              archives are used for any other purpose.                   *
00011  *                                                                         *
00012  *              Note also that the section names are statically handled.   *
00013  *              To be entirely correct, the section names should be read   *
00014  *              from the section names meta-file, and then the various     *
00015  *              content sections and the "transforms" to apply to the data *
00016  *              they contain should be inferred from the section name and  *
00017  *              the meta-files referenced using that name; however, all of *
00018  *              the files I've been able to get my hands on appear to have *
00019  *              only two sections: Uncompressed and MSCompressed.          *
00020  *              Additionally, the ITSS.DLL file included with Windows does *
00021  *              not appear to handle any different transforms than the     *
00022  *              simple LZX-transform.  Furthermore, the list of transforms *
00023  *              to apply is broken, in that only half the required space   *
00024  *              is allocated for the list.  (It appears as though the      *
00025  *              space is allocated for ASCII strings, but the strings are  *
00026  *              written as unicode.  As a result, only the first half of   *
00027  *              the string appears.)  So this is probably not too big of   *
00028  *              a deal, at least until CHM v4 (MS .lit files), which also  *
00029  *              incorporate encryption, of some description.               *
00030  *                                                                         *
00031  ***************************************************************************/
00032 
00033 /***************************************************************************
00034  *
00035  * This library is free software; you can redistribute it and/or
00036  * modify it under the terms of the GNU Lesser General Public
00037  * License as published by the Free Software Foundation; either
00038  * version 2.1 of the License, or (at your option) any later version.
00039  *
00040  * This library is distributed in the hope that it will be useful,
00041  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00042  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00043  * Lesser General Public License for more details.
00044  *
00045  * You should have received a copy of the GNU Lesser General Public
00046  * License along with this library; if not, write to the Free Software
00047  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00048  *
00049  ***************************************************************************/
00050 
00051 /***************************************************************************
00052  *                                                                         *
00053  * Adapted for Wine by Mike McCormack                                      *
00054  *                                                                         *
00055  ***************************************************************************/
00056 
00057 #include "config.h"
00058 #include "wine/port.h"
00059 
00060 #include <stdarg.h>
00061 #include <stdio.h>
00062 #include <stdlib.h>
00063 #include <string.h>
00064 
00065 #include "windef.h"
00066 #include "winbase.h"
00067 #include "wine/unicode.h"
00068 
00069 #include "chm_lib.h"
00070 #include "lzx.h"
00071 
00072 #define CHM_ACQUIRE_LOCK(a) do {                        \
00073         EnterCriticalSection(&(a));                     \
00074     } while(0)
00075 #define CHM_RELEASE_LOCK(a) do {                        \
00076         LeaveCriticalSection(&(a));                     \
00077     } while(0)
00078 
00079 #define CHM_NULL_FD (INVALID_HANDLE_VALUE)
00080 #define CHM_CLOSE_FILE(fd) CloseHandle((fd))
00081 
00082 /*
00083  * defines related to tuning
00084  */
00085 #ifndef CHM_MAX_BLOCKS_CACHED
00086 #define CHM_MAX_BLOCKS_CACHED 5
00087 #endif
00088 #define CHM_PARAM_MAX_BLOCKS_CACHED 0
00089 
00090 /*
00091  * architecture specific defines
00092  *
00093  * Note: as soon as C99 is more widespread, the below defines should
00094  * probably just use the C99 sized-int types.
00095  *
00096  * The following settings will probably work for many platforms.  The sizes
00097  * don't have to be exactly correct, but the types must accommodate at least as
00098  * many bits as they specify.
00099  */
00100 
00101 /* i386, 32-bit, Windows */
00102 typedef BYTE   UChar;
00103 typedef SHORT  Int16;
00104 typedef USHORT UInt16;
00105 typedef LONG   Int32;
00106 typedef DWORD      UInt32;
00107 typedef LONGLONG   Int64;
00108 typedef ULONGLONG  UInt64;
00109 
00110 /* utilities for unmarshalling data */
00111 static int _unmarshal_char_array(unsigned char **pData,
00112                                  unsigned int *pLenRemain,
00113                                  char *dest,
00114                                  int count)
00115 {
00116     if (count <= 0  ||  (unsigned int)count > *pLenRemain)
00117         return 0;
00118     memcpy(dest, (*pData), count);
00119     *pData += count;
00120     *pLenRemain -= count;
00121     return 1;
00122 }
00123 
00124 static int _unmarshal_uchar_array(unsigned char **pData,
00125                                   unsigned int *pLenRemain,
00126                                   unsigned char *dest,
00127                                   int count)
00128 {
00129         if (count <= 0  ||  (unsigned int)count > *pLenRemain)
00130         return 0;
00131     memcpy(dest, (*pData), count);
00132     *pData += count;
00133     *pLenRemain -= count;
00134     return 1;
00135 }
00136 
00137 static int _unmarshal_int32(unsigned char **pData,
00138                             unsigned int *pLenRemain,
00139                             Int32 *dest)
00140 {
00141     if (4 > *pLenRemain)
00142         return 0;
00143     *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24;
00144     *pData += 4;
00145     *pLenRemain -= 4;
00146     return 1;
00147 }
00148 
00149 static int _unmarshal_uint32(unsigned char **pData,
00150                              unsigned int *pLenRemain,
00151                              UInt32 *dest)
00152 {
00153     if (4 > *pLenRemain)
00154         return 0;
00155     *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24;
00156     *pData += 4;
00157     *pLenRemain -= 4;
00158     return 1;
00159 }
00160 
00161 static int _unmarshal_int64(unsigned char **pData,
00162                             unsigned int *pLenRemain,
00163                             Int64 *dest)
00164 {
00165     Int64 temp;
00166     int i;
00167     if (8 > *pLenRemain)
00168         return 0;
00169     temp=0;
00170     for(i=8; i>0; i--)
00171     {
00172         temp <<= 8;
00173         temp |= (*pData)[i-1];
00174     }
00175     *dest = temp;
00176     *pData += 8;
00177     *pLenRemain -= 8;
00178     return 1;
00179 }
00180 
00181 static int _unmarshal_uint64(unsigned char **pData,
00182                              unsigned int *pLenRemain,
00183                              UInt64 *dest)
00184 {
00185     UInt64 temp;
00186     int i;
00187     if (8 > *pLenRemain)
00188         return 0;
00189     temp=0;
00190     for(i=8; i>0; i--)
00191     {
00192         temp <<= 8;
00193         temp |= (*pData)[i-1];
00194     }
00195     *dest = temp;
00196     *pData += 8;
00197     *pLenRemain -= 8;
00198     return 1;
00199 }
00200 
00201 static int _unmarshal_uuid(unsigned char **pData,
00202                            unsigned int *pDataLen,
00203                            unsigned char *dest)
00204 {
00205     return _unmarshal_uchar_array(pData, pDataLen, dest, 16);
00206 }
00207 
00208 /* names of sections essential to decompression */
00209 static const WCHAR _CHMU_RESET_TABLE[] = {
00210 ':',':','D','a','t','a','S','p','a','c','e','/',
00211         'S','t','o','r','a','g','e','/',
00212         'M','S','C','o','m','p','r','e','s','s','e','d','/',
00213         'T','r','a','n','s','f','o','r','m','/',
00214         '{','7','F','C','2','8','9','4','0','-','9','D','3','1',
00215           '-','1','1','D','0','-','9','B','2','7','-',
00216           '0','0','A','0','C','9','1','E','9','C','7','C','}','/',
00217         'I','n','s','t','a','n','c','e','D','a','t','a','/',
00218         'R','e','s','e','t','T','a','b','l','e',0
00219 };
00220 static const WCHAR _CHMU_LZXC_CONTROLDATA[] = {
00221 ':',':','D','a','t','a','S','p','a','c','e','/',
00222         'S','t','o','r','a','g','e','/',
00223         'M','S','C','o','m','p','r','e','s','s','e','d','/',
00224         'C','o','n','t','r','o','l','D','a','t','a',0
00225 };
00226 static const WCHAR _CHMU_CONTENT[] = {
00227 ':',':','D','a','t','a','S','p','a','c','e','/',
00228         'S','t','o','r','a','g','e','/',
00229         'M','S','C','o','m','p','r','e','s','s','e','d','/',
00230         'C','o','n','t','e','n','t',0
00231 };
00232 
00233 /*
00234  * structures local to this module
00235  */
00236 
00237 /* structure of ITSF headers */
00238 #define _CHM_ITSF_V2_LEN (0x58)
00239 #define _CHM_ITSF_V3_LEN (0x60)
00240 struct chmItsfHeader
00241 {
00242     char        signature[4];           /*  0 (ITSF) */
00243     Int32       version;                /*  4 */
00244     Int32       header_len;             /*  8 */
00245     Int32       unknown_000c;           /*  c */
00246     UInt32      last_modified;          /* 10 */
00247     UInt32      lang_id;                /* 14 */
00248     UChar       dir_uuid[16];           /* 18 */
00249     UChar       stream_uuid[16];        /* 28 */
00250     UInt64      unknown_offset;         /* 38 */
00251     UInt64      unknown_len;            /* 40 */
00252     UInt64      dir_offset;             /* 48 */
00253     UInt64      dir_len;                /* 50 */
00254     UInt64      data_offset;            /* 58 (Not present before V3) */
00255 }; /* __attribute__ ((aligned (1))); */
00256 
00257 static int _unmarshal_itsf_header(unsigned char **pData,
00258                                   unsigned int *pDataLen,
00259                                   struct chmItsfHeader *dest)
00260 {
00261     /* we only know how to deal with the 0x58 and 0x60 byte structures */
00262     if (*pDataLen != _CHM_ITSF_V2_LEN  &&  *pDataLen != _CHM_ITSF_V3_LEN)
00263         return 0;
00264 
00265     /* unmarshal common fields */
00266     _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
00267     _unmarshal_int32     (pData, pDataLen, &dest->version);
00268     _unmarshal_int32     (pData, pDataLen, &dest->header_len);
00269     _unmarshal_int32     (pData, pDataLen, &dest->unknown_000c);
00270     _unmarshal_uint32    (pData, pDataLen, &dest->last_modified);
00271     _unmarshal_uint32    (pData, pDataLen, &dest->lang_id);
00272     _unmarshal_uuid      (pData, pDataLen,  dest->dir_uuid);
00273     _unmarshal_uuid      (pData, pDataLen,  dest->stream_uuid);
00274     _unmarshal_uint64    (pData, pDataLen, &dest->unknown_offset);
00275     _unmarshal_uint64    (pData, pDataLen, &dest->unknown_len);
00276     _unmarshal_uint64    (pData, pDataLen, &dest->dir_offset);
00277     _unmarshal_uint64    (pData, pDataLen, &dest->dir_len);
00278 
00279     /* error check the data */
00280     /* XXX: should also check UUIDs, probably, though with a version 3 file,
00281      * current MS tools do not seem to use them.
00282      */
00283     if (memcmp(dest->signature, "ITSF", 4) != 0)
00284         return 0;
00285     if (dest->version == 2)
00286     {
00287         if (dest->header_len < _CHM_ITSF_V2_LEN)
00288             return 0;
00289     }
00290     else if (dest->version == 3)
00291     {
00292         if (dest->header_len < _CHM_ITSF_V3_LEN)
00293             return 0;
00294     }
00295     else
00296         return 0;
00297 
00298     /* now, if we have a V3 structure, unmarshal the rest.
00299      * otherwise, compute it
00300      */
00301     if (dest->version == 3)
00302     {
00303         if (*pDataLen != 0)
00304             _unmarshal_uint64(pData, pDataLen, &dest->data_offset);
00305         else
00306             return 0;
00307     }
00308     else
00309         dest->data_offset = dest->dir_offset + dest->dir_len;
00310 
00311     return 1;
00312 }
00313 
00314 /* structure of ITSP headers */
00315 #define _CHM_ITSP_V1_LEN (0x54)
00316 struct chmItspHeader
00317 {
00318     char        signature[4];           /*  0 (ITSP) */
00319     Int32       version;                /*  4 */
00320     Int32       header_len;             /*  8 */
00321     Int32       unknown_000c;           /*  c */
00322     UInt32      block_len;              /* 10 */
00323     Int32       blockidx_intvl;         /* 14 */
00324     Int32       index_depth;            /* 18 */
00325     Int32       index_root;             /* 1c */
00326     Int32       index_head;             /* 20 */
00327     Int32       unknown_0024;           /* 24 */
00328     UInt32      num_blocks;             /* 28 */
00329     Int32       unknown_002c;           /* 2c */
00330     UInt32      lang_id;                /* 30 */
00331     UChar       system_uuid[16];        /* 34 */
00332     UChar       unknown_0044[16];       /* 44 */
00333 }; /* __attribute__ ((aligned (1))); */
00334 
00335 static int _unmarshal_itsp_header(unsigned char **pData,
00336                                   unsigned int *pDataLen,
00337                                   struct chmItspHeader *dest)
00338 {
00339     /* we only know how to deal with a 0x54 byte structures */
00340     if (*pDataLen != _CHM_ITSP_V1_LEN)
00341         return 0;
00342 
00343     /* unmarshal fields */
00344     _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
00345     _unmarshal_int32     (pData, pDataLen, &dest->version);
00346     _unmarshal_int32     (pData, pDataLen, &dest->header_len);
00347     _unmarshal_int32     (pData, pDataLen, &dest->unknown_000c);
00348     _unmarshal_uint32    (pData, pDataLen, &dest->block_len);
00349     _unmarshal_int32     (pData, pDataLen, &dest->blockidx_intvl);
00350     _unmarshal_int32     (pData, pDataLen, &dest->index_depth);
00351     _unmarshal_int32     (pData, pDataLen, &dest->index_root);
00352     _unmarshal_int32     (pData, pDataLen, &dest->index_head);
00353     _unmarshal_int32     (pData, pDataLen, &dest->unknown_0024);
00354     _unmarshal_uint32    (pData, pDataLen, &dest->num_blocks);
00355     _unmarshal_int32     (pData, pDataLen, &dest->unknown_002c);
00356     _unmarshal_uint32    (pData, pDataLen, &dest->lang_id);
00357     _unmarshal_uuid      (pData, pDataLen,  dest->system_uuid);
00358     _unmarshal_uchar_array(pData, pDataLen, dest->unknown_0044, 16);
00359 
00360     /* error check the data */
00361     if (memcmp(dest->signature, "ITSP", 4) != 0)
00362         return 0;
00363     if (dest->version != 1)
00364         return 0;
00365     if (dest->header_len != _CHM_ITSP_V1_LEN)
00366         return 0;
00367 
00368     return 1;
00369 }
00370 
00371 /* structure of PMGL headers */
00372 static const char _chm_pmgl_marker[4] = "PMGL";
00373 #define _CHM_PMGL_LEN (0x14)
00374 struct chmPmglHeader
00375 {
00376     char        signature[4];           /*  0 (PMGL) */
00377     UInt32      free_space;             /*  4 */
00378     UInt32      unknown_0008;           /*  8 */
00379     Int32       block_prev;             /*  c */
00380     Int32       block_next;             /* 10 */
00381 }; /* __attribute__ ((aligned (1))); */
00382 
00383 static int _unmarshal_pmgl_header(unsigned char **pData,
00384                                   unsigned int *pDataLen,
00385                                   struct chmPmglHeader *dest)
00386 {
00387     /* we only know how to deal with a 0x14 byte structures */
00388     if (*pDataLen != _CHM_PMGL_LEN)
00389         return 0;
00390 
00391     /* unmarshal fields */
00392     _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
00393     _unmarshal_uint32    (pData, pDataLen, &dest->free_space);
00394     _unmarshal_uint32    (pData, pDataLen, &dest->unknown_0008);
00395     _unmarshal_int32     (pData, pDataLen, &dest->block_prev);
00396     _unmarshal_int32     (pData, pDataLen, &dest->block_next);
00397 
00398     /* check structure */
00399     if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0)
00400         return 0;
00401 
00402     return 1;
00403 }
00404 
00405 /* structure of PMGI headers */
00406 static const char _chm_pmgi_marker[4] = "PMGI";
00407 #define _CHM_PMGI_LEN (0x08)
00408 struct chmPmgiHeader
00409 {
00410     char        signature[4];           /*  0 (PMGI) */
00411     UInt32      free_space;             /*  4 */
00412 }; /* __attribute__ ((aligned (1))); */
00413 
00414 static int _unmarshal_pmgi_header(unsigned char **pData,
00415                                   unsigned int *pDataLen,
00416                                   struct chmPmgiHeader *dest)
00417 {
00418     /* we only know how to deal with a 0x8 byte structures */
00419     if (*pDataLen != _CHM_PMGI_LEN)
00420         return 0;
00421 
00422     /* unmarshal fields */
00423     _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
00424     _unmarshal_uint32    (pData, pDataLen, &dest->free_space);
00425 
00426     /* check structure */
00427     if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0)
00428         return 0;
00429 
00430     return 1;
00431 }
00432 
00433 /* structure of LZXC reset table */
00434 #define _CHM_LZXC_RESETTABLE_V1_LEN (0x28)
00435 struct chmLzxcResetTable
00436 {
00437     UInt32      version;
00438     UInt32      block_count;
00439     UInt32      unknown;
00440     UInt32      table_offset;
00441     UInt64      uncompressed_len;
00442     UInt64      compressed_len;
00443     UInt64      block_len;     
00444 }; /* __attribute__ ((aligned (1))); */
00445 
00446 static int _unmarshal_lzxc_reset_table(unsigned char **pData,
00447                                        unsigned int *pDataLen,
00448                                        struct chmLzxcResetTable *dest)
00449 {
00450     /* we only know how to deal with a 0x28 byte structures */
00451     if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN)
00452         return 0;
00453 
00454     /* unmarshal fields */
00455     _unmarshal_uint32    (pData, pDataLen, &dest->version);
00456     _unmarshal_uint32    (pData, pDataLen, &dest->block_count);
00457     _unmarshal_uint32    (pData, pDataLen, &dest->unknown);
00458     _unmarshal_uint32    (pData, pDataLen, &dest->table_offset);
00459     _unmarshal_uint64    (pData, pDataLen, &dest->uncompressed_len);
00460     _unmarshal_uint64    (pData, pDataLen, &dest->compressed_len);
00461     _unmarshal_uint64    (pData, pDataLen, &dest->block_len);
00462 
00463     /* check structure */
00464     if (dest->version != 2)
00465         return 0;
00466 
00467     return 1;
00468 }
00469 
00470 /* structure of LZXC control data block */
00471 #define _CHM_LZXC_MIN_LEN (0x18)
00472 #define _CHM_LZXC_V2_LEN (0x1c)
00473 struct chmLzxcControlData
00474 {
00475     UInt32      size;                   /*  0        */
00476     char        signature[4];           /*  4 (LZXC) */
00477     UInt32      version;                /*  8        */
00478     UInt32      resetInterval;          /*  c        */
00479     UInt32      windowSize;             /* 10        */
00480     UInt32      windowsPerReset;        /* 14        */
00481     UInt32      unknown_18;             /* 18        */
00482 };
00483 
00484 static int _unmarshal_lzxc_control_data(unsigned char **pData,
00485                                         unsigned int *pDataLen,
00486                                         struct chmLzxcControlData *dest)
00487 {
00488     /* we want at least 0x18 bytes */
00489     if (*pDataLen < _CHM_LZXC_MIN_LEN)
00490         return 0;
00491 
00492     /* unmarshal fields */
00493     _unmarshal_uint32    (pData, pDataLen, &dest->size);
00494     _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
00495     _unmarshal_uint32    (pData, pDataLen, &dest->version);
00496     _unmarshal_uint32    (pData, pDataLen, &dest->resetInterval);
00497     _unmarshal_uint32    (pData, pDataLen, &dest->windowSize);
00498     _unmarshal_uint32    (pData, pDataLen, &dest->windowsPerReset);
00499 
00500     if (*pDataLen >= _CHM_LZXC_V2_LEN)
00501         _unmarshal_uint32    (pData, pDataLen, &dest->unknown_18);
00502     else
00503         dest->unknown_18 = 0;
00504 
00505     if (dest->version == 2)
00506     {
00507         dest->resetInterval *= 0x8000;
00508         dest->windowSize *= 0x8000;
00509     }
00510     if (dest->windowSize == 0  ||  dest->resetInterval == 0)
00511         return 0;
00512 
00513     /* for now, only support resetInterval a multiple of windowSize/2 */
00514     if (dest->windowSize == 1)
00515         return 0;
00516     if ((dest->resetInterval % (dest->windowSize/2)) != 0)
00517         return 0;
00518 
00519     /* check structure */
00520     if (memcmp(dest->signature, "LZXC", 4) != 0)
00521         return 0;
00522 
00523     return 1;
00524 }
00525 
00526 /* the structure used for chm file handles */
00527 struct chmFile
00528 {
00529     HANDLE              fd;
00530 
00531     CRITICAL_SECTION    mutex;
00532     CRITICAL_SECTION    lzx_mutex;
00533     CRITICAL_SECTION    cache_mutex;
00534 
00535     UInt64              dir_offset;
00536     UInt64              dir_len;    
00537     UInt64              data_offset;
00538     Int32               index_root;
00539     Int32               index_head;
00540     UInt32              block_len;     
00541 
00542     UInt64              span;
00543     struct chmUnitInfo  rt_unit;
00544     struct chmUnitInfo  cn_unit;
00545     struct chmLzxcResetTable reset_table;
00546 
00547     /* LZX control data */
00548     int                 compression_enabled;
00549     UInt32              window_size;
00550     UInt32              reset_interval;
00551     UInt32              reset_blkcount;
00552 
00553     /* decompressor state */
00554     struct LZXstate    *lzx_state;
00555     int                 lzx_last_block;
00556 
00557     /* cache for decompressed blocks */
00558     UChar             **cache_blocks;
00559     Int64              *cache_block_indices;
00560     Int32               cache_num_blocks;
00561 };
00562 
00563 /*
00564  * utility functions local to this module
00565  */
00566 
00567 /* utility function to handle differences between {pread,read}(64)? */
00568 static Int64 _chm_fetch_bytes(struct chmFile *h,
00569                               UChar *buf,
00570                               UInt64 os,
00571                               Int64 len)
00572 {
00573     Int64 readLen=0;
00574     if (h->fd  ==  CHM_NULL_FD)
00575         return readLen;
00576 
00577     CHM_ACQUIRE_LOCK(h->mutex);
00578     /* NOTE: this might be better done with CreateFileMapping, et cetera... */
00579     {
00580         LARGE_INTEGER old_pos, new_pos;
00581         DWORD actualLen=0;
00582 
00583         /* awkward Win32 Seek/Tell */
00584         new_pos.QuadPart = 0;
00585         SetFilePointerEx( h->fd, new_pos, &old_pos, FILE_CURRENT );
00586         new_pos.QuadPart = os;
00587         SetFilePointerEx( h->fd, new_pos, NULL, FILE_BEGIN );
00588 
00589         /* read the data */
00590         if (ReadFile(h->fd,
00591                      buf,
00592                      (DWORD)len,
00593                      &actualLen,
00594                      NULL))
00595             readLen = actualLen;
00596         else
00597             readLen = 0;
00598 
00599         /* restore original position */
00600         SetFilePointerEx( h->fd, old_pos, NULL, FILE_BEGIN );
00601     }
00602     CHM_RELEASE_LOCK(h->mutex);
00603     return readLen;
00604 }
00605 
00606 /*
00607  * set a parameter on the file handle.
00608  * valid parameter types:
00609  *          CHM_PARAM_MAX_BLOCKS_CACHED:
00610  *                 how many decompressed blocks should be cached?  A simple
00611  *                 caching scheme is used, wherein the index of the block is
00612  *                 used as a hash value, and hash collision results in the
00613  *                 invalidation of the previously cached block.
00614  */
00615 static void chm_set_param(struct chmFile *h,
00616                           int paramType,
00617                           int paramVal)
00618 {
00619     switch (paramType)
00620     {
00621         case CHM_PARAM_MAX_BLOCKS_CACHED:
00622             CHM_ACQUIRE_LOCK(h->cache_mutex);
00623             if (paramVal != h->cache_num_blocks)
00624             {
00625                 UChar **newBlocks;
00626                 Int64 *newIndices;
00627                 int     i;
00628 
00629                 /* allocate new cached blocks */
00630                 newBlocks = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UChar *));
00631                 newIndices = HeapAlloc(GetProcessHeap(), 0, paramVal * sizeof (UInt64));
00632                 for (i=0; i<paramVal; i++)
00633                 {
00634                     newBlocks[i] = NULL;
00635                     newIndices[i] = 0;
00636                 }
00637 
00638                 /* re-distribute old cached blocks */
00639                 if (h->cache_blocks)
00640                 {
00641                     for (i=0; i<h->cache_num_blocks; i++)
00642                     {
00643                         int newSlot = (int)(h->cache_block_indices[i] % paramVal);
00644 
00645                         if (h->cache_blocks[i])
00646                         {
00647                             /* in case of collision, destroy newcomer */
00648                             if (newBlocks[newSlot])
00649                             {
00650                                 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]);
00651                                 h->cache_blocks[i] = NULL;
00652                             }
00653                             else
00654                             {
00655                                 newBlocks[newSlot] = h->cache_blocks[i];
00656                                 newIndices[newSlot] =
00657                                             h->cache_block_indices[i];
00658                             }
00659                         }
00660                     }
00661 
00662                     HeapFree(GetProcessHeap(), 0, h->cache_blocks);
00663                     HeapFree(GetProcessHeap(), 0, h->cache_block_indices);
00664                 }
00665 
00666                 /* now, set new values */
00667                 h->cache_blocks = newBlocks;
00668                 h->cache_block_indices = newIndices;
00669                 h->cache_num_blocks = paramVal;
00670             }
00671             CHM_RELEASE_LOCK(h->cache_mutex);
00672             break;
00673 
00674         default:
00675             break;
00676     }
00677 }
00678 
00679 /* open an ITS archive */
00680 struct chmFile *chm_openW(const WCHAR *filename)
00681 {
00682     unsigned char               sbuffer[256];
00683     unsigned int                sremain;
00684     unsigned char              *sbufpos;
00685     struct chmFile             *newHandle=NULL;
00686     struct chmItsfHeader        itsfHeader;
00687     struct chmItspHeader        itspHeader;
00688 #if 0
00689     struct chmUnitInfo          uiSpan;
00690 #endif
00691     struct chmUnitInfo          uiLzxc;
00692     struct chmLzxcControlData   ctlData;
00693 
00694     /* allocate handle */
00695     newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile));
00696     newHandle->fd = CHM_NULL_FD;
00697     newHandle->lzx_state = NULL;
00698     newHandle->cache_blocks = NULL;
00699     newHandle->cache_block_indices = NULL;
00700     newHandle->cache_num_blocks = 0;
00701 
00702     /* open file */
00703     if ((newHandle->fd=CreateFileW(filename,
00704                                    GENERIC_READ,
00705                                    FILE_SHARE_READ,
00706                                    NULL,
00707                                    OPEN_EXISTING,
00708                                    FILE_ATTRIBUTE_NORMAL,
00709                                    NULL)) == CHM_NULL_FD)
00710     {
00711         HeapFree(GetProcessHeap(), 0, newHandle);
00712         return NULL;
00713     }
00714 
00715     /* initialize mutexes, if needed */
00716     InitializeCriticalSection(&newHandle->mutex);
00717     newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex");
00718     InitializeCriticalSection(&newHandle->lzx_mutex);
00719     newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex");
00720     InitializeCriticalSection(&newHandle->cache_mutex);
00721     newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex");
00722 
00723     /* read and verify header */
00724     sremain = _CHM_ITSF_V3_LEN;
00725     sbufpos = sbuffer;
00726     if (_chm_fetch_bytes(newHandle, sbuffer, 0, sremain) != sremain    ||
00727         !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader))
00728     {
00729         chm_close(newHandle);
00730         return NULL;
00731     }
00732 
00733     /* stash important values from header */
00734     newHandle->dir_offset  = itsfHeader.dir_offset;
00735     newHandle->dir_len     = itsfHeader.dir_len;
00736     newHandle->data_offset = itsfHeader.data_offset;
00737 
00738     /* now, read and verify the directory header chunk */
00739     sremain = _CHM_ITSP_V1_LEN;
00740     sbufpos = sbuffer;
00741     if (_chm_fetch_bytes(newHandle, sbuffer,
00742                          itsfHeader.dir_offset, sremain) != sremain    ||
00743         !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader))
00744     {
00745         chm_close(newHandle);
00746         return NULL;
00747     }
00748 
00749     /* grab essential information from ITSP header */
00750     newHandle->dir_offset += itspHeader.header_len;
00751     newHandle->dir_len    -= itspHeader.header_len;
00752     newHandle->index_root  = itspHeader.index_root;
00753     newHandle->index_head  = itspHeader.index_head;
00754     newHandle->block_len   = itspHeader.block_len;
00755 
00756     /* if the index root is -1, this means we don't have any PMGI blocks.
00757      * as a result, we must use the sole PMGL block as the index root
00758      */
00759     if (newHandle->index_root == -1)
00760         newHandle->index_root = newHandle->index_head;
00761 
00762     /* initialize cache */
00763     chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
00764                   CHM_MAX_BLOCKS_CACHED);
00765 
00766     /* By default, compression is enabled. */
00767     newHandle->compression_enabled = 1;
00768 
00769     /* prefetch most commonly needed unit infos */
00770     if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
00771                                                   _CHMU_RESET_TABLE,
00772                                                   &newHandle->rt_unit)    ||
00773         newHandle->rt_unit.space == CHM_COMPRESSED                        ||
00774         CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
00775                                                   _CHMU_CONTENT,
00776                                                   &newHandle->cn_unit)    ||
00777         newHandle->cn_unit.space == CHM_COMPRESSED                        ||
00778         CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
00779                                                   _CHMU_LZXC_CONTROLDATA,
00780                                                   &uiLzxc)                ||
00781         uiLzxc.space == CHM_COMPRESSED)
00782     {
00783         newHandle->compression_enabled = 0;
00784     }
00785 
00786     /* read reset table info */
00787     if (newHandle->compression_enabled)
00788     {
00789         sremain = _CHM_LZXC_RESETTABLE_V1_LEN;
00790         sbufpos = sbuffer;
00791         if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer,
00792                                 0, sremain) != sremain                        ||
00793             !_unmarshal_lzxc_reset_table(&sbufpos, &sremain,
00794                                          &newHandle->reset_table))
00795         {
00796             newHandle->compression_enabled = 0;
00797         }
00798     }
00799 
00800     /* read control data */
00801     if (newHandle->compression_enabled)
00802     {
00803         sremain = (unsigned long)uiLzxc.length;
00804         sbufpos = sbuffer;
00805         if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer,
00806                                 0, sremain) != sremain                       ||
00807             !_unmarshal_lzxc_control_data(&sbufpos, &sremain,
00808                                           &ctlData))
00809         {
00810             newHandle->compression_enabled = 0;
00811         }
00812 
00813         newHandle->window_size = ctlData.windowSize;
00814         newHandle->reset_interval = ctlData.resetInterval;
00815 
00816 /* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */
00817 /*       must be multiplied by this formerly unknown ctrl data field in   */
00818 /*       order to decompress some files.                                  */
00819 #if 0
00820         newHandle->reset_blkcount = newHandle->reset_interval /
00821                     (newHandle->window_size / 2);
00822 #else
00823         newHandle->reset_blkcount = newHandle->reset_interval    /
00824                                     (newHandle->window_size / 2) *
00825                                     ctlData.windowsPerReset;
00826 #endif
00827     }
00828 
00829     return newHandle;
00830 }
00831 
00832 /* Duplicate an ITS archive handle */
00833 struct chmFile *chm_dup(struct chmFile *oldHandle)
00834 {
00835     struct chmFile *newHandle=NULL;
00836 
00837     newHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(struct chmFile));
00838     memcpy(newHandle, oldHandle, sizeof(struct chmFile));
00839 
00840     /* duplicate fd handle */
00841     DuplicateHandle(GetCurrentProcess(), oldHandle->fd,
00842                     GetCurrentProcess(), &(newHandle->fd),
00843                     0, FALSE, DUPLICATE_SAME_ACCESS);
00844     newHandle->lzx_state = NULL;
00845     newHandle->cache_blocks = NULL;
00846     newHandle->cache_block_indices = NULL;
00847     newHandle->cache_num_blocks = 0;
00848 
00849     /* initialize mutexes, if needed */
00850     InitializeCriticalSection(&newHandle->mutex);
00851     newHandle->mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.mutex");
00852     InitializeCriticalSection(&newHandle->lzx_mutex);
00853     newHandle->lzx_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.lzx_mutex");
00854     InitializeCriticalSection(&newHandle->cache_mutex);
00855     newHandle->cache_mutex.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": chmFile.cache_mutex");
00856 
00857     /* initialize cache */
00858     chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
00859                   CHM_MAX_BLOCKS_CACHED);
00860 
00861     return newHandle;
00862 }
00863 
00864 /* close an ITS archive */
00865 void chm_close(struct chmFile *h)
00866 {
00867     if (h != NULL)
00868     {
00869         if (h->fd != CHM_NULL_FD)
00870             CHM_CLOSE_FILE(h->fd);
00871         h->fd = CHM_NULL_FD;
00872 
00873         h->mutex.DebugInfo->Spare[0] = 0;
00874         DeleteCriticalSection(&h->mutex);
00875         h->lzx_mutex.DebugInfo->Spare[0] = 0;
00876         DeleteCriticalSection(&h->lzx_mutex);
00877         h->cache_mutex.DebugInfo->Spare[0] = 0;
00878         DeleteCriticalSection(&h->cache_mutex);
00879 
00880         if (h->lzx_state)
00881             LZXteardown(h->lzx_state);
00882         h->lzx_state = NULL;
00883 
00884         if (h->cache_blocks)
00885         {
00886             int i;
00887             for (i=0; i<h->cache_num_blocks; i++)
00888             {
00889                 HeapFree(GetProcessHeap(), 0, h->cache_blocks[i]);
00890             }
00891             HeapFree(GetProcessHeap(), 0, h->cache_blocks);
00892             h->cache_blocks = NULL;
00893         }
00894 
00895         HeapFree(GetProcessHeap(), 0, h->cache_block_indices);
00896         h->cache_block_indices = NULL;
00897 
00898         HeapFree(GetProcessHeap(), 0, h);
00899     }
00900 }
00901 
00902 /*
00903  * helper methods for chm_resolve_object
00904  */
00905 
00906 /* skip a compressed dword */
00907 static void _chm_skip_cword(UChar **pEntry)
00908 {
00909     while (*(*pEntry)++ >= 0x80)
00910         ;
00911 }
00912 
00913 /* skip the data from a PMGL entry */
00914 static void _chm_skip_PMGL_entry_data(UChar **pEntry)
00915 {
00916     _chm_skip_cword(pEntry);
00917     _chm_skip_cword(pEntry);
00918     _chm_skip_cword(pEntry);
00919 }
00920 
00921 /* parse a compressed dword */
00922 static UInt64 _chm_parse_cword(UChar **pEntry)
00923 {
00924     UInt64 accum = 0;
00925     UChar temp;
00926     while ((temp=*(*pEntry)++) >= 0x80)
00927     {
00928         accum <<= 7;
00929         accum += temp & 0x7f;
00930     }
00931 
00932     return (accum << 7) + temp;
00933 }
00934 
00935 /* parse a utf-8 string into an ASCII char buffer */
00936 static int _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path)
00937 {
00938     /* MJM - Modified to return real Unicode strings */ 
00939     while (count != 0)
00940     {
00941         *path++ = (*(*pEntry)++);
00942         --count;
00943     }
00944 
00945     *path = '\0';
00946     return 1;
00947 }
00948 
00949 /* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */
00950 static int _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui)
00951 {
00952     UInt64 strLen;
00953 
00954     /* parse str len */
00955     strLen = _chm_parse_cword(pEntry);
00956     if (strLen > CHM_MAX_PATHLEN)
00957         return 0;
00958 
00959     /* parse path */
00960     if (! _chm_parse_UTF8(pEntry, strLen, ui->path))
00961         return 0;
00962 
00963     /* parse info */
00964     ui->space  = (int)_chm_parse_cword(pEntry);
00965     ui->start  = _chm_parse_cword(pEntry);
00966     ui->length = _chm_parse_cword(pEntry);
00967     return 1;
00968 }
00969 
00970 /* find an exact entry in PMGL; return NULL if we fail */
00971 static UChar *_chm_find_in_PMGL(UChar *page_buf,
00972                          UInt32 block_len,
00973                          const WCHAR *objPath)
00974 {
00975     /* XXX: modify this to do a binary search using the nice index structure
00976      *      that is provided for us.
00977      */
00978     struct chmPmglHeader header;
00979     UInt32 hremain;
00980     UChar *end;
00981     UChar *cur;
00982     UChar *temp;
00983     UInt64 strLen;
00984     WCHAR buffer[CHM_MAX_PATHLEN+1];
00985 
00986     /* figure out where to start and end */
00987     cur = page_buf;
00988     hremain = _CHM_PMGL_LEN;
00989     if (! _unmarshal_pmgl_header(&cur, &hremain, &header))
00990         return NULL;
00991     end = page_buf + block_len - (header.free_space);
00992 
00993     /* now, scan progressively */
00994     while (cur < end)
00995     {
00996         /* grab the name */
00997         temp = cur;
00998         strLen = _chm_parse_cword(&cur);
00999         if (! _chm_parse_UTF8(&cur, strLen, buffer))
01000             return NULL;
01001 
01002         /* check if it is the right name */
01003         if (! strcmpiW(buffer, objPath))
01004             return temp;
01005 
01006         _chm_skip_PMGL_entry_data(&cur);
01007     }
01008 
01009     return NULL;
01010 }
01011 
01012 /* find which block should be searched next for the entry; -1 if no block */
01013 static Int32 _chm_find_in_PMGI(UChar *page_buf,
01014                         UInt32 block_len,
01015                         const WCHAR *objPath)
01016 {
01017     /* XXX: modify this to do a binary search using the nice index structure
01018      *      that is provided for us
01019      */
01020     struct chmPmgiHeader header;
01021     UInt32 hremain;
01022     int page=-1;
01023     UChar *end;
01024     UChar *cur;
01025     UInt64 strLen;
01026     WCHAR buffer[CHM_MAX_PATHLEN+1];
01027 
01028     /* figure out where to start and end */
01029     cur = page_buf;
01030     hremain = _CHM_PMGI_LEN;
01031     if (! _unmarshal_pmgi_header(&cur, &hremain, &header))
01032         return -1;
01033     end = page_buf + block_len - (header.free_space);
01034 
01035     /* now, scan progressively */
01036     while (cur < end)
01037     {
01038         /* grab the name */
01039         strLen = _chm_parse_cword(&cur);
01040         if (! _chm_parse_UTF8(&cur, strLen, buffer))
01041             return -1;
01042 
01043         /* check if it is the right name */
01044         if (strcmpiW(buffer, objPath) > 0)
01045             return page;
01046 
01047         /* load next value for path */
01048         page = (int)_chm_parse_cword(&cur);
01049     }
01050 
01051     return page;
01052 }
01053 
01054 /* resolve a particular object from the archive */
01055 int chm_resolve_object(struct chmFile *h,
01056                        const WCHAR *objPath,
01057                        struct chmUnitInfo *ui)
01058 {
01059     /*
01060      * XXX: implement caching scheme for dir pages
01061      */
01062 
01063     Int32 curPage;
01064 
01065     /* buffer to hold whatever page we're looking at */
01066     UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len);
01067 
01068     /* starting page */
01069     curPage = h->index_root;
01070 
01071     /* until we have either returned or given up */
01072     while (curPage != -1)
01073     {
01074 
01075         /* try to fetch the index page */
01076         if (_chm_fetch_bytes(h, page_buf,
01077                              h->dir_offset + (UInt64)curPage*h->block_len,
01078                              h->block_len) != h->block_len)
01079     {
01080         HeapFree(GetProcessHeap(), 0, page_buf);
01081             return CHM_RESOLVE_FAILURE;
01082     }
01083 
01084         /* now, if it is a leaf node: */
01085         if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0)
01086         {
01087             /* scan block */
01088             UChar *pEntry = _chm_find_in_PMGL(page_buf,
01089                                               h->block_len,
01090                                               objPath);
01091             if (pEntry == NULL)
01092             {
01093             HeapFree(GetProcessHeap(), 0, page_buf);
01094                 return CHM_RESOLVE_FAILURE;
01095             }
01096 
01097             /* parse entry and return */
01098             _chm_parse_PMGL_entry(&pEntry, ui);
01099         HeapFree(GetProcessHeap(), 0, page_buf);
01100             return CHM_RESOLVE_SUCCESS;
01101         }
01102 
01103         /* else, if it is a branch node: */
01104         else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0)
01105             curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath);
01106 
01107         /* else, we are confused.  give up. */
01108         else
01109         {
01110         HeapFree(GetProcessHeap(), 0, page_buf);
01111             return CHM_RESOLVE_FAILURE;
01112         }
01113     }
01114 
01115     /* didn't find anything.  fail. */
01116     HeapFree(GetProcessHeap(), 0, page_buf);
01117     return CHM_RESOLVE_FAILURE;
01118 }
01119 
01120 /*
01121  * utility methods for dealing with compressed data
01122  */
01123 
01124 /* get the bounds of a compressed block.  return 0 on failure */
01125 static int _chm_get_cmpblock_bounds(struct chmFile *h,
01126                              UInt64 block,
01127                              UInt64 *start,
01128                              Int64 *len)
01129 {
01130     UChar buffer[8], *dummy;
01131     UInt32 remain;
01132 
01133     /* for all but the last block, use the reset table */
01134     if (block < h->reset_table.block_count-1)
01135     {
01136         /* unpack the start address */
01137         dummy = buffer;
01138         remain = 8;
01139         if (_chm_fetch_bytes(h, buffer,
01140                              h->data_offset
01141                                 + h->rt_unit.start
01142                                 + h->reset_table.table_offset
01143                                 + block*8,
01144                              remain) != remain                            ||
01145             !_unmarshal_uint64(&dummy, &remain, start))
01146             return 0;
01147 
01148         /* unpack the end address */
01149         dummy = buffer;
01150         remain = 8;
01151         if (_chm_fetch_bytes(h, buffer,
01152                              h->data_offset
01153                                 + h->rt_unit.start
01154                                 + h->reset_table.table_offset
01155                                 + block*8 + 8,
01156                          remain) != remain                                ||
01157             !_unmarshal_int64(&dummy, &remain, len))
01158             return 0;
01159     }
01160 
01161     /* for the last block, use the span in addition to the reset table */
01162     else
01163     {
01164         /* unpack the start address */
01165         dummy = buffer;
01166         remain = 8;
01167         if (_chm_fetch_bytes(h, buffer,
01168                              h->data_offset
01169                                 + h->rt_unit.start
01170                                 + h->reset_table.table_offset
01171                                 + block*8,
01172                              remain) != remain                            ||
01173             !_unmarshal_uint64(&dummy, &remain, start))
01174             return 0;
01175 
01176         *len = h->reset_table.compressed_len;
01177     }
01178 
01179     /* compute the length and absolute start address */
01180     *len -= *start;
01181     *start += h->data_offset + h->cn_unit.start;
01182 
01183     return 1;
01184 }
01185 
01186 /* decompress the block.  must have lzx_mutex. */
01187 static Int64 _chm_decompress_block(struct chmFile *h,
01188                                    UInt64 block,
01189                                    UChar **ubuffer)
01190 {
01191     UChar *cbuffer = HeapAlloc( GetProcessHeap(), 0,
01192                               ((unsigned int)h->reset_table.block_len + 6144));
01193     UInt64 cmpStart;                                    /* compressed start  */
01194     Int64 cmpLen;                                       /* compressed len    */
01195     int indexSlot;                                      /* cache index slot  */
01196     UChar *lbuffer;                                     /* local buffer ptr  */
01197     UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset interval align */
01198     UInt32 i;                                           /* local loop index  */
01199 
01200     /* let the caching system pull its weight! */
01201     if (block - blockAlign <= h->lzx_last_block  &&
01202         block              >= h->lzx_last_block)
01203         blockAlign = (block - h->lzx_last_block);
01204 
01205     /* check if we need previous blocks */
01206     if (blockAlign != 0)
01207     {
01208         /* fetch all required previous blocks since last reset */
01209         for (i = blockAlign; i > 0; i--)
01210         {
01211             UInt32 curBlockIdx = block - i;
01212 
01213             /* check if we most recently decompressed the previous block */
01214             if (h->lzx_last_block != curBlockIdx)
01215             {
01216                 if ((curBlockIdx % h->reset_blkcount) == 0)
01217                 {
01218 #ifdef CHM_DEBUG
01219                     fprintf(stderr, "***RESET (1)***\n");
01220 #endif
01221                     LZXreset(h->lzx_state);
01222                 }
01223 
01224                 indexSlot = (int)((curBlockIdx) % h->cache_num_blocks);
01225                 h->cache_block_indices[indexSlot] = curBlockIdx;
01226                 if (! h->cache_blocks[indexSlot])
01227                     h->cache_blocks[indexSlot] =
01228                       HeapAlloc(GetProcessHeap(), 0,
01229                                 (unsigned int)(h->reset_table.block_len));
01230                 lbuffer = h->cache_blocks[indexSlot];
01231 
01232                 /* decompress the previous block */
01233 #ifdef CHM_DEBUG
01234                 fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx);
01235 #endif
01236                 if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) ||
01237                     _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen      ||
01238                     LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
01239                                   (int)h->reset_table.block_len) != DECR_OK)
01240                 {
01241 #ifdef CHM_DEBUG
01242                     fprintf(stderr, "   (DECOMPRESS FAILED!)\n");
01243 #endif
01244                     HeapFree(GetProcessHeap(), 0, cbuffer);
01245                     return 0;
01246                 }
01247 
01248                 h->lzx_last_block = (int)curBlockIdx;
01249             }
01250         }
01251     }
01252     else
01253     {
01254         if ((block % h->reset_blkcount) == 0)
01255         {
01256 #ifdef CHM_DEBUG
01257             fprintf(stderr, "***RESET (2)***\n");
01258 #endif
01259             LZXreset(h->lzx_state);
01260         }
01261     }
01262 
01263     /* allocate slot in cache */
01264     indexSlot = (int)(block % h->cache_num_blocks);
01265     h->cache_block_indices[indexSlot] = block;
01266     if (! h->cache_blocks[indexSlot])
01267         h->cache_blocks[indexSlot] =
01268           HeapAlloc(GetProcessHeap(), 0, ((unsigned int)h->reset_table.block_len));
01269     lbuffer = h->cache_blocks[indexSlot];
01270     *ubuffer = lbuffer;
01271 
01272     /* decompress the block we actually want */
01273 #ifdef CHM_DEBUG
01274     fprintf(stderr, "Decompressing block #%4d (REAL )\n", block);
01275 #endif
01276     if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen)          ||
01277         _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen          ||
01278         LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
01279                       (int)h->reset_table.block_len) != DECR_OK)
01280     {
01281 #ifdef CHM_DEBUG
01282         fprintf(stderr, "   (DECOMPRESS FAILED!)\n");
01283 #endif
01284         HeapFree(GetProcessHeap(), 0, cbuffer);
01285         return 0;
01286     }
01287     h->lzx_last_block = (int)block;
01288 
01289     /* XXX: modify LZX routines to return the length of the data they
01290      * decompressed and return that instead, for an extra sanity check.
01291      */
01292     HeapFree(GetProcessHeap(), 0, cbuffer);
01293     return h->reset_table.block_len;
01294 }
01295 
01296 /* grab a region from a compressed block */
01297 static Int64 _chm_decompress_region(struct chmFile *h,
01298                                     UChar *buf,
01299                                     UInt64 start,
01300                                     Int64 len)
01301 {
01302     UInt64 nBlock, nOffset;
01303     UInt64 nLen;
01304     UInt64 gotLen;
01305     UChar *ubuffer = NULL;
01306 
01307         if (len <= 0)
01308                 return 0;
01309 
01310     /* figure out what we need to read */
01311     nBlock = start / h->reset_table.block_len;
01312     nOffset = start % h->reset_table.block_len;
01313     nLen = len;
01314     if (nLen > (h->reset_table.block_len - nOffset))
01315         nLen = h->reset_table.block_len - nOffset;
01316 
01317     /* if block is cached, return data from it. */
01318     CHM_ACQUIRE_LOCK(h->lzx_mutex);
01319     CHM_ACQUIRE_LOCK(h->cache_mutex);
01320     if (h->cache_block_indices[nBlock % h->cache_num_blocks] == nBlock    &&
01321         h->cache_blocks[nBlock % h->cache_num_blocks] != NULL)
01322     {
01323         memcpy(buf,
01324                h->cache_blocks[nBlock % h->cache_num_blocks] + nOffset,
01325                (unsigned int)nLen);
01326         CHM_RELEASE_LOCK(h->cache_mutex);
01327         CHM_RELEASE_LOCK(h->lzx_mutex);
01328         return nLen;
01329     }
01330     CHM_RELEASE_LOCK(h->cache_mutex);
01331 
01332     /* data request not satisfied, so... start up the decompressor machine */
01333     if (! h->lzx_state)
01334     {
01335         int window_size = ffs(h->window_size) - 1;
01336         h->lzx_last_block = -1;
01337         h->lzx_state = LZXinit(window_size);
01338     }
01339 
01340     /* decompress some data */
01341     gotLen = _chm_decompress_block(h, nBlock, &ubuffer);
01342     if (gotLen < nLen)
01343         nLen = gotLen;
01344     memcpy(buf, ubuffer+nOffset, (unsigned int)nLen);
01345     CHM_RELEASE_LOCK(h->lzx_mutex);
01346     return nLen;
01347 }
01348 
01349 /* retrieve (part of) an object */
01350 LONGINT64 chm_retrieve_object(struct chmFile *h,
01351                                struct chmUnitInfo *ui,
01352                                unsigned char *buf,
01353                                LONGUINT64 addr,
01354                                LONGINT64 len)
01355 {
01356     /* must be valid file handle */
01357     if (h == NULL)
01358         return 0;
01359 
01360     /* starting address must be in correct range */
01361     if (addr >= ui->length)
01362         return 0;
01363 
01364     /* clip length */
01365     if (addr + len > ui->length)
01366         len = ui->length - addr;
01367 
01368     /* if the file is uncompressed, it's simple */
01369     if (ui->space == CHM_UNCOMPRESSED)
01370     {
01371         /* read data */
01372         return _chm_fetch_bytes(h,
01373                                 buf,
01374                                 h->data_offset + ui->start + addr,
01375                                 len);
01376     }
01377 
01378     /* else if the file is compressed, it's a little trickier */
01379     else /* ui->space == CHM_COMPRESSED */
01380     {
01381         Int64 swath=0, total=0;
01382 
01383         /* if compression is not enabled for this file... */
01384         if (! h->compression_enabled)
01385             return total;
01386 
01387         do {
01388 
01389             /* swill another mouthful */
01390             swath = _chm_decompress_region(h, buf, ui->start + addr, len);
01391 
01392             /* if we didn't get any... */
01393             if (swath == 0)
01394                 return total;
01395 
01396             /* update stats */
01397             total += swath;
01398             len -= swath;
01399             addr += swath;
01400             buf += swath;
01401 
01402         } while (len != 0);
01403 
01404         return total;
01405     }
01406 }
01407 
01408 int chm_enumerate_dir(struct chmFile *h,
01409                       const WCHAR *prefix,
01410                       int what,
01411                       CHM_ENUMERATOR e,
01412                       void *context)
01413 {
01414     /*
01415      * XXX: do this efficiently (i.e. using the tree index)
01416      */
01417 
01418     Int32 curPage;
01419 
01420     /* buffer to hold whatever page we're looking at */
01421     UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len);
01422     struct chmPmglHeader header;
01423     UChar *end;
01424     UChar *cur;
01425     unsigned int lenRemain;
01426 
01427     /* set to 1 once we've started */
01428     int it_has_begun=0;
01429 
01430     /* the current ui */
01431     struct chmUnitInfo ui;
01432     int flag;
01433     UInt64 ui_path_len;
01434 
01435     /* the length of the prefix */
01436     WCHAR prefixRectified[CHM_MAX_PATHLEN+1];
01437     int prefixLen;
01438     WCHAR lastPath[CHM_MAX_PATHLEN];
01439     int lastPathLen;
01440 
01441     /* starting page */
01442     curPage = h->index_head;
01443 
01444     /* initialize pathname state */
01445     lstrcpynW(prefixRectified, prefix, CHM_MAX_PATHLEN);
01446     prefixLen = strlenW(prefixRectified);
01447     if (prefixLen != 0)
01448     {
01449         if (prefixRectified[prefixLen-1] != '/')
01450         {
01451             prefixRectified[prefixLen] = '/';
01452             prefixRectified[prefixLen+1] = '\0';
01453             ++prefixLen;
01454         }
01455     }
01456     lastPath[0] = '\0';
01457     lastPathLen = -1;
01458 
01459     /* until we have either returned or given up */
01460     while (curPage != -1)
01461     {
01462 
01463         /* try to fetch the index page */
01464         if (_chm_fetch_bytes(h,
01465                              page_buf,
01466                              h->dir_offset + (UInt64)curPage*h->block_len,
01467                              h->block_len) != h->block_len)
01468         {
01469             HeapFree(GetProcessHeap(), 0, page_buf);
01470             return 0;
01471         }
01472 
01473         /* figure out start and end for this page */
01474         cur = page_buf;
01475         lenRemain = _CHM_PMGL_LEN;
01476         if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header))
01477         {
01478             HeapFree(GetProcessHeap(), 0, page_buf);
01479             return 0;
01480         }
01481         end = page_buf + h->block_len - (header.free_space);
01482 
01483         /* loop over this page */
01484         while (cur < end)
01485         {
01486             if (! _chm_parse_PMGL_entry(&cur, &ui))
01487             {
01488                 HeapFree(GetProcessHeap(), 0, page_buf);
01489                 return 0;
01490             }
01491 
01492             /* check if we should start */
01493             if (! it_has_begun)
01494             {
01495                 if (ui.length == 0  &&  strncmpiW(ui.path, prefixRectified, prefixLen) == 0)
01496                     it_has_begun = 1;
01497                 else
01498                     continue;
01499 
01500                 if (ui.path[prefixLen] == '\0')
01501                     continue;
01502             }
01503 
01504             /* check if we should stop */
01505             else
01506             {
01507                 if (strncmpiW(ui.path, prefixRectified, prefixLen) != 0)
01508                 {
01509                     HeapFree(GetProcessHeap(), 0, page_buf);
01510                     return 1;
01511                 }
01512             }
01513 
01514             /* check if we should include this path */
01515             if (lastPathLen != -1)
01516             {
01517                 if (strncmpiW(ui.path, lastPath, lastPathLen) == 0)
01518                     continue;
01519             }
01520             strcpyW(lastPath, ui.path);
01521             lastPathLen = strlenW(lastPath);
01522 
01523             /* get the length of the path */
01524             ui_path_len = strlenW(ui.path)-1;
01525 
01526             /* check for DIRS */
01527             if (ui.path[ui_path_len] == '/'  &&  !(what & CHM_ENUMERATE_DIRS))
01528                 continue;
01529 
01530             /* check for FILES */
01531             if (ui.path[ui_path_len] != '/'  &&  !(what & CHM_ENUMERATE_FILES))
01532                 continue;
01533 
01534             /* check for NORMAL vs. META */
01535             if (ui.path[0] == '/')
01536             {
01537 
01538                 /* check for NORMAL vs. SPECIAL */
01539                 if (ui.path[1] == '#'  ||  ui.path[1] == '$')
01540                     flag = CHM_ENUMERATE_SPECIAL;
01541                 else
01542                     flag = CHM_ENUMERATE_NORMAL;
01543             }
01544             else
01545                 flag = CHM_ENUMERATE_META;
01546             if (! (what & flag))
01547                 continue;
01548 
01549             /* call the enumerator */
01550             {
01551                 int status = (*e)(h, &ui, context);
01552                 switch (status)
01553                 {
01554                     case CHM_ENUMERATOR_FAILURE:
01555                         HeapFree(GetProcessHeap(), 0, page_buf);
01556                         return 0;
01557                     case CHM_ENUMERATOR_CONTINUE:
01558                         break;
01559                     case CHM_ENUMERATOR_SUCCESS:
01560                         HeapFree(GetProcessHeap(), 0, page_buf);
01561                         return 1;
01562                     default:
01563                         break;
01564                 }
01565             }
01566         }
01567 
01568         /* advance to next page */
01569         curPage = header.block_next;
01570     }
01571 
01572     HeapFree(GetProcessHeap(), 0, page_buf);
01573     return 1;
01574 }

Generated on Sat May 26 2012 04:22:43 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.